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界面
继续阅读“UEFI开发探索73- YIE002USB开发板(02 Windows编程)”

1,788 total views, 1 views today

UEFI开发探索72- YIE002USB开发板(01 开篇)

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

最近把USB的各个方面都研究了一遍,也在UEFI下实现了USB设备的访问。趁着这热乎劲,我计划把USB HID通信的知识,集合到一个新的开发板中进行介绍,也即我目前正在考虑设计的YIE002。

虽然YIE002的开发板还在规划中,我使用其他的开发板,已经打通了USB的通道了。只等着YIE002开发打样回来,把嵌固件代码移植过去就行了。

因为涉及到三个平台的开发(Windows、UEFI以及嵌入式平台),开发中需要记忆的知识点有点多。虽然现在对这些知识还比较清楚,时间长了后,有些细节容易忘记。因此,本篇开始进行YIE002的各类软件实验。

后续的篇章中,会交叉进行YIE001和YIE002的实验。两者的区别在于,YIE001主要是基于PCIE总线的,而YIE002则使用的是USB总线。

1 YIE002开发板计划

首先简单介绍下YIE002的开发板计划。 如图1所示,是我规划的YIE002开发板。

继续阅读“UEFI开发探索72- YIE002USB开发板(01 开篇)”

1,976 total views, no views today

UEFI开发探索71- YIE001PCIe开发板(07 OptionROM框架)


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

正在编辑新书《UEFI编程实践》(暂定名),现在介绍的内容,都整理到了第8章中了。

写书还是比较费神的,博客中可以随意行文,书稿则完全不能这么做。为了做到准确,有时为了确定一个数据,需要花费整个上午查找资料。期待今年上半年能够出版吧。

关于Option ROM,在第34、35和36篇中,已经描述过其历史和原理了。现在有了开发板,就可以在上面直接进行开发了。

1 开发Option ROM

代码是基于BlankDrv中的架构进行开发的,主要修改Supported()函数和Start()函数。Supported()函数中,判断是否为目标PCIE设备(也即是否为YIE001开发板);Start()函数中,添加界面代码以及访问硬件设备的代码。

示例1中给出了修改后的Supported()代码。

【示例1】Supported()函数

EFI_STATUS EFIAPI BlankDrvDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   Controller,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath   OPTIONAL)
{
  EFI_STATUS Status;
  UINT16 MyVendorID, MyDeviceID;
EFI_PCI_IO_PROTOCOL  *PciIo;
  Status = gBS->OpenProtocol (Controller, &gEfiPciIoProtocolGuid,
                  (VOID **) &PciIo,This->DriverBindingHandle,
                  Controller,EFI_OPEN_PROTOCOL_BY_DRIVER);
  if (EFI_ERROR (Status)) return Status;
  Status = PciIo->Pci.Read(PciIo,EfiPciIoWidthUint16,0,1,&MyVendorID);
  if (EFI_ERROR (Status)) goto Done; //获取厂商ID
  Status = PciIo->Pci.Read(PciIo,EfiPciIoWidthUint16,2,1,&MyDeviceID);
  if (EFI_ERROR (Status)) goto Done; //获取设备ID
  Status = EFI_SUCCESS;
  //通过厂商ID和设备ID,判断是否为目标设备
  if (MyVendorID != CH366_VENDOR_ID || MyDeviceID != CH366_DEVICE_ID)
    Status = EFI_UNSUPPORTED;
  Done:  //关闭所用的Protocol
  gBS->CloseProtocol(Controller,&gEfiPciIoProtocolGuid,
         This->DriverBindingHandle,Controller);
  return Status;
}

在Supported()函数中,首先使用OpenProtocol()打开PCI I/O Protocol,如果打开失败,则返回EFI_UNSUPPORTED。然后使用得到的PCI I/O Protocol实例,从PCI设备的配置空间,获得厂商ID和设备ID。如果所得到的厂商ID及设备ID,与目标设备的一致,则返回EFI_SUCCESS,否则返回EFI_UNSUPPORTED。

继续阅读“UEFI开发探索71- YIE001PCIe开发板(07 OptionROM框架)”

1,944 total views, 1 views today

UEFI开发探索70- YIE001PCIe开发板(06 UEFI驱动)

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

前两篇中,我们了解了UEFI驱动模型的基本架构。 在此基础上,本篇将以一个有趣的UEFI驱动GopRotate,配合自己编写的测试用UEFI应用TestGopRotate,演示UEFI驱动的运行和测试过程。

1 UEFI驱动GopRotate

此示例由apop2提供,源码仓库为https://github.com/apop2/GopRotate。工程GopRotate包含5个源文件:ComponentName.c、GopRotate.c、GopRotate.h、GopRotateBlt.c和GopRotate.inf。这几个文件的作用分别如下:

