| /** @file | |
| Emu Bus driver | |
| Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR> | |
| Portions copyright (c) 2011, Apple Inc. All rights reserved. | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "EmuBusDriverDxe.h" | |
| // | |
| // DriverBinding protocol global | |
| // | |
| EFI_DRIVER_BINDING_PROTOCOL gEmuBusDriverBinding = { | |
| EmuBusDriverBindingSupported, | |
| EmuBusDriverBindingStart, | |
| EmuBusDriverBindingStop, | |
| 0xa, | |
| NULL, | |
| NULL | |
| }; | |
| EFI_STATUS | |
| EFIAPI | |
| EmuBusDriverBindingSupported ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE ControllerHandle, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
| EMU_THUNK_PROTOCOL *EmuThunk; | |
| // | |
| // Check the contents of the first Device Path Node of RemainingDevicePath to make sure | |
| // it is a legal Device Path Node for this bus driver's children. | |
| // | |
| if (RemainingDevicePath != NULL) { | |
| // | |
| // Check if RemainingDevicePath is the End of Device Path Node, | |
| // if yes, go on checking other conditions | |
| // | |
| if (!IsDevicePathEnd (RemainingDevicePath)) { | |
| // | |
| // If RemainingDevicePath isn't the End of Device Path Node, | |
| // check its validation | |
| // | |
| if (RemainingDevicePath->Type != HARDWARE_DEVICE_PATH || | |
| RemainingDevicePath->SubType != HW_VENDOR_DP || | |
| DevicePathNodeLength(RemainingDevicePath) != sizeof(EMU_VENDOR_DEVICE_PATH_NODE)) { | |
| return EFI_UNSUPPORTED; | |
| } | |
| } | |
| } | |
| // | |
| // Open the IO Abstraction(s) needed to perform the supported test | |
| // | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEmuThunkProtocolGuid, | |
| (VOID **)&EmuThunk , | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (Status == EFI_ALREADY_STARTED) { | |
| return EFI_SUCCESS; | |
| } | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Close the I/O Abstraction(s) used to perform the supported test | |
| // | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEmuThunkProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| // | |
| // Open the EFI Device Path protocol needed to perform the supported test | |
| // | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID **)&ParentDevicePath, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (Status == EFI_ALREADY_STARTED) { | |
| return EFI_SUCCESS; | |
| } | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| // | |
| // Close protocol, don't use device path protocol in the Support() function | |
| // | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| return Status; | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| EmuBusDriverBindingStart ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE ControllerHandle, | |
| IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_STATUS InstallStatus; | |
| EMU_THUNK_PROTOCOL *EmuThunk; | |
| EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
| EMU_IO_DEVICE *EmuDevice; | |
| EMU_BUS_DEVICE *EmuBusDevice; | |
| EMU_IO_THUNK_PROTOCOL *EmuIoThunk; | |
| UINT16 ComponentName[512]; | |
| EMU_VENDOR_DEVICE_PATH_NODE *Node; | |
| BOOLEAN CreateDevice; | |
| InstallStatus = EFI_UNSUPPORTED; | |
| Status = EFI_UNSUPPORTED; | |
| // | |
| // Grab the protocols we need | |
| // | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| (VOID **)&ParentDevicePath, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { | |
| return Status; | |
| } | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEmuThunkProtocolGuid, | |
| (VOID **)&EmuThunk, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_BY_DRIVER | |
| ); | |
| if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { | |
| return Status; | |
| } | |
| if (Status != EFI_ALREADY_STARTED) { | |
| EmuBusDevice = AllocatePool (sizeof (EMU_BUS_DEVICE)); | |
| if (EmuBusDevice == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| EmuBusDevice->Signature = EMU_BUS_DEVICE_SIGNATURE; | |
| EmuBusDevice->ControllerNameTable = NULL; | |
| AddUnicodeString2 ( | |
| "eng", | |
| gEmuBusDriverComponentName.SupportedLanguages, | |
| &EmuBusDevice->ControllerNameTable, | |
| L"Emulator Bus Controller", | |
| TRUE | |
| ); | |
| AddUnicodeString2 ( | |
| "en", | |
| gEmuBusDriverComponentName2.SupportedLanguages, | |
| &EmuBusDevice->ControllerNameTable, | |
| L"Emulator Bus Controller", | |
| FALSE | |
| ); | |
| Status = gBS->InstallMultipleProtocolInterfaces ( | |
| &ControllerHandle, | |
| &gEfiCallerIdGuid, EmuBusDevice, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreeUnicodeStringTable (EmuBusDevice->ControllerNameTable); | |
| gBS->FreePool (EmuBusDevice); | |
| return Status; | |
| } | |
| } | |
| for (Status = EFI_SUCCESS, EmuIoThunk = NULL; !EFI_ERROR (Status); ) { | |
| Status = EmuThunk->GetNextProtocol (TRUE, &EmuIoThunk); | |
| if (EFI_ERROR (Status)) { | |
| break; | |
| } | |
| CreateDevice = TRUE; | |
| if (RemainingDevicePath != NULL) { | |
| CreateDevice = FALSE; | |
| // | |
| // Check if RemainingDevicePath is the End of Device Path Node, | |
| // if yes, don't create any child device | |
| // | |
| if (!IsDevicePathEnd (RemainingDevicePath)) { | |
| // | |
| // If RemainingDevicePath isn't the End of Device Path Node, | |
| // check its validation | |
| // | |
| Node = (EMU_VENDOR_DEVICE_PATH_NODE *) RemainingDevicePath; | |
| if (Node->VendorDevicePath.Header.Type == HARDWARE_DEVICE_PATH && | |
| Node->VendorDevicePath.Header.SubType == HW_VENDOR_DP && | |
| DevicePathNodeLength (&Node->VendorDevicePath.Header) == sizeof (EMU_VENDOR_DEVICE_PATH_NODE) | |
| ) { | |
| if (CompareGuid (&Node->VendorDevicePath.Guid, EmuIoThunk->Protocol) && Node->Instance == EmuIoThunk->Instance) { | |
| CreateDevice = TRUE; | |
| } | |
| } | |
| } | |
| } | |
| if (CreateDevice) { | |
| // | |
| // Allocate instance structure, and fill in parent information. | |
| // | |
| EmuDevice = AllocatePool (sizeof (EMU_IO_DEVICE)); | |
| if (EmuDevice == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| EmuDevice->Handle = NULL; | |
| EmuDevice->ControllerHandle = ControllerHandle; | |
| EmuDevice->ParentDevicePath = ParentDevicePath; | |
| CopyMem (&EmuDevice->EmuIoThunk, EmuIoThunk, sizeof (EMU_IO_THUNK_PROTOCOL)); | |
| EmuDevice->ControllerNameTable = NULL; | |
| StrnCpy (ComponentName, EmuIoThunk->ConfigString, sizeof (ComponentName)/sizeof (CHAR16)); | |
| EmuDevice->DevicePath = EmuBusCreateDevicePath ( | |
| ParentDevicePath, | |
| EmuIoThunk->Protocol, | |
| EmuIoThunk->Instance | |
| ); | |
| if (EmuDevice->DevicePath == NULL) { | |
| gBS->FreePool (EmuDevice); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| AddUnicodeString ( | |
| "eng", | |
| gEmuBusDriverComponentName.SupportedLanguages, | |
| &EmuDevice->ControllerNameTable, | |
| ComponentName | |
| ); | |
| EmuDevice->Signature = EMU_IO_DEVICE_SIGNATURE; | |
| InstallStatus = gBS->InstallMultipleProtocolInterfaces ( | |
| &EmuDevice->Handle, | |
| &gEfiDevicePathProtocolGuid, EmuDevice->DevicePath, | |
| &gEmuIoThunkProtocolGuid, &EmuDevice->EmuIoThunk, | |
| NULL | |
| ); | |
| if (EFI_ERROR (InstallStatus)) { | |
| FreeUnicodeStringTable (EmuDevice->ControllerNameTable); | |
| gBS->FreePool (EmuDevice); | |
| } else { | |
| // | |
| // Open For Child Device | |
| // | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEmuThunkProtocolGuid, | |
| (VOID **)&EmuThunk , | |
| This->DriverBindingHandle, | |
| EmuDevice->Handle, | |
| EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| InstallStatus = EFI_SUCCESS; | |
| } | |
| } | |
| } | |
| } | |
| return InstallStatus; | |
| } | |
| EFI_STATUS | |
| EFIAPI | |
| EmuBusDriverBindingStop ( | |
| IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
| IN EFI_HANDLE ControllerHandle, | |
| IN UINTN NumberOfChildren, | |
| IN EFI_HANDLE *ChildHandleBuffer | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| BOOLEAN AllChildrenStopped; | |
| EMU_IO_THUNK_PROTOCOL *EmuIoThunk; | |
| EMU_BUS_DEVICE *EmuBusDevice; | |
| EMU_IO_DEVICE *EmuDevice; | |
| EMU_THUNK_PROTOCOL *EmuThunk; | |
| // | |
| // Complete all outstanding transactions to Controller. | |
| // Don't allow any new transaction to Controller to be started. | |
| // | |
| if (NumberOfChildren == 0) { | |
| // | |
| // Close the bus driver | |
| // | |
| Status = gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEfiCallerIdGuid, | |
| (VOID **)&EmuBusDevice, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return Status; | |
| } | |
| gBS->UninstallMultipleProtocolInterfaces ( | |
| ControllerHandle, | |
| &gEfiCallerIdGuid, EmuBusDevice, | |
| NULL | |
| ); | |
| FreeUnicodeStringTable (EmuBusDevice->ControllerNameTable); | |
| gBS->FreePool (EmuBusDevice); | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEmuThunkProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEfiDevicePathProtocolGuid, | |
| This->DriverBindingHandle, | |
| ControllerHandle | |
| ); | |
| return EFI_SUCCESS; | |
| } | |
| AllChildrenStopped = TRUE; | |
| for (Index = 0; Index < NumberOfChildren; Index++) { | |
| Status = gBS->OpenProtocol ( | |
| ChildHandleBuffer[Index], | |
| &gEmuIoThunkProtocolGuid, | |
| (VOID **)&EmuIoThunk, | |
| This->DriverBindingHandle, | |
| ControllerHandle, | |
| EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| EmuDevice = EMU_IO_DEVICE_FROM_THIS (EmuIoThunk); | |
| Status = gBS->CloseProtocol ( | |
| ControllerHandle, | |
| &gEmuThunkProtocolGuid, | |
| This->DriverBindingHandle, | |
| EmuDevice->Handle | |
| ); | |
| Status = gBS->UninstallMultipleProtocolInterfaces ( | |
| EmuDevice->Handle, | |
| &gEfiDevicePathProtocolGuid, EmuDevice->DevicePath, | |
| &gEmuIoThunkProtocolGuid, &EmuDevice->EmuIoThunk, | |
| NULL | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| gBS->OpenProtocol ( | |
| ControllerHandle, | |
| &gEmuThunkProtocolGuid, | |
| (VOID **) &EmuThunk , | |
| This->DriverBindingHandle, | |
| EmuDevice->Handle, | |
| EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
| ); | |
| } else { | |
| // | |
| // Close the child handle | |
| // | |
| FreeUnicodeStringTable (EmuDevice->ControllerNameTable); | |
| FreePool (EmuDevice); | |
| } | |
| } | |
| if (EFI_ERROR (Status)) { | |
| AllChildrenStopped = FALSE; | |
| } | |
| } | |
| if (!AllChildrenStopped) { | |
| return EFI_DEVICE_ERROR; | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /*++ | |
| Routine Description: | |
| Create a device path node using Guid and InstanceNumber and append it to | |
| the passed in RootDevicePath | |
| Arguments: | |
| RootDevicePath - Root of the device path to return. | |
| Guid - GUID to use in vendor device path node. | |
| InstanceNumber - Instance number to use in the vendor device path. This | |
| argument is needed to make sure each device path is unique. | |
| Returns: | |
| EFI_DEVICE_PATH_PROTOCOL | |
| **/ | |
| EFI_DEVICE_PATH_PROTOCOL * | |
| EmuBusCreateDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *RootDevicePath, | |
| IN EFI_GUID *Guid, | |
| IN UINT16 InstanceNumber | |
| ) | |
| { | |
| EMU_VENDOR_DEVICE_PATH_NODE DevicePath; | |
| DevicePath.VendorDevicePath.Header.Type = HARDWARE_DEVICE_PATH; | |
| DevicePath.VendorDevicePath.Header.SubType = HW_VENDOR_DP; | |
| SetDevicePathNodeLength (&DevicePath.VendorDevicePath.Header, sizeof (EMU_VENDOR_DEVICE_PATH_NODE)); | |
| // | |
| // The GUID defines the Class | |
| // | |
| CopyMem (&DevicePath.VendorDevicePath.Guid, Guid, sizeof (EFI_GUID)); | |
| // | |
| // Add an instance number so we can make sure there are no Device Path | |
| // duplication. | |
| // | |
| DevicePath.Instance = InstanceNumber; | |
| return AppendDevicePathNode ( | |
| RootDevicePath, | |
| (EFI_DEVICE_PATH_PROTOCOL *) &DevicePath | |
| ); | |
| } | |
| /** | |
| The user Entry Point for module EmuBusDriver. The user code starts with this function. | |
| @param[in] ImageHandle The firmware allocated handle for the EFI image. | |
| @param[in] SystemTable A pointer to the EFI System Table. | |
| @retval EFI_SUCCESS The entry point is executed successfully. | |
| @retval other Some error occurs when executing this entry point. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| InitializeEmuBusDriver ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| Status = EfiLibInstallAllDriverProtocols ( | |
| ImageHandle, | |
| SystemTable, | |
| &gEmuBusDriverBinding, | |
| ImageHandle, | |
| &gEmuBusDriverComponentName, | |
| NULL, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| return Status; | |
| } | |