/** @file Usb legacy keyboard/mouse control driver ;****************************************************************************** ;* Copyright (c) 2014 - 2015, Insyde Software Corporation. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include UINT32 mHidOutputData = 0; UINT32 mHidStatusData = 0; UINT16 mSoftIrqGeneratePort; EFI_GUID mUsbLegacyControlProtocolGuid = USB_LEGACY_CONTROL_PROTOCOL_GUID; // // The device path used for SMI registration. Use dev:0x1f fun:0x00 to it // struct { ACPI_HID_DEVICE_PATH Acpi; PCI_DEVICE_PATH Pci; EFI_DEVICE_PATH_PROTOCOL End; } mDevicePath = { { ACPI_DEVICE_PATH, ACPI_DP, (UINT8)(sizeof(ACPI_HID_DEVICE_PATH)), (UINT8)((sizeof(ACPI_HID_DEVICE_PATH)) >> 8), EISA_PNP_ID(0x0A03), 0 }, { HARDWARE_DEVICE_PATH, HW_PCI_DP, (UINT8)(sizeof(PCI_DEVICE_PATH)), (UINT8)((sizeof(PCI_DEVICE_PATH)) >> 8), 0x00, 0x1f }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, END_DEVICE_PATH_LENGTH, 0 } }; // // The body of UsbLegacyControlProtocol used for protocol installation // typedef struct { USB_LEGACY_CONTROL_PROTOCOL UsbLegacyControlProtocol; BOOLEAN SmiHandlerInstalled; } USB_LEGACY_CONTROL_PRIVATE; USB_LEGACY_CONTROL_PRIVATE mPrivate = { { UsbLegacyControl, NULL, NULL, FALSE }, FALSE }; EFI_SMM_SYSTEM_TABLE2 *mSmst; /** Entry point for EFI drivers. @param ImageHandle EFI_HANDLE @param SystemTable EFI_SYSTEM_TABLE @retval EFI_SUCCESS Success @retval Others Fail **/ EFI_STATUS EFIAPI UsbLegacyControlDriverEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_HANDLE Handle; UINT32 TcoBase; EFI_SMM_USB_DISPATCH2_PROTOCOL *UsbDispatch; EFI_SMM_USB_REGISTER_CONTEXT UsbContext; EFI_SMM_BASE2_PROTOCOL *SmmBase; BOOLEAN InSmm; // // if port 64 is "0xff", it's legacyFree // if ( IoRead8 (0x64) != 0xFF) { return EFI_UNSUPPORTED ; } SmmBase = NULL; Status = gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&SmmBase); InSmm = FALSE; if (!EFI_ERROR (Status)) { SmmBase->InSmm (SmmBase, &InSmm); } // // Register USB legacy SMI handler // if (InSmm){ Status = SmmBase->GetSmstLocation (SmmBase, &mSmst); if (EFI_ERROR (Status)) { return Status; } Status = mSmst->SmmLocateProtocol ( &gEfiSmmUsbDispatch2ProtocolGuid, NULL, (VOID **)&UsbDispatch ); if (EFI_ERROR(Status)) { return EFI_UNSUPPORTED; } UsbContext.Type = UsbLegacy; UsbContext.Device = (EFI_DEVICE_PATH_PROTOCOL*)&mDevicePath; Status = UsbDispatch->Register( UsbDispatch, (EFI_SMM_HANDLER_ENTRY_POINT2)UsbLegacyControlSmiHandler, &UsbContext, &Handle ); if (EFI_ERROR(Status)) { return EFI_UNSUPPORTED; } // // This is SMM instance // mPrivate.UsbLegacyControlProtocol.InSmm = TRUE; } // // Install USB_LEGACY_CONTROL_PROTOCOL // Handle = NULL; Status = gBS->InstallProtocolInterface ( &Handle, &mUsbLegacyControlProtocolGuid, EFI_NATIVE_INTERFACE, &mPrivate.UsbLegacyControlProtocol ); // // Setup the Soft IRQ generate port // TcoBase = PciRead32 (PCI_LIB_ADDRESS (SMBUS_BUS, SMBUS_DEV, SMBUS_FUN, R_SMBUS_TCOBASE)) & B_SMBUS_TCOBASE_MASK; mSoftIrqGeneratePort = (UINT16)(TcoBase + R_TCO_SW_IRQ_GEN); return Status; } /** Main routine for the USB legacy control @param Command USB_LEGACY_CONTROL_SETUP_EMULATION USB_LEGACY_CONTROL_GET_KBC_DATA_POINTER USB_LEGACY_CONTROL_GET_KBC_STATUS_POINTER USB_LEGACY_CONTROL_GENERATE_IRQ @param Param The parameter for the command @retval EFI_SUCCESS Success @retval Others Fail **/ EFI_STATUS EFIAPI UsbLegacyControl ( IN UINTN Command, IN VOID *Param ) { UINT8 Data8; UINT32 Data32; switch (Command) { case USB_LEGACY_CONTROL_SETUP_EMULATION: // // Used for emulation on/off // Data32 = PciRead32 (PCI_LIB_ADDRESS (LPC_BUS, LPC_DEV, LPC_FUN, R_ULKMC)); // // Always turn off USBSMIEN && USBPIRQEN // Data32 &= ~(B_ULKMC_USBSMIEN | B_ULKMC_USBPIRQEN); if (!Param) { // // Turn off the trap SMI // Data32 &= ~(B_ULKMC_TRAP_60R | B_ULKMC_TRAP_60W | B_ULKMC_TRAP_64R | B_ULKMC_TRAP_64W); } else { // // Turn on the trap SMI // Data32 |= (B_ULKMC_TRAP_60R | B_ULKMC_TRAP_60W | B_ULKMC_TRAP_64R | B_ULKMC_TRAP_64W); } PciWrite32 (PCI_LIB_ADDRESS (LPC_BUS, LPC_DEV, LPC_FUN, R_ULKMC), Data32); // // Inspect the SMI handler data to see is it need to update // if (Param && !mPrivate.SmiHandlerInstalled && mPrivate.UsbLegacyControlProtocol.SmiHandler) { mPrivate.SmiHandlerInstalled = TRUE; // // Trap SMI if now in BS to pass the SMI handler related data to SMM instance // if (mPrivate.UsbLegacyControlProtocol.InSmm == FALSE) { // // Trigger SMI through 64 port read // IoRead8 (0x64); } } break; case USB_LEGACY_CONTROL_GET_KBC_DATA_POINTER: // // Get the "pointer" of KBC data (port 0x60) // *(UINT32**)Param = &mHidOutputData; break; case USB_LEGACY_CONTROL_GET_KBC_STATUS_POINTER: // // Get the "pointer" of KBC status (port 0x64) // *(UINT32**)Param = &mHidStatusData; break; case USB_LEGACY_CONTROL_GENERATE_IRQ: // // IRQ 1/12 generating // Data8 = IoRead8 (mSoftIrqGeneratePort); // // Make signal as low // Data8 &= ~(0x01 << ((mHidStatusData & 0x20) ? IRQ12 : IRQ1)); IoWrite8 (mSoftIrqGeneratePort, Data8); // // make signal as high // IoWrite8 (mSoftIrqGeneratePort, 0x03); // // We don't need the output data right now // *(BOOLEAN*)Param = FALSE; break; case USB_LEGACY_CONTROL_GET_CAPABILITY: default: break; } return EFI_SUCCESS; } /** The UsbLegacyControl SMI main handler @param Handle @param Context @retval VOID **/ VOID UsbLegacyControlSmiHandler ( IN EFI_HANDLE Handle, IN CONST EFI_SMM_USB_REGISTER_CONTEXT *Context ) { EFI_STATUS Status; UINT32 Data32; UINT32 TrapEvent; UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINTN Index; USB_LEGACY_CONTROL_PRIVATE *Private; EFI_TPL OldTpl; TrapEvent = 0; Data32 = PciRead32 (PCI_LIB_ADDRESS (LPC_BUS, LPC_DEV, LPC_FUN, R_ULKMC)); if (Data32 & B_ULKMC_TRAP_STATUS) { // // Clear the status // PciWrite32 (PCI_LIB_ADDRESS (LPC_BUS, LPC_DEV, LPC_FUN, R_ULKMC), Data32); TrapEvent = (Data32 & B_ULKMC_TRAP_STATUS) >> N_ULKMC_TRAP_STATUS; } // // Call sub handler // if (mPrivate.UsbLegacyControlProtocol.SmiHandler) { mPrivate.UsbLegacyControlProtocol.SmiHandler (TrapEvent, mPrivate.UsbLegacyControlProtocol.SmiContext); } // // Check is the sub handler "SmiHandler" been installed. Must launched in POST // if (!mPrivate.SmiHandlerInstalled) { OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); gBS->RestoreTPL (OldTpl); if (OldTpl == TPL_HIGH_LEVEL) { gBS->RestoreTPL (TPL_CALLBACK); } mPrivate.SmiHandlerInstalled = TRUE; Status = gBS->LocateHandleBuffer ( ByProtocol, &mUsbLegacyControlProtocolGuid, NULL, &HandleCount, &HandleBuffer ); if (!EFI_ERROR (Status)) { for (Index = 0; Index < HandleCount; Index++) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &mUsbLegacyControlProtocolGuid, (VOID**)&Private ); if (!EFI_ERROR (Status) && Private->UsbLegacyControlProtocol.InSmm == FALSE) { // // Get the SMI handler related data from BS driver // mPrivate.UsbLegacyControlProtocol.SmiHandler = Private->UsbLegacyControlProtocol.SmiHandler; mPrivate.UsbLegacyControlProtocol.SmiContext = Private->UsbLegacyControlProtocol.SmiContext; break; } } } gBS->FreePool (HandleBuffer); if (OldTpl == TPL_HIGH_LEVEL) { gBS->RaiseTPL (TPL_HIGH_LEVEL); } } }