audk/StdLib/EfiSocketLib/Service.c

480 lines
14 KiB
C

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