本文介绍如何在Shell 下实现控制 USB Keyboard 上面的 LED。
代码流程如下: 首先是找到USB键盘,然后获得它上加载的 USB IO Protocol,通过发送 set report request 的放置通知它当前 LED 应该设置的状态即可。
代码有点长如果你是第一次接触,建议先研究之前的获得 USB VID PID的例子.
#include <Uefi.h> #include <Library/UefiLib.h> #include <Library/ShellCEntryLib.h> #include <Library/MemoryAllocationLib.h> #include <Library/BaseMemoryLib.h> #include <Protocol/UsbIo.h> #include "EfiKey.h" extern EFI_BOOT_SERVICES *gBS; extern EFI_HANDLE gImageHandle; EFI_GUID gEfiUsbIoProtocolGuid = { 0x2B2F68D6, 0x0CD2, 0x44CF, { 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 }}; EFI_GUID gEfiSimpleTextInputExProtocolGuid = {0xdd9e7534, 0x7762, 0x4698, { 0x8c, 0x14, 0xf5, 0x85, 0x17, 0xa6, 0x25, 0xaa }}; //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; } VOID RunSetKeyLED ( IN USB_KB_DEV *UsbKeyboardDevice ) { LED_MAP Led; UINT8 ReportId; UINT8 i; for (i=0;i<32;i++) { // // Set each field in Led map. // Led.NumLock = i % 2; Led.CapsLock = (i>>1) % 2; Led.ScrollLock = (i>>2) % 2; Led.Resrvd = 0; ReportId = 0; // // Call Set_Report Request to lighten the LED. // UsbSetReportRequest ( UsbKeyboardDevice->UsbIo, UsbKeyboardDevice->InterfaceDescriptor.InterfaceNumber, ReportId, HID_OUTPUT_REPORT, 1, (UINT8 *) &Led ); gBS->Stall(100000); } } UINTN GetUSB( ) { EFI_STATUS Status; UINTN HandleIndex, HandleCount; EFI_HANDLE *DevicePathHandleBuffer = NULL; EFI_USB_IO_PROTOCOL *USBIO; EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; USB_KB_DEV *UsbKeyboardDevice; EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; //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; } //Check if the Handle has SimpleEx Protocol Status = gBS->OpenProtocol( DevicePathHandleBuffer[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID**)&SimpleEx, gImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); //If it has the Protocol, it means it's a USB Keyboard if (!EFI_ERROR(Status)) { //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 0; } //Show the PID and VID Print(L"Found a USB Keyboard. VendorID = %04X, ProductID = %04X\n", DeviceDescriptor.IdVendor, DeviceDescriptor.IdProduct); //Get USB_KB_DEV struct by SimpleEx Protocol UsbKeyboardDevice = TEXT_INPUT_EX_USB_KB_DEV_FROM_THIS (SimpleEx); //Change LED status RunSetKeyLED(UsbKeyboardDevice); } } gBS->FreePool(DevicePathHandleBuffer); return HandleCount; } int EFIAPI main ( IN int Argc, IN CHAR16 **Argv ) { GetUSB( ); return EFI_SUCCESS; }
首先是第一个键盘的 LED乱闪,之后是第二个键盘乱闪。
在实际使用中,如果系统同时接入了2个以上的键盘你会发现他们的LED状态是同步的。比如,你在一个键盘上按下 Num Lock,那么另外的一块键盘上的 Num Lock LED同样也会发生变化。因为软件层有专门的机制来进行“同步”。在 EDK2 的代码中\MdeModulePkg\Bus\Usb\UsbKbDxe\KeyBoard.c 可以看到SetKeyLED 这个函数(本文使用的 RunSetKeyLED 也是脱胎于这个函数),有兴趣的朋友可以仔细研究一下。
/** Sets USB keyboard LED state. @param UsbKeyboardDevice The USB_KB_DEV instance. **/ VOID SetKeyLED ( IN USB_KB_DEV *UsbKeyboardDevice )
完整的代码和 X64 Application下载
你好!
请问有方法取得所有 USB的KeyCode吗?
比如用 UsbIo->UsbAsyncInterruptTransfer
用SimpleInput的方法只能取到一部分。
有什么具体的实验吗? 我可以研究一下。 就是根据你的实验,有组合键取不到吗?
我参考 KeyBoard.c中的USBKeyboardRecoveryHandler,想要取得每次按键的 KeyCode,但是按了几个键就freezy了。
用 SimpletextInputEx的话标准键都能取到,但在Setup/KeyboardLayout.c中的mKeyboardLayoutBin 没有定义的键就取不到了,
不知道你有没有什么好的方法,非常感谢。
不好意思,我看了一下,没搞明白的
感谢回复!
我现在就是没有一个研究的方向
1. 卸载掉现有键盘驱动,安装自己的,结果直接freezy
2. 给现有键盘驱动添加自定义键,这个还在研究方法。
3. 直接从USBIO 读取,没有成功,结果同1。