如果想让 Arduino 和 UEFI 进行交互,可以使用USB串口驱动,比如:【参考1】提供了一个FTDI的UEFI驱动,如果想用在 Arduino 上需要一些修改。此外,可行的方案就是通过USB HID 直接和 Arduino Leonardo进行通讯(Arduino Uno 也是可以的,但是需要修改 16U2的Firmware,比较麻烦和折腾)。本文就介绍一下具体的实现。
首先说一下硬件部分,在一个 Proto Shied上连接了一个串口转USB的小卡,将 TX/RX/GND 三根对应的接在一起就能正常工作了。
软件方面,Arduino代码如下使用了NicoHood 的 HID库。代码就是不断检查 HID是否收到消息,如果有,那么就从Serial1 发送出去。
/* Copyright (c) 2014-2015 NicoHood See the readme for credit to other people. Advanced RawHID example Shows how to send bytes via RawHID. Press a button to send some example values. Every received data is mirrored to the host via Serial. See HID Project documentation for more information. https://github.com/NicoHood/HID/wiki/RawHID-API */ #include "HID-Project.h" // Buffer to hold RawHID data. // If host tries to send more data than this, // it will respond with an error. // If the data is not read until the host sends the next data // it will also respond with an error and the data will be lost. uint8_t rawhidData[64]; void setup() { Serial1.begin(115200); // Set the RawHID OUT report array. // Feature reports are also (parallel) possible, see the other example for this. RawHID.begin(rawhidData, sizeof(rawhidData)); } void loop() { // Check if there is new data from the RawHID device auto bytesAvailable = RawHID.available(); int c; if (bytesAvailable) { // Mirror data via Serial while (bytesAvailable--) { c=(RawHID.read()&0xFF); if (c / 16 ==0) {Serial1.print("0");} Serial1.print(c,HEX); Serial1.print(" "); } } }
UEFI Shell Application的原理就是使用 USBIO枚举系统中的全部USB 设备,找到VID/PID是Arduino Leonardo 的设备,再检测InterfaceClass是否为03 (HID),如果是那么就打开,用UsbSetReportRequest 发送一段随机数过去。完整代码如下:
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/BaseMemoryLib.h> #include <Protocol/UsbIo.h> #include <stdlib.h> extern EFI_BOOT_SERVICES *gBS; EFI_GUID gEfiUsbIoProtocolGuid = { 0x2B2F68D6, 0x0CD2, 0x44CF, { 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 }}; //C:\UDK2015\MdePkg\Library\UefiUsbLib\Hid.c /** Set the report descriptor of the specified USB HID interface. Submit a USB set HID report request for the USB device specified by UsbIo, Interface, ReportId, and ReportType, and set the report descriptor using the buffer specified by ReportLength and Report. If UsbIo is NULL, then ASSERT(). If Report is NULL, then ASSERT(). @param UsbIo A pointer to the USB I/O Protocol instance for the specific USB target. @param Interface The index of the report interface on the USB target. @param ReportId The identifier of the report to retrieve. @param ReportType The type of report to retrieve. @param ReportLength The size, in bytes, of Report. @param Report A pointer to the report descriptor buffer to set. @retval EFI_SUCCESS The request executed successfully. @retval EFI_TIMEOUT A timeout occurred executing the request. @retval EFI_DEVICE_ERROR The request failed due to a device error. **/ EFI_STATUS EFIAPI UsbSetReportRequest ( IN EFI_USB_IO_PROTOCOL *UsbIo, IN UINT8 Interface, IN UINT8 ReportId, IN UINT8 ReportType, IN UINT16 ReportLen, IN UINT8 *Report ) { UINT32 Status; EFI_STATUS Result; EFI_USB_DEVICE_REQUEST Request; // // Fill Device request packet // Request.RequestType = USB_HID_CLASS_SET_REQ_TYPE; Request.Request = EFI_USB_SET_REPORT_REQUEST; Request.Value = (UINT16) ((ReportType << 8) | ReportId); Request.Index = Interface; Request.Length = ReportLen; Result = UsbIo->UsbControlTransfer ( UsbIo, &Request, EfiUsbDataOut, 3000, //PcdGet32 (PcdUsbTransferTimeoutValue), Report, ReportLen, &Status ); return Result; } UINTN GetUSB() { EFI_STATUS Status; UINTN HandleIndex, HandleCount; EFI_HANDLE *DevicePathHandleBuffer = NULL; EFI_USB_IO_PROTOCOL *USBIO; EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; EFI_USB_INTERFACE_DESCRIPTOR IfDesc; UINT8 arr[64]; UINT8 i; //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 Arduino Leonrado if ((0x2341==DeviceDescriptor.IdVendor) && (0x8036==DeviceDescriptor.IdProduct)) { //Show the PID and VID Print(L"Found a Leonrado Device VendorID = %04X, ProductID = %04X\n", DeviceDescriptor.IdVendor, DeviceDescriptor.IdProduct); // // Get Interface Descriptor // Status = USBIO->UsbGetInterfaceDescriptor (USBIO, &IfDesc); if (EFI_ERROR (Status)) { Print(L"ERROR : Usb Get Interface Descriptor fail.\n"); return EFI_SUCCESS; } //Check the Interface Class for HID if (0x03 == IfDesc.InterfaceClass) { Print(L"Found HID device, send the data!\n"); for (i=0;i<64;i++) { arr[i]=(UINT8) rand(); Print(L"%2X ",arr[i]); } Print(L"\n"); Status=UsbSetReportRequest ( USBIO, IfDesc.InterfaceNumber, 0, //Report ID HID_OUTPUT_REPORT, sizeof(arr), arr); if (EFI_ERROR (Status)) { Print(L"Error=[%r]\n",Status); return EFI_SUCCESS; } } } } gBS->FreePool(DevicePathHandleBuffer); return HandleCount; } int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { GetUSB(); return EFI_SUCCESS; }
运行结果,Shell 下显示:
PC端串口接收到的数据如下:
完整的代码和 X64 EFI 文件下载:
参考:
- http://www.lab-z.com/stufdti/ FTDI 串口驱动