前面的文章介绍了如何使用 CH55X 制作一个 USB 提醒器【参考1】,这次介绍如何在 UEFI Shell 下编写 Application 来控制使用它。
从思路上来说,可以使用加载驱动,然后调用驱动引入的 protocol 来进行控制。比如,FT232 有一个驱动,可以在 UEFI Shell 下直接调用【参考2】.但是,这次的USB 提醒器并没有这样的Driver(估计需要等待 WCH 来进行开发吧)。于是,我们只能尝试直接对其发送数据。
首先使用 USBView 查看一下:
这里需要特别关注的是 Endpoint, 可以看到有下面三个 Endpoint, 第一个是 interrupt, 用于传送控制信息;后面两个分别是 Bulk 的输入和输出用于传输真正的串口数据。
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x81 -> Direction: IN - EndpointID: 1
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0008 = 0x08 bytes
bInterval: 0x40
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x02 -> Direction: OUT - EndpointID: 2
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x82 -> Direction: IN - EndpointID: 2
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
另外因为 USBNotifier 实际上是一个假串口设备(意思是并非对外转接为串口数据),所以即使初始化时如果没有设置波特率等等这样的参数,仍然能够正确收到数据。所以,我们只需要将数据丢到Bulk 的 Endpoint 就可以正常收到并处理。关键步骤如下:
- 枚举当前系统中有 USBIo 的全部 handle
- 使用 UsbGetDeviceDescriptor() 取得每个设备的 PID 和 VID
- 使用 UsbGetInterfaceDescriptor() 找到 USBNotifier 的 EndPoint
- 使用 UsbBulkTransfer() 针对找到的 Endpoint 发送数据
需要特别注意的是:在 UEFI Shell 下, USBNotifier 会被认为两个设备,Interrupt 的Endpoint 为一个设备,另外2个 Bulk 的 Endpoint 被识别为一个设备。
完整的代码:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/UsbIo.h>
extern EFI_BOOT_SERVICES *gBS;
#define USB_ENDPOINT_ADDR(EpAddr) ((EpAddr) & 0x7F)
EFI_GUID gEfiUsbIoProtocolGuid =
{ 0x2B2F68D6, 0x0CD2, 0x44CF, { 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 }};
int
EFIAPI
main (
IN int Argc,
IN CHAR16 **Argv
)
{
EFI_STATUS Status;
UINTN HandleIndex, HandleCount;
//UINT8 i,j;
EFI_HANDLE *DevicePathHandleBuffer = NULL;
EFI_USB_IO_PROTOCOL *USBIO;
EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor;
EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
UINT8 arr[64];
UINTN LengthInBytes;
UINT32 TransferStatus;
//Get all the Handles that have UsbIO Protocol
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiUsbIoProtocolGuid,
NULL,
&HandleCount,
&DevicePathHandleBuffer);
if (EFI_ERROR(Status))
{
Print(L"ERROR : Get USBIO count fail.\n");
return 0;
}
for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++)
{
Status = gBS->HandleProtocol(
DevicePathHandleBuffer[HandleIndex],
&gEfiUsbIoProtocolGuid,
(VOID**)&USBIO);
if (EFI_ERROR(Status))
{
Print(L"ERROR : Open USBIO fail.\n");
gBS->FreePool(DevicePathHandleBuffer);
return 0;
}
//Get USB Device Descriptor
Status = USBIO->UsbGetDeviceDescriptor(USBIO, &DeviceDescriptor);
if (EFI_ERROR(Status))
{
Print(L"ERROR : Usb Get Device Descriptor fail.\n");
gBS->FreePool(DevicePathHandleBuffer);
return EFI_SUCCESS;
}
//Find the device which VID and PID is USB notifier
if ((0x1209==DeviceDescriptor.IdVendor) && (0xC550==DeviceDescriptor.IdProduct))
{
//Show the PID and VID
Print(L"Found a USB Notifier = %04X, ProductID = %04X\n",
DeviceDescriptor.IdVendor,
DeviceDescriptor.IdProduct);
//
// Get Interface Descriptor
//
Status = USBIO->UsbGetInterfaceDescriptor (USBIO, &IfDesc);
if (EFI_ERROR (Status))
{
continue;
}
if (IfDesc.NumEndpoints==2)
{
Print(L"Found OUTPUT Endpoint, send the data!\n");
arr[0]=0x5B; arr[1]=0x63; arr[2]=0xFF;
arr[3]=0x00; arr[4]=0x00; arr[5]=0x5D;
LengthInBytes=6;
Status = USBIO->UsbBulkTransfer (
USBIO,
2,
&arr[0],
&LengthInBytes,
3000,
&TransferStatus );
Print(L"Red color\n");
gBS->Stall(3000000UL);
arr[2]=00; arr[3]=0xFF; arr[4]=0x00;
Status = USBIO->UsbBulkTransfer (
USBIO,
2,
&arr[0],
&LengthInBytes,
3000,
&TransferStatus );
Print(L"Green color\n");
gBS->Stall(3000000UL);
arr[2]=0x00; arr[3]=0x00; arr[4]=0xFF;
Status = USBIO->UsbBulkTransfer (
USBIO,
2,
&arr[0],
&LengthInBytes,
3000,
&TransferStatus );
Print(L"Blue color\n");
gBS->Stall(3000000UL);
arr[2]=0x00; arr[3]=0x00; arr[4]=0x00;
Status = USBIO->UsbBulkTransfer (
USBIO,
2,
&arr[0],
&LengthInBytes,
3000,
&TransferStatus );
}
}
}
gBS->FreePool(DevicePathHandleBuffer);
return EFI_SUCCESS;
}
工作的视频可以在 B 站看到。
https://www.bilibili.com/video/BV1dQ4y1X7XG/
完整工程下载:
参考:
- https://www.lab-z.com/usbnt/ 做一个 USB 提醒器
- https://www.lab-z.com/stufdti/ Step to UEFI (93)FTDI 串口驱动
你好:我想问一下,Build run 进入Nt32模拟器之后,UGA Window一直卡屏是怎么回事,键盘不可以用,直接卡在初始化的界面。
你说的这个问题我还真没有遇到过,建议你先看看输出的 log 看看是不是在等什么。
更直接的方法是:换一台机器,或者在本机开一个虚拟机重新编译试试看。
(Python 2.7.3 on win32) Traceback (most recent call last):
File "build\build.py", line 295, in LaunchCommand
File "C:\Python27\lib\subprocess.py", line 945, in wait
# Child
KeyboardInterrupt
这是终端我强制结束后的输出。之前机子都是正常运行的,我刚换新电脑装的环境才出现的这个问题,看了下那个py文件,确实是一直再等待某个进程结束然后导致进入界面就直接卡死。
你机器上有杀毒软件吗?有时候杀毒软件会导致进程异常。
没有装杀毒软件,怎么尝试还是解决不了NT32模拟器进去卡死问题,换了新电脑就出现这个问题。今天用Qemu模拟器倒是可以正常进入没有问题,就很奇怪。感谢您的回复,关注你文章几年了,真的给我的学习带来了很大的帮助,谢谢了!