1) ComponentName.c。实现了EFI_COMPONENT_NAME_PROTOCOL的接口函数,提供UEFI驱动的名称;
2)GopRotate.c。实现EFI_DRIVER_BINDING_PROTOCOL及其接口函数,安装EFI_DRIVER_BINDING_PROTOCOL和EFI_COMPONENT_NAME_PROTOCOL,以及实现UEFI Shell界面旋转功能的Protocol;
3) GopRotate.h。定义了驱动所需数据结构以及Protocol接口函数原型;
4) GopRotateBlt.c。实现UEFI Shell界面旋转的函数,以及提供此功能的Protocol接口函数的实现;
5) GopRotate.inf,编译UEFI驱动的INF文件。

为实现UEFI Shell界面的旋转,示例工程GopRotate主要做了以下工作:

  • 找到安装了EFI_GRAPHICS_OUTPUT_PROTOCOL的控制器,并且不能是虚拟设备;
  • 对EFI_GRAPHICS_OUTPUT_PROTOCOL的Blt()接口函数进行替换,更换为GopRotate中实现旋转显示的函数BltRotate();
  • Shell界面旋转多少度,由内部私有结构体的成员变量Rotation决定。实现控制此变量的GRAPHICS_OUTPUT_PROTOCOL_ROTATE_PROTOCOL,此Protocol提供了两个接口函数,用来获取Rotation值和设置Rotation的值。

示例工程GopRotate的私有结构体如示例1所示。

继续阅读“UEFI开发探索70- YIE001PCIe开发板(06 UEFI驱动)”

1,988 total views, 1 views today

UEFI开发探索69- YIE001PCIe开发板(05 UEFI驱动)

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

上一篇中,驱动BlankDrv完成了核心的EFI_DRIVER_BINDING_PROTOCOL,以及获取驱动名Protocol的处理后,整个UEFI驱动框架还有两项工作等待完成:

  • 安装UEFI驱动所提供的Protocol;
  • 驱动加载后是常驻内存的,应该提供UEFI驱动的卸载函数,以支持将驱动系统中移除的功能。

1 完成驱动框架

安装Protocol可以使用InstallMultipleProtocolInterfaces()函数,由于BlankDrv中有三种Protocol需要安装,所以使用了库函数EfiLibInstallDriverBindingComponentName2()来安装所有的Protocol。如示例1所示。

【示例1】安装BlankDrv所提供的Protocol

EFI_STATUS EFIAPI UefiMain(
  IN EFI_HANDLE           ImageHandle,
  IN EFI_SYSTEM_TABLE     *SystemTable
  )
{
  EFI_STATUS          Status;
  …..
//安装需要提供的三种Protocol
  Status = EfiLibInstallDriverBindingComponentName2 (
             ImageHandle,
             SystemTable,
             &gBlankDrvDriverBinding, 
             ImageHandle,
             &gBlankDrvComponentName,
             &gBlankDrvComponentName2
             );
  return Status;
}

驱动的卸载函数,在使用UEFI Shell命令unload的时候会被调用。此函数的入口在UEFI工程的INF文件中添加,由[Defines] Section的UNLOAD_IMAGE字段给出。在示例工程BlankDrv的BlankDrv.inf中,按照如下方式进行了定义:

[Defines]
  ……
ENTRY_POINT                    = UefiMain         //驱动的入口函数
UNLOAD_IMAGE                   = DefaultUnload   //驱动的卸载函数

继续阅读“UEFI开发探索69- YIE001PCIe开发板(05 UEFI驱动)”

2,722 total views, no views today

UEFI开发探索68- YIE001PCIe开发板(04 UEFI驱动)

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

插个新告知:YIE001的开发板,思路已经比较清晰了-使用PCIE芯片,编写Option ROM,在此基础上控制硬件,做一些有趣的实验。由于主要是用业余时间,估计在这两个月内就能把要做的实验完成。

因此,有了新的想法-做一个USB的开发板YIE002,实现从UEFI访问、Windows/Linux访问的全通道USB HID设备。当然,可以用它来收集温度、产生随机数等。准备做成U盘大小,方便直接插在电脑上进行实验。我正在收集网友的想法,有啥想实现的,留言就行,资源允许,我就在板子上实现了。

回到UEFI驱动的介绍。

UEFI开发探索66和67,介绍了服务型驱动的构建和实现。下面几篇,用来介绍符合UEFI驱动模型的驱动程序。本篇主要介绍其框架结构和程序接口,内容有点枯燥,最好对照着源码进行阅读。

一个完整的符合UEFI驱动模型的驱动程序,大致可以分为两个部分:EFI Driver Binding Protocol和驱动本身提供的服务。前者用来管理驱动,后者才是用户需要使用的部分,一般包括一个或多个Protocol。

