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