Step to UEFI (232)UEFI Shell 下控制 USBNotifier

前面的文章介绍了如何使用 CH55X 制作一个 USB 提醒器【参考1】,这次介绍如何在 UEFI Shell 下编写 Application 来控制使用它。

从思路上来说,可以使用加载驱动,然后调用驱动引入的 protocol 来进行控制。比如,FT232 有一个驱动,可以在 UEFI Shell 下直接调用【参考2】.但是,这次的USB 提醒器并没有这样的Driver(估计需要等待 WCH 来进行开发吧)。于是,我们只能尝试直接对其发送数据。

首先使用 USBView 查看一下:

USBViewer 查看结果

这里需要特别关注的是 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 就可以正常收到并处理。关键步骤如下:

  1. 枚举当前系统中有 USBIo 的全部 handle
  2. 使用 UsbGetDeviceDescriptor() 取得每个设备的 PID 和 VID
  3. 使用 UsbGetInterfaceDescriptor() 找到 USBNotifier 的 EndPoint
  4. 使用 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/

完整工程下载:

参考:

  1. https://www.lab-z.com/usbnt/  做一个 USB 提醒器
  2. https://www.lab-z.com/stufdti/ Step to UEFI (93)FTDI 串口驱动

《Step to UEFI (232)UEFI Shell 下控制 USBNotifier》有5个想法

  1. 你好:我想问一下,Build run 进入Nt32模拟器之后,UGA Window一直卡屏是怎么回事,键盘不可以用,直接卡在初始化的界面。

    1. 你说的这个问题我还真没有遇到过,建议你先看看输出的 log 看看是不是在等什么。

      更直接的方法是:换一台机器,或者在本机开一个虚拟机重新编译试试看。

      1. (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文件,确实是一直再等待某个进程结束然后导致进入界面就直接卡死。

          1. 没有装杀毒软件,怎么尝试还是解决不了NT32模拟器进去卡死问题,换了新电脑就出现这个问题。今天用Qemu模拟器倒是可以正常进入没有问题,就很奇怪。感谢您的回复,关注你文章几年了,真的给我的学习带来了很大的帮助,谢谢了!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注