比如UEFI的USB主控制器驱动,它提供了两个Protocol供用户使用,包括EFI_USB_HC_PROOCOL和EFI_USB2_HC_PROTOCOL,用来访问USB设备。另外,为了方便用户使用,驱动程序一般还会包含EFI Component Name Protocol,用来显示驱动信息。

在网页https://sourceforge.net/projects/edk2/files/EDK%20II%20Releases/Demo%20apps/上,可以下载Intel早期提供的驱动框架示例BlankDrv,用来学习UEFI驱动模型。

1 EFI Driver Binding Protocol

示例工程BlankDrv包含四个文件:

  1. BlankDrv.c。实现EFI_DRIVER_BINDING_PROTOCOL及其接口函数,并安装EFI_DRIVER_BINDING_PROTOCOL和EFI_COMPONENT_NAME_PROTOCOL。
  2. BlankDrv.h。定义了驱动所需数据结构以及Protocol接口函数原型;
  3.  ComponentName.c。实现了EFI_COMPONENT_NAME_PROTOCOL的接口函数;
  4. BlankDrv.inf,编译UEFI驱动的INF文件。
继续阅读“UEFI开发探索68- YIE001PCIe开发板(04 UEFI驱动)”

1,809 total views, no views today

UEFI开发探索67- YIE001PCIe开发板(03 UEFI驱动)

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

上一篇做好了服务型UEFI驱动的示例工程,本篇准备编写测试用的UEFI应用,演示如何使用驱动提供的Protocol。另外,对于如何加载驱动进行测试,也会详细讲述。

1 编写测试驱动的UEFI应用

完成了服务型驱动和相应示例Protocol的构建后,我们准备编写访问示例Protocol的UEFI应用。本节准备的示例工程TestServiceDrv,在文末提供了下载地址。

编写测试示例Protocol的代码,步骤如下:

  • 将示例工程ServiceDrv的头文件MyProtocol.h拷贝到示例工程TestServiceDrv的文件夹下。同时修改TestServiceDrv.inf文件,在[Sources] Section下添加此头文件名;
  • 在TestServiceDrv的主程序所在源文件TestServiceDrv.c中添加GUID的声明和定义;
  • 在主程序中添加测试用的代码。

在我们准备的服务型驱动例子中,准备了三个接口函数。其中,接口函数My_SampleIn()和

MySample_DoSth()对私有数据进行了不同赋值,接口函数My_SampleOut()则将私有数据打印了出来。我们可以据此编写测试用的代码,如示例1所示。

【示例1】测试示例Protocol

EFI_STATUS EFIAPI UefiMain (IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE  *SystemTable)
{
  EFI_STATUS      Status;
  EFI_MYSAMPLE_PROTOCOL *myprotocol;
  Status=gBS->LocateProtocol(&gEfiMYSampleProtocolGUID,//示例Protocol GUID
NULL, (VOID **)&myprotocol);
  if(EFI_ERROR(Status))
  {
    Print(L”LocatProtocol error: %r\n”, Status);
    return Status;
  }
  //测试示例Protocol的接口函数
  myprotocol->MySample_In(myprotocol,L”Hello,My protocol!”);
  myprotocol->MySample_Out(myprotocol);
  myprotocol->MySample_DoSth(myprotocol,L”Enjoy UEFI!”);
  myprotocol->MySample_Out(myprotocol);
  return EFI_SUCCESS;
}

示例应用TestServiceDrv是配合之前构建的服务型驱动ServiceDrv工作的,如果ServiceDrv没有加载,则提示找不到Protocol;如果ServiceDrv加载后,则会如示例1所示,依次调用示例Protocol的接口函数。

继续阅读“UEFI开发探索67- YIE001PCIe开发板(03 UEFI驱动)”

1,649 total views, no views today

UEFI开发探索66- YIE001PCIe开发板(02 UEFI驱动)

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

在之前的篇章中,或多或少地介绍了一些UEFI驱动的内容。我们要在YIE001上实现的PCIE Option ROM,实际上就是一种UEFI驱动。这点与Legacy BIOS的Option ROM完全不同,在系列博客的第34篇中,可以知道Legacy Option ROM完全是并行于BIOS的。

当然,这样描述Legacy Option ROM也不准确,Legacy BIOS与Legacy Option ROM只是遵循一个调用标准,双方并不干涉。而UEFI Option ROM则是完全遵循UEFI驱动模型的,甚至可以用它安装Protocol,供其他程序使用。

在实际进行编程前,必须理清UEFI驱动的结构和编写方法。预计将用3篇左右的篇幅,把这些内容介绍清楚,以方便对YIE001代码的调试和测试。

1 UEFI驱动分类

从类型上来看,UEFI驱动可以分为启动服务驱动(Boot Service Drivers)和运行时驱动(Runtime Drivers)。两者的区别在于,在UEFI BIOS的RT阶段OS Loader获得平台控制权后,运行时驱动仍然有效。

