为了巩固对 Console Splitter 这只driver 认知上的巩固,这么篇文章我们会写一个虚拟的简单的输入设备。具体的思路是以下几点:
- 经过上篇文章对Splitter 这只driver 的分析,我们知道,如果想让Splitter 能够管理一个device,那么这个device至少应该安装了gEfiConsoleInDeviceGuid && gEfiSimpleTextInProtocolGuid 所以我们应该让我们这个device被安装上这两个GUID。
- gEfiSimpleTextInProtocolGuid 是每个driver 独立实现自己的protocol 所以这个GUID我们可以自己手动给安装。但是gEfiConsoleInDeviceGuid 是另外一只driver (ConPlatform.c )给自己能support的device handle上安装的GUID ,那么ConPlatromr.c 这只driver support 哪些device呢 。我们上篇也做过简单地介绍就ConPlatform 这只driver 它能够管理的device 是这些device handle 上安装了 gEfiSimpleTextInProtocolGuid, gDevicePathProtocolGuid, 并且该device的path 记录ConIn在这个变量里(热插拔设备除外)。
- 因为在应用程序里,我们用ReadKeyStorke 和 WaitForKey,所以我们要给这个虚拟device 实例化一个SimpleTextInProtocol 。为了让程序尽量简单,我们只是不停的汇报一个字母‘A’。
经过上述分析,我们知道我们这个device 上要手动安装一个SimpleTextInProtocol 和 EfiDevicePathProtocol (这个path 必须在ConIn 里,或者是一个热插拔的device path)然后我们EDK 里的ConPlatform.c 这只driver 就会给这个虚拟的device 安装一个gEfiConsoleInDeviceGuid 最后,这个device就会满足我们Con Splitter.c 这只driver support函数的要求,我们虚拟device 就会动起来。可能说起来会有点啰嗦,看个流程图就一目了然了。
(待画,第一次用CSDN 不太会画流程图)
看code
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiLib.h>
#include <Library/BaseLib.h>
#include <Protocol/SimpleTextIn.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Protocol/DevicePath.h>
#include <Library/DevicePathLib.h>
#include "NonDriver.h"
VHidDev *gVirHidDev;
EFI_KEY Buffer[10];
UINT8 Keyflag;
VOID
EFIAPI
CallBack (
IN EFI_EVENT Event,
IN VOID *Context
)
{
gBS->SignalEvent (Event);
}
STATIC vPath_T gvHidDevPath = {
{
{
MESSAGING_DEVICE_PATH,
MSG_USB_WWID_DP,
{
(UINT8) (sizeof (USB_WWID_DEVICE_PATH)),
(UINT8) (sizeof (USB_WWID_DEVICE_PATH) >> 8)
}
},
0x01,
0xaa,
0xbb
},
{
END_DEVICE_PATH_TYPE,
END_ENTIRE_DEVICE_PATH_SUBTYPE,
{
(UINT8) (END_DEVICE_PATH_LENGTH),
(UINT8) (END_DEVICE_PATH_LENGTH >> 8)
}
}
};
EFI_STATUS
EFIAPI
vReadKeyStroke (
IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
OUT EFI_INPUT_KEY *Key
)
{
Key->UnicodeChar = L'A';
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
TestNonDriver (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
DEBUG((EFI_D_INFO, " NonDriver Test Start\n"));
Status = gBS->AllocatePool (
EfiBootServicesData,
sizeof (VHidDev),
(void **)&gVirHidDev
);
if (EFI_ERROR (Status)) {
DEBUG((EFI_D_INFO, "allocate pool %r\n", Status));
return EFI_OUT_OF_RESOURCES;
}
gVirHidDev->vConIn.Reset = NULL;
gVirHidDev->vConIn.ReadKeyStroke = vReadKeyStroke;
gVirHidDev->Handle = NULL;
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_NOTIFY,
CallBack,
NULL,
&gVirHidDev->vConIn.WaitForKey
);
if (EFI_ERROR(Status)) {
DEBUG((EFI_D_ERROR, "Create Event %r\n", Status));
return Status;
}
Status = gBS->InstallMultipleProtocolInterfaces(
&gVirHidDev->Handle,
&gEfiDevicePathProtocolGuid,
&gvHidDevPath,
&gEfiSimpleTextInProtocolGuid,
&gVirHidDev->vConIn,
NULL
);
return EFI_SUCCESS;
}
其实有了上述分析,我们看code就会很容易,简单地分析一下。首先我们先给我们的虚拟device分配一个地址空间,我们会实例化一个SimpleTextInProtocol在ReadKeyStroke函数里只是简单地向Splitter driver 汇报字母A。然后我们会创建一个Event,上篇我们提到过,如果我们想读用户输入,我们会把程序阻塞住,直到这个Event被Signal起来。所以我们在虚拟device里把这个Event的Callback只是简单地实现一个Signal Event,好让用户程序结束阻塞,继续运行。最后我们会给这个device安装一个device path protocol 好让蒙过ConPlatform.c 这只driver检查那一关,为了简单,我们把这个device模拟成一个USB WWID 那么就不会对这个device path 和 ConIn 记录里的path 进行比对。 主函数大概就是这样,如果想下载code,下面是链接地址
最后我们可以把这个虚拟device 加入EDK source code 里,我这边是用Linux 下的模拟器,所以把.inf 档放进Emulator.dsc 和 Emulator.fdf 里,然后开虚拟我们就会看到屏幕上一直出现字符A。