/** @file | |
Main file of the MMC Dxe driver. The driver entrypoint is defined into this file. | |
Copyright (c) 2011-2013, ARM Limited. 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 <Protocol/DevicePath.h> | |
#include <Library/BaseLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/DevicePathLib.h> | |
#include "Mmc.h" | |
EFI_BLOCK_IO_MEDIA mMmcMediaTemplate = { | |
SIGNATURE_32('m','m','c','o'), // MediaId | |
TRUE, // RemovableMedia | |
FALSE, // MediaPresent | |
FALSE, // LogicalPartition | |
FALSE, // ReadOnly | |
FALSE, // WriteCaching | |
512, // BlockSize | |
4, // IoAlign | |
0, // Pad | |
0 // LastBlock | |
}; | |
// | |
// This device structure is serviced as a header. | |
// Its next field points to the first root bridge device node. | |
// | |
LIST_ENTRY mMmcHostPool; | |
/** | |
Event triggered by the timer to check if any cards have been removed | |
or if new ones have been plugged in | |
**/ | |
EFI_EVENT gCheckCardsEvent; | |
/** | |
Initialize the MMC Host Pool to support multiple MMC devices | |
**/ | |
VOID | |
InitializeMmcHostPool ( | |
VOID | |
) | |
{ | |
InitializeListHead (&mMmcHostPool); | |
} | |
/** | |
Insert a new Mmc Host controller to the pool | |
**/ | |
VOID | |
InsertMmcHost ( | |
IN MMC_HOST_INSTANCE *MmcHostInstance | |
) | |
{ | |
InsertTailList (&mMmcHostPool, &(MmcHostInstance->Link)); | |
} | |
/* | |
Remove a new Mmc Host controller to the pool | |
*/ | |
VOID | |
RemoveMmcHost ( | |
IN MMC_HOST_INSTANCE *MmcHostInstance | |
) | |
{ | |
RemoveEntryList (&(MmcHostInstance->Link)); | |
} | |
MMC_HOST_INSTANCE* CreateMmcHostInstance ( | |
IN EFI_MMC_HOST_PROTOCOL* MmcHost | |
) | |
{ | |
EFI_STATUS Status; | |
MMC_HOST_INSTANCE* MmcHostInstance; | |
EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
MmcHostInstance = AllocateZeroPool (sizeof (MMC_HOST_INSTANCE)); | |
if (MmcHostInstance == NULL) { | |
return NULL; | |
} | |
MmcHostInstance->Signature = MMC_HOST_INSTANCE_SIGNATURE; | |
MmcHostInstance->State = MmcHwInitializationState; | |
MmcHostInstance->BlockIo.Media = AllocateCopyPool (sizeof(EFI_BLOCK_IO_MEDIA), &mMmcMediaTemplate); | |
if (MmcHostInstance->BlockIo.Media == NULL) { | |
goto FREE_INSTANCE; | |
} | |
MmcHostInstance->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION; | |
MmcHostInstance->BlockIo.Reset = MmcReset; | |
MmcHostInstance->BlockIo.ReadBlocks = MmcReadBlocks; | |
MmcHostInstance->BlockIo.WriteBlocks = MmcWriteBlocks; | |
MmcHostInstance->BlockIo.FlushBlocks = MmcFlushBlocks; | |
MmcHostInstance->MmcHost = MmcHost; | |
// Create DevicePath for the new MMC Host | |
Status = MmcHost->BuildDevicePath (MmcHost, &NewDevicePathNode); | |
if (EFI_ERROR (Status)) { | |
goto FREE_MEDIA; | |
} | |
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocatePool (END_DEVICE_PATH_LENGTH); | |
if (DevicePath == NULL) { | |
goto FREE_MEDIA; | |
} | |
SetDevicePathEndNode (DevicePath); | |
MmcHostInstance->DevicePath = AppendDevicePathNode (DevicePath, NewDevicePathNode); | |
// Publish BlockIO protocol interface | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&MmcHostInstance->MmcHandle, | |
&gEfiBlockIoProtocolGuid,&MmcHostInstance->BlockIo, | |
&gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath, | |
NULL | |
); | |
if (EFI_ERROR(Status)) { | |
goto FREE_DEVICE_PATH; | |
} | |
return MmcHostInstance; | |
FREE_DEVICE_PATH: | |
FreePool(DevicePath); | |
FREE_MEDIA: | |
FreePool(MmcHostInstance->BlockIo.Media); | |
FREE_INSTANCE: | |
FreePool(MmcHostInstance); | |
return NULL; | |
} | |
EFI_STATUS DestroyMmcHostInstance ( | |
IN MMC_HOST_INSTANCE* MmcHostInstance | |
) | |
{ | |
EFI_STATUS Status; | |
// Uninstall Protocol Interfaces | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
MmcHostInstance->MmcHandle, | |
&gEfiBlockIoProtocolGuid,&(MmcHostInstance->BlockIo), | |
&gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
// Free Memory allocated for the instance | |
if (MmcHostInstance->BlockIo.Media) { | |
FreePool(MmcHostInstance->BlockIo.Media); | |
} | |
FreePool (MmcHostInstance); | |
return Status; | |
} | |
/** | |
This function checks if the controller implement the Mmc Host and the Device Path Protocols | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MmcDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
//EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; | |
EFI_MMC_HOST_PROTOCOL *MmcHost; | |
EFI_DEV_PATH_PTR Node; | |
// | |
// Check RemainingDevicePath validation | |
// | |
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 | |
// | |
Node.DevPath = RemainingDevicePath; | |
if (Node.DevPath->Type != HARDWARE_DEVICE_PATH || | |
Node.DevPath->SubType != HW_VENDOR_DP || | |
DevicePathNodeLength(Node.DevPath) != sizeof(VENDOR_DEVICE_PATH)) { | |
return EFI_UNSUPPORTED; | |
} | |
} | |
} | |
// | |
// Check if Mmc Host protocol is installed by platform | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiMmcHostProtocolGuid, | |
(VOID **) &MmcHost, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (Status == EFI_ALREADY_STARTED) { | |
return EFI_SUCCESS; | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Close the Mmc Host used to perform the supported test | |
// | |
gBS->CloseProtocol ( | |
Controller, | |
&gEfiMmcHostProtocolGuid, | |
This->DriverBindingHandle, | |
Controller | |
); | |
return EFI_SUCCESS; | |
} | |
/** | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MmcDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
MMC_HOST_INSTANCE *MmcHostInstance; | |
EFI_MMC_HOST_PROTOCOL *MmcHost; | |
// | |
// Check RemainingDevicePath validation | |
// | |
if (RemainingDevicePath != NULL) { | |
// | |
// Check if RemainingDevicePath is the End of Device Path Node, | |
// if yes, return EFI_SUCCESS | |
// | |
if (IsDevicePathEnd (RemainingDevicePath)) { | |
return EFI_SUCCESS; | |
} | |
} | |
// | |
// Get the Mmc Host protocol | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
&gEfiMmcHostProtocolGuid, | |
(VOID **) &MmcHost, | |
This->DriverBindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
if (Status == EFI_ALREADY_STARTED) { | |
return EFI_SUCCESS; | |
} | |
return Status; | |
} | |
MmcHostInstance = CreateMmcHostInstance(MmcHost); | |
if (MmcHostInstance != NULL) { | |
// Add the handle to the pool | |
InsertMmcHost (MmcHostInstance); | |
MmcHostInstance->Initialized = FALSE; | |
// Detect card presence now | |
CheckCardsCallback (NULL, NULL); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MmcDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE Controller, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
EFI_STATUS Status = EFI_SUCCESS; | |
LIST_ENTRY *CurrentLink; | |
MMC_HOST_INSTANCE *MmcHostInstance; | |
MMC_TRACE("MmcDriverBindingStop()"); | |
// For each MMC instance | |
CurrentLink = mMmcHostPool.ForwardLink; | |
while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) { | |
MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink); | |
ASSERT(MmcHostInstance != NULL); | |
// Close gEfiMmcHostProtocolGuid | |
Status = gBS->CloseProtocol ( | |
Controller, | |
&gEfiMmcHostProtocolGuid,(VOID **) &MmcHostInstance->MmcHost, | |
This->DriverBindingHandle | |
); | |
// Remove MMC Host Instance from the pool | |
RemoveMmcHost (MmcHostInstance); | |
// Destroy MmcHostInstance | |
DestroyMmcHostInstance (MmcHostInstance); | |
} | |
return Status; | |
} | |
VOID | |
EFIAPI | |
CheckCardsCallback ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
LIST_ENTRY *CurrentLink; | |
MMC_HOST_INSTANCE *MmcHostInstance; | |
EFI_STATUS Status; | |
CurrentLink = mMmcHostPool.ForwardLink; | |
while (CurrentLink != NULL && CurrentLink != &mMmcHostPool) { | |
MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink); | |
ASSERT(MmcHostInstance != NULL); | |
if (MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost) == !MmcHostInstance->Initialized) { | |
MmcHostInstance->State = MmcHwInitializationState; | |
MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized; | |
MmcHostInstance->Initialized = !MmcHostInstance->Initialized; | |
if (MmcHostInstance->BlockIo.Media->MediaPresent) { | |
InitializeMmcDevice (MmcHostInstance); | |
} | |
Status = gBS->ReinstallProtocolInterface ( | |
(MmcHostInstance->MmcHandle), | |
&gEfiBlockIoProtocolGuid, | |
&(MmcHostInstance->BlockIo), | |
&(MmcHostInstance->BlockIo) | |
); | |
if (EFI_ERROR(Status)) { | |
Print(L"MMC Card: Error reinstalling BlockIo interface\n"); | |
} | |
} | |
CurrentLink = CurrentLink->ForwardLink; | |
} | |
} | |
EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = { | |
MmcDriverBindingSupported, | |
MmcDriverBindingStart, | |
MmcDriverBindingStop, | |
0xa, | |
NULL, | |
NULL | |
}; | |
/** | |
**/ | |
EFI_STATUS | |
EFIAPI | |
MmcDxeInitialize ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
// | |
// Initializes MMC Host pool | |
// | |
InitializeMmcHostPool (); | |
// | |
// Install driver model protocol(s). | |
// | |
Status = EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gMmcDriverBinding, | |
ImageHandle, | |
&gMmcComponentName, | |
&gMmcComponentName2 | |
); | |
ASSERT_EFI_ERROR (Status); | |
// Install driver diagnostics | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&ImageHandle, | |
&gEfiDriverDiagnostics2ProtocolGuid,&gMmcDriverDiagnostics2, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
// Use a timer to detect if a card has been plugged in or removed | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL | EVT_TIMER, | |
TPL_CALLBACK, | |
CheckCardsCallback, | |
NULL, | |
&gCheckCardsEvent); | |
ASSERT_EFI_ERROR (Status); | |
Status = gBS->SetTimer( | |
gCheckCardsEvent, | |
TimerPeriodic, | |
(UINT64)(10*1000*200)); // 200 ms | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} |