/** @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;
}