mirror of https://github.com/acidanthera/audk.git
449 lines
12 KiB
C
449 lines
12 KiB
C
/** @file
|
|
Main file of the MMC Dxe driver. The driver entrypoint is defined into this file.
|
|
|
|
Copyright (c) 2011, 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 <Protocol/MmcHost.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/DebugLib.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(&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;
|
|
}
|
|
|
|
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->Initialized) {
|
|
MmcHostInstance->State = MmcHwInitializationState;
|
|
MmcHostInstance->BlockIo.Media->MediaPresent = !MmcHostInstance->Initialized;
|
|
MmcHostInstance->Initialized = !MmcHostInstance->Initialized;
|
|
|
|
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;
|
|
}
|