从功能上划分,UEFI驱动可以分为以下类别。

1) 符合UEFI驱动模型(UEFI Driver Model)的驱动。这类驱动包括总线驱动(Bus Drivers)、设备驱动(Device Drivers)和混合驱动(Hybrid Drivers),一般用来驱动对应的硬件设备;

2) 服务型驱动(Service Drivers)。这类驱动不管理任何设备,一般用来产生Protocol;

3) 初始化驱动(Initializing Drivers)。它不会产生任何句柄,也不增加任何Protocol到系统数据库,主要用来进行一些初始化操作,执行完就会从系统内存中卸载;

4) 根桥型驱动(Root Bridge Drivers)。它用来初始化平台上的根桥控制器,并产生一个设备地址Protocol,以及访问总线设备的Protocol,一般由总线驱动用来访问设备。在7.1节中,我们使用的支持访问PCI/PCIe设备的EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL,就是一个典型的例子。

符合UEFI驱动模型的驱动和服务型驱动,在实际项目中运用较多,其他两种则使用较少。本篇主要介绍下服务型驱动。

2 服务型驱动的功能

在前面的探索中,大量使用了Protocol构建各种应用。也在某些篇章了解了Protocol的基本运作方式,特别是42、43中,构建了完整的服务型驱动和测试程序。

本篇介绍的服务型驱动,和之前的并没有太大区别。主要是为了将来阅读方便,以YIE001的名义,将服务型驱动和UEFI驱动的其他内容整理在一起了。闲言少叙,下面进入正篇。

UEFI中,Protocol可算是最重要的概念了。对它的理解和使用,贯穿了整个UEFI探索系列博客。Protocol由各类驱动提供,在这其中,服务型驱动不需遵循UEFI驱动模型,也不需要管理任何硬件设备。其主要的目的就是用来产生一个或多个Protocol,安装到相应的服务句柄上。

Protocol由一个128位的全局唯一ID(GUID)和Protocol接口的结构体组成,结构体中包含了Protocol的接口函数,可以用来访问对应的设备。在UEFI的系统中,其句柄数据库维护了设备句柄、Protocol接口和镜像句柄、控制器句柄之间的关系。对于Protocol接口的增加、移除或者替代,都可以在句柄数据库中跟踪到,如图1所示。

图1 句柄数据库
继续阅读“UEFI开发探索66- YIE001PCIe开发板(02 UEFI驱动)”

1,729 total views, no views today

UEFI开发探索65- YIE001PCIe开发板(01开篇)

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

有段时间没有更新博客了,最近大部分时间都用在公司项目上、以及整理书稿了。在整理书稿的过程中,到了讲述PCIe协议这章的时候,总觉得只是介绍协议和UEFI代码,未免有点太枯燥了。

因此,我把之前写好的章节全部推翻,介绍PCI协议的内容,改为做一个PCIE的开发板,并动手实现板卡的UEFI Option ROM。

也就是说,我按照想象开发了带Flash ROM的PCIE开发板,把UEFI Oprom代码灌入,设备插在计算机上,就可以显示编写好的界面,并能控制开发板上器件,实现一些有趣的功能。

开发板我取名为YIE001,来源于我们博客网站的缩写。目前的计划如下:

  1. 实现基本的Oprom代码;
  2. 实现界面和键盘控制功能;
  3. 实现跑马灯程序;
  4. 实现开发板的按键控制功能;
  5. 实现开发板上的屏幕显示功能;
  6. 移植基础的GUI到开发板上;
  7. 读取插在主机上U盘的内容;
  8. 做个贪吃蛇的游戏。
  9. More……

其中第5个,是指开发板上的I2C接口的OLED屏幕,而不是主机的屏幕。目前不确定这个功能是否能实现,我所用的PCIE芯片Ch366,它的I2C时序很奇怪,我还没有调试,不知道结果如何。当然,时间原因,以上的计划很有可能改变,我尽量按照这些目标开展计划。

另一个计划是关于UEFI与USB的,我准备稍微拓展一下,把如何用单片机实现HID通讯,Windows下如何访问HID设备、UEFI下如何访问HID设备,作为一个大专题,全面讲一下。时间足够的话,Linux系统下访问HID设备的方法也试着实现一下。这就把USB HID从上到下,我觉得应该掌握的知识,全部都探索了一遍。

本篇博客,主要介绍下刚打样回来的YIE001的硬件结构。

开发板YIE001主要就是为开发UEFI Option ROM而制作的,其目的为了演示Option ROM代码如何调用Protocol、如何控制硬件。它是一块PCIe的板卡,提供了拨动开关控制、LED灯的控制以及I2C的接口,其产品结构示如图1所示。

图1 YIE001开发板结构
继续阅读“UEFI开发探索65- YIE001PCIe开发板(01开篇)”

2,164 total views, 1 views today