/** @file | |
Connect to and disconnect from the various network layers | |
Copyright (c) 2011, Intel Corporation | |
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 "Socket.h" | |
/** | |
Connect to the network service bindings | |
Walk the network service protocols on the controller handle and | |
locate any that are not in use. Create ::ESL_SERVICE structures to | |
manage the network layer interfaces for the socket driver. Tag | |
each of the network interfaces that are being used. Finally, this | |
routine calls ESL_SOCKET_BINDING::pfnInitialize to prepare the network | |
interface for use by the socket layer. | |
@param [in] BindingHandle Handle for protocol binding. | |
@param [in] Controller Handle of device to work with. | |
@retval EFI_SUCCESS This driver is added to Controller. | |
@retval EFI_OUT_OF_RESOURCES No more memory available. | |
@retval EFI_UNSUPPORTED This driver does not support this device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EslServiceConnect ( | |
IN EFI_HANDLE BindingHandle, | |
IN EFI_HANDLE Controller | |
) | |
{ | |
BOOLEAN bInUse; | |
EFI_STATUS ExitStatus; | |
UINTN LengthInBytes; | |
UINT8 * pBuffer; | |
CONST ESL_SOCKET_BINDING * pEnd; | |
VOID * pJunk; | |
ESL_SERVICE ** ppServiceListHead; | |
ESL_SERVICE * pService; | |
CONST ESL_SOCKET_BINDING * pSocketBinding; | |
EFI_SERVICE_BINDING_PROTOCOL * pServiceBinding; | |
EFI_STATUS Status; | |
EFI_TPL TplPrevious; | |
DBG_ENTER ( ); | |
// | |
// Assume the list is empty | |
// | |
ExitStatus = EFI_UNSUPPORTED; | |
bInUse = FALSE; | |
// | |
// Walk the list of network connection points | |
// | |
pSocketBinding = &cEslSocketBinding[0]; | |
pEnd = &pSocketBinding[ cEslSocketBindingEntries ]; | |
while ( pEnd > pSocketBinding ) { | |
// | |
// Determine if the controller supports the network protocol | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
pSocketBinding->pNetworkBinding, | |
(VOID**)&pServiceBinding, | |
BindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if ( !EFI_ERROR ( Status )) { | |
// | |
// Determine if the socket layer is already connected | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
(EFI_GUID *)pSocketBinding->pTagGuid, | |
&pJunk, | |
BindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if ( EFI_UNSUPPORTED == Status ) { | |
// | |
// Allocate a service structure since the tag is not present | |
// | |
LengthInBytes = sizeof ( *pService ); | |
Status = gBS->AllocatePool ( | |
EfiRuntimeServicesData, | |
LengthInBytes, | |
(VOID **) &pService | |
); | |
if ( !EFI_ERROR ( Status )) { | |
DEBUG (( DEBUG_POOL | DEBUG_INIT, | |
"0x%08x: Allocate pService, %d bytes\r\n", | |
pService, | |
LengthInBytes )); | |
// | |
// Set the structure signature and service binding | |
// | |
ZeroMem ( pService, LengthInBytes ); | |
pService->Signature = SERVICE_SIGNATURE; | |
pService->pSocketBinding = pSocketBinding; | |
pService->Controller = Controller; | |
pService->pServiceBinding = pServiceBinding; | |
// | |
// Mark the controller in use | |
// | |
if ( !bInUse ) { | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&Controller, | |
&gEfiCallerIdGuid, | |
NULL, | |
NULL | |
); | |
if ( !EFI_ERROR ( Status )) { | |
DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, | |
"Installed: gEfiCallerIdGuid on 0x%08x\r\n", | |
Controller )); | |
bInUse = TRUE; | |
} | |
else { | |
if ( EFI_INVALID_PARAMETER == Status ) { | |
Status = EFI_SUCCESS; | |
} | |
} | |
} | |
if ( !EFI_ERROR ( Status )) { | |
// | |
// Mark the network service protocol in use | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&Controller, | |
pSocketBinding->pTagGuid, | |
pService, | |
NULL | |
); | |
if ( !EFI_ERROR ( Status )) { | |
DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, | |
"Installed: %s TagGuid on 0x%08x\r\n", | |
pSocketBinding->pName, | |
Controller )); | |
// | |
// Synchronize with the socket layer | |
// | |
RAISE_TPL ( TplPrevious, TPL_SOCKETS ); | |
// | |
// Connect the service to the list | |
// | |
pBuffer = (UINT8 *)&mEslLayer; | |
pBuffer = &pBuffer[ pSocketBinding->ServiceListOffset ]; | |
ppServiceListHead = (ESL_SERVICE **)pBuffer; | |
pService->pNext = *ppServiceListHead; | |
*ppServiceListHead = pService; | |
// | |
// Release the socket layer synchronization | |
// | |
RESTORE_TPL ( TplPrevious ); | |
// | |
// At least one service was made available | |
// | |
ExitStatus = EFI_SUCCESS; | |
} | |
else { | |
DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT, | |
"ERROR - Failed to install %s TagGuid on 0x%08x, Status: %r\r\n", | |
pSocketBinding->pName, | |
Controller, | |
Status )); | |
} | |
if ( EFI_ERROR ( Status )) { | |
// | |
// The controller is no longer in use | |
// | |
if ( bInUse ) { | |
gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiCallerIdGuid, | |
NULL, | |
NULL ); | |
DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, | |
"Removed: gEfiCallerIdGuid from 0x%08x\r\n", | |
Controller )); | |
} | |
} | |
} | |
else { | |
DEBUG (( DEBUG_ERROR | DEBUG_INIT, | |
"ERROR - Failed to install gEfiCallerIdGuid on 0x%08x, Status: %r\r\n", | |
Controller, | |
Status )); | |
} | |
// | |
// Release the service if necessary | |
// | |
if ( EFI_ERROR ( Status )) { | |
gBS->FreePool ( pService ); | |
DEBUG (( DEBUG_POOL | DEBUG_INIT, | |
"0x%08x: Free pService, %d bytes\r\n", | |
pService, | |
sizeof ( *pService ))); | |
pService = NULL; | |
} | |
} | |
else { | |
DEBUG (( DEBUG_ERROR | DEBUG_INIT, | |
"ERROR - Failed service allocation, Status: %r\r\n", | |
Status )); | |
ExitStatus = EFI_OUT_OF_RESOURCES; | |
break; | |
} | |
} | |
} | |
// | |
// Set the next network protocol | |
// | |
pSocketBinding += 1; | |
} | |
// | |
// Display the driver start status | |
// | |
DBG_EXIT_STATUS ( ExitStatus ); | |
return ExitStatus; | |
} | |
/** | |
Shutdown the connections to the network layer by locating the | |
tags on the network interfaces established by ::EslServiceConnect. | |
This routine shutdowns any activity on the network interface and | |
then frees the ::ESL_SERVICE structures. | |
@param [in] BindingHandle Handle for protocol binding. | |
@param [in] Controller Handle of device to stop driver on. | |
@retval EFI_SUCCESS This driver is removed Controller. | |
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error. | |
@retval other This driver was not removed from this device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EslServiceDisconnect ( | |
IN EFI_HANDLE BindingHandle, | |
IN EFI_HANDLE Controller | |
) | |
{ | |
UINT8 * pBuffer; | |
CONST ESL_SOCKET_BINDING * pEnd; | |
ESL_PORT * pPort; | |
ESL_SERVICE * pPreviousService; | |
ESL_SERVICE * pService; | |
ESL_SERVICE ** ppServiceListHead; | |
CONST ESL_SOCKET_BINDING * pSocketBinding; | |
EFI_STATUS Status; | |
EFI_TPL TplPrevious; | |
DBG_ENTER ( ); | |
// | |
// Walk the list of network connection points in reverse order | |
// | |
pEnd = &cEslSocketBinding[0]; | |
pSocketBinding = &pEnd[ cEslSocketBindingEntries ]; | |
while ( pEnd < pSocketBinding ) { | |
// | |
// Set the next network protocol | |
// | |
pSocketBinding -= 1; | |
// | |
// Determine if the driver connected | |
// | |
Status = gBS->OpenProtocol ( | |
Controller, | |
(EFI_GUID *)pSocketBinding->pTagGuid, | |
(VOID **)&pService, | |
BindingHandle, | |
Controller, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if ( !EFI_ERROR ( Status )) { | |
// | |
// Synchronize with the socket layer | |
// | |
RAISE_TPL ( TplPrevious, TPL_SOCKETS ); | |
// | |
// Walk the list of ports | |
// | |
pPort = pService->pPortList; | |
while ( NULL != pPort ) { | |
// | |
// Remove the port from the port list | |
// | |
pPort->pService = NULL; | |
pService->pPortList = pPort->pLinkService; | |
// | |
// Close the port | |
// | |
EslSocketPortCloseStart ( pPort, | |
TRUE, | |
DEBUG_POOL | DEBUG_INIT ); | |
// | |
// Set the next port | |
// | |
pPort = pService->pPortList; | |
} | |
// | |
// Remove the service from the service list | |
// | |
pBuffer = (UINT8 *)&mEslLayer; | |
pBuffer = &pBuffer[ pService->pSocketBinding->ServiceListOffset ]; | |
ppServiceListHead = (ESL_SERVICE **)pBuffer; | |
pPreviousService = *ppServiceListHead; | |
if ( pService == pPreviousService ) { | |
// | |
// Remove the service from the beginning of the list | |
// | |
*ppServiceListHead = pService->pNext; | |
} | |
else { | |
// | |
// Remove the service from the middle of the list | |
// | |
while ( NULL != pPreviousService ) { | |
if ( pService == pPreviousService->pNext ) { | |
pPreviousService->pNext = pService->pNext; | |
break; | |
} | |
pPreviousService = pPreviousService->pNext; | |
} | |
} | |
// | |
// Release the socket layer synchronization | |
// | |
RESTORE_TPL ( TplPrevious ); | |
// | |
// Break the driver connection | |
// | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
pSocketBinding->pTagGuid, | |
pService, | |
NULL ); | |
if ( !EFI_ERROR ( Status )) { | |
DEBUG (( DEBUG_POOL | DEBUG_INIT, | |
"Removed: %s TagGuid from 0x%08x\r\n", | |
pSocketBinding->pName, | |
Controller )); | |
} | |
else { | |
DEBUG (( DEBUG_ERROR | DEBUG_POOL | DEBUG_INIT, | |
"ERROR - Failed to removed %s TagGuid from 0x%08x, Status: %r\r\n", | |
pSocketBinding->pName, | |
Controller, | |
Status )); | |
} | |
// | |
// Free the service structure | |
// | |
Status = gBS->FreePool ( pService ); | |
if ( !EFI_ERROR ( Status )) { | |
DEBUG (( DEBUG_POOL | DEBUG_INIT, | |
"0x%08x: Free pService, %d bytes\r\n", | |
pService, | |
sizeof ( *pService ))); | |
} | |
else { | |
DEBUG (( DEBUG_POOL | DEBUG_INIT, | |
"ERROR - Failed to free pService 0x%08x, Status: %r\r\n", | |
pService, | |
Status )); | |
} | |
pService = NULL; | |
} | |
} | |
// | |
// The controller is no longer in use | |
// | |
gBS->UninstallMultipleProtocolInterfaces ( | |
Controller, | |
&gEfiCallerIdGuid, | |
NULL, | |
NULL ); | |
DEBUG (( DEBUG_POOL | DEBUG_INIT | DEBUG_INFO, | |
"Removed: gEfiCallerIdGuid from 0x%08x\r\n", | |
Controller )); | |
// | |
// The driver is disconnected from the network controller | |
// | |
Status = EFI_SUCCESS; | |
// | |
// Display the driver start status | |
// | |
DBG_EXIT_STATUS ( Status ); | |
return Status; | |
} | |
/** | |
Initialize the service layer | |
@param [in] ImageHandle Handle for the image. | |
**/ | |
VOID | |
EFIAPI | |
EslServiceLoad ( | |
IN EFI_HANDLE ImageHandle | |
) | |
{ | |
ESL_LAYER * pLayer; | |
// | |
// Save the image handle | |
// | |
pLayer = &mEslLayer; | |
ZeroMem ( pLayer, sizeof ( *pLayer )); | |
pLayer->Signature = LAYER_SIGNATURE; | |
pLayer->ImageHandle = ImageHandle; | |
// | |
// Connect the service binding protocol to the image handle | |
// | |
pLayer->pServiceBinding = &mEfiServiceBinding; | |
} | |
/** | |
Shutdown the service layer | |
**/ | |
VOID | |
EFIAPI | |
EslServiceUnload ( | |
VOID | |
) | |
{ | |
ESL_LAYER * pLayer; | |
// | |
// Undo the work by ServiceLoad | |
// | |
pLayer = &mEslLayer; | |
pLayer->ImageHandle = NULL; | |
pLayer->pServiceBinding = NULL; | |
} |