/** @file The wrap of TCP/IP Socket interface Copyright (c) 2004 - 2007, 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. Module Name: IScsiTcp4Io.c Abstract: The wrap of TCP/IP Socket interface **/ #include "IScsiImpl.h" /** The common notify function associated with various Tcp4Io events. @param Event[in] The event signaled. @param Contect[in] The context. @retval None. **/ VOID EFIAPI Tcp4IoCommonNotify ( IN EFI_EVENT Event, IN VOID *Context ) { *((BOOLEAN *) Context) = TRUE; } /** Create a TCP socket with the specified configuration data. @param Image[in] The handle of the driver image. @param Controller[in] The handle of the controller. @param ConfigData[in] The Tcp4 configuration data. @param Tcp4Io[in] The Tcp4Io. @retval EFI_SUCCESS The TCP socket is created and configured. @retval Other Failed to create the TCP socket or configure it. **/ EFI_STATUS Tcp4IoCreateSocket ( IN EFI_HANDLE Image, IN EFI_HANDLE Controller, IN TCP4_IO_CONFIG_DATA *ConfigData, IN TCP4_IO *Tcp4Io ) { EFI_STATUS Status; EFI_TCP4_PROTOCOL *Tcp4; EFI_TCP4_CONFIG_DATA Tcp4ConfigData; EFI_TCP4_OPTION ControlOption; EFI_TCP4_ACCESS_POINT *AccessPoint; Tcp4Io->Handle = NULL; Tcp4Io->ConnToken.CompletionToken.Event = NULL; Tcp4Io->TxToken.CompletionToken.Event = NULL; Tcp4Io->RxToken.CompletionToken.Event = NULL; Tcp4Io->CloseToken.CompletionToken.Event = NULL; Tcp4 = NULL; // // Create the TCP4 child instance and get the TCP4 protocol. // Status = NetLibCreateServiceChild ( Controller, Image, &gEfiTcp4ServiceBindingProtocolGuid, &Tcp4Io->Handle ); if (EFI_ERROR (Status)) { return Status; } Status = gBS->OpenProtocol ( Tcp4Io->Handle, &gEfiTcp4ProtocolGuid, (VOID **)&Tcp4Io->Tcp4, Image, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { goto ON_ERROR; } Tcp4Io->Image = Image; Tcp4Io->Controller = Controller; Tcp4 = Tcp4Io->Tcp4; // // Set the configuration parameters. // ControlOption.ReceiveBufferSize = 0x200000; ControlOption.SendBufferSize = 0x200000; ControlOption.MaxSynBackLog = 0; ControlOption.ConnectionTimeout = 0; ControlOption.DataRetries = 6; ControlOption.FinTimeout = 0; ControlOption.TimeWaitTimeout = 0; ControlOption.KeepAliveProbes = 4; ControlOption.KeepAliveTime = 0; ControlOption.KeepAliveInterval = 0; ControlOption.EnableNagle = FALSE; ControlOption.EnableTimeStamp = FALSE; ControlOption.EnableWindowScaling = TRUE; ControlOption.EnableSelectiveAck = FALSE; ControlOption.EnablePathMtuDiscovery = FALSE; Tcp4ConfigData.TypeOfService = 8; Tcp4ConfigData.TimeToLive = 255; Tcp4ConfigData.ControlOption = &ControlOption; AccessPoint = &Tcp4ConfigData.AccessPoint; AccessPoint->UseDefaultAddress = FALSE; AccessPoint->StationPort = 0; AccessPoint->RemotePort = ConfigData->RemotePort; AccessPoint->ActiveFlag = TRUE; CopyMem (&AccessPoint->StationAddress, &ConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&AccessPoint->SubnetMask, &ConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&AccessPoint->RemoteAddress, &ConfigData->RemoteIp, sizeof (EFI_IPv4_ADDRESS)); // // Configure the TCP4 protocol. // Status = Tcp4->Configure (Tcp4, &Tcp4ConfigData); if (EFI_ERROR (Status)) { goto ON_ERROR; } if (!EFI_IP4_EQUAL (&ConfigData->Gateway, &mZeroIp4Addr)) { // // the gateway is not zero, add the default route by hand // Status = Tcp4->Routes (Tcp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, &ConfigData->Gateway); if (EFI_ERROR (Status)) { goto ON_ERROR; } } // // Create events for variuos asynchronous operations. // Status = gBS->CreateEvent ( EFI_EVENT_NOTIFY_SIGNAL, TPL_NOTIFY, Tcp4IoCommonNotify, &Tcp4Io->IsConnDone, &Tcp4Io->ConnToken.CompletionToken.Event ); if (EFI_ERROR (Status)) { goto ON_ERROR; } Status = gBS->CreateEvent ( EFI_EVENT_NOTIFY_SIGNAL, TPL_NOTIFY, Tcp4IoCommonNotify, &Tcp4Io->IsTxDone, &Tcp4Io->TxToken.CompletionToken.Event ); if (EFI_ERROR (Status)) { goto ON_ERROR; } Status = gBS->CreateEvent ( EFI_EVENT_NOTIFY_SIGNAL, TPL_NOTIFY, Tcp4IoCommonNotify, &Tcp4Io->IsRxDone, &Tcp4Io->RxToken.CompletionToken.Event ); if (EFI_ERROR (Status)) { goto ON_ERROR; } Status = gBS->CreateEvent ( EFI_EVENT_NOTIFY_SIGNAL, TPL_NOTIFY, Tcp4IoCommonNotify, &Tcp4Io->IsCloseDone, &Tcp4Io->CloseToken.CompletionToken.Event ); if (EFI_ERROR (Status)) { goto ON_ERROR; } Tcp4Io->IsTxDone = FALSE; Tcp4Io->IsRxDone = FALSE; return EFI_SUCCESS; ON_ERROR: if (Tcp4Io->RxToken.CompletionToken.Event != NULL) { gBS->CloseEvent (Tcp4Io->RxToken.CompletionToken.Event); } if (Tcp4Io->TxToken.CompletionToken.Event != NULL) { gBS->CloseEvent (Tcp4Io->TxToken.CompletionToken.Event); } if (Tcp4Io->ConnToken.CompletionToken.Event != NULL) { gBS->CloseEvent (Tcp4Io->ConnToken.CompletionToken.Event); } if (Tcp4 != NULL) { Tcp4->Configure (Tcp4, NULL); gBS->CloseProtocol ( Tcp4Io->Handle, &gEfiTcp4ProtocolGuid, Image, Controller ); } NetLibDestroyServiceChild ( Controller, Image, &gEfiTcp4ServiceBindingProtocolGuid, Tcp4Io->Handle ); return Status; } /** Destroy the socket. @param[in] Tcp4Io The Tcp4Io which wraps the socket to be destroyeds. @retval None. **/ VOID Tcp4IoDestroySocket ( IN TCP4_IO *Tcp4Io ) { EFI_TCP4_PROTOCOL *Tcp4; Tcp4 = Tcp4Io->Tcp4; Tcp4->Configure (Tcp4, NULL); gBS->CloseEvent (Tcp4Io->TxToken.CompletionToken.Event); gBS->CloseEvent (Tcp4Io->RxToken.CompletionToken.Event); gBS->CloseEvent (Tcp4Io->ConnToken.CompletionToken.Event); gBS->CloseProtocol ( Tcp4Io->Handle, &gEfiTcp4ProtocolGuid, Tcp4Io->Image, Tcp4Io->Controller ); NetLibDestroyServiceChild ( Tcp4Io->Controller, Tcp4Io->Image, &gEfiTcp4ServiceBindingProtocolGuid, Tcp4Io->Handle ); } /** Connect to the other endpoint of the TCP socket. @param Tcp4Io[in] The Tcp4Io wrapping the TCP socket. @param Timeout[in] The time to wait for connection done. @retval None. **/ EFI_STATUS Tcp4IoConnect ( IN TCP4_IO *Tcp4Io, IN EFI_EVENT Timeout ) { EFI_TCP4_PROTOCOL *Tcp4; EFI_STATUS Status; Tcp4Io->IsConnDone = FALSE; Tcp4 = Tcp4Io->Tcp4; Status = Tcp4->Connect (Tcp4, &Tcp4Io->ConnToken); if (EFI_ERROR (Status)) { return Status; } while (!Tcp4Io->IsConnDone && EFI_ERROR (gBS->CheckEvent (Timeout))) { Tcp4->Poll (Tcp4); } if (!Tcp4Io->IsConnDone) { Status = EFI_TIMEOUT; } else { Status = Tcp4Io->ConnToken.CompletionToken.Status; } return Status; } /** Reset the socket. @param Tcp4Io[in] The Tcp4Io wrapping the TCP socket. @retval None. **/ VOID Tcp4IoReset ( IN TCP4_IO *Tcp4Io ) { EFI_STATUS Status; EFI_TCP4_PROTOCOL *Tcp4; Tcp4Io->CloseToken.AbortOnClose = TRUE; Tcp4Io->IsCloseDone = FALSE; Tcp4 = Tcp4Io->Tcp4; Status = Tcp4->Close (Tcp4, &Tcp4Io->CloseToken); if (EFI_ERROR (Status)) { return ; } while (!Tcp4Io->IsCloseDone) { Tcp4->Poll (Tcp4); } } /** Transmit the Packet to the other endpoint of the socket. @param Tcp4Io[in] The Tcp4Io wrapping the TCP socket. @param Packet[in] The packet to transmit @retval EFI_SUCCESS The packet is trasmitted. @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. **/ EFI_STATUS Tcp4IoTransmit ( IN TCP4_IO *Tcp4Io, IN NET_BUF *Packet ) { EFI_TCP4_TRANSMIT_DATA *TxData; EFI_TCP4_PROTOCOL *Tcp4; EFI_STATUS Status; TxData = AllocatePool (sizeof (EFI_TCP4_TRANSMIT_DATA) + (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA)); if (TxData == NULL) { return EFI_OUT_OF_RESOURCES; } TxData->Push = TRUE; TxData->Urgent = FALSE; TxData->DataLength = Packet->TotalSize; // // Build the fragment table. // TxData->FragmentCount = Packet->BlockOpNum; NetbufBuildExt (Packet, (NET_FRAGMENT *) &TxData->FragmentTable[0], &TxData->FragmentCount); Tcp4Io->TxToken.Packet.TxData = TxData; // // Trasnmit the packet. // Tcp4 = Tcp4Io->Tcp4; Status = Tcp4->Transmit (Tcp4, &Tcp4Io->TxToken); if (EFI_ERROR (Status)) { goto ON_EXIT; } while (!Tcp4Io->IsTxDone) { Tcp4->Poll (Tcp4); } Tcp4Io->IsTxDone = FALSE; Status = Tcp4Io->TxToken.CompletionToken.Status; ON_EXIT: gBS->FreePool (TxData); return Status; } /** Receive data from the socket. @param Tcp4Io[in] The Tcp4Io which wraps the socket to be destroyeds. @param Packet[in] The buffer to hold the data copy from the soket rx buffer. @param AsyncMode[in] Is this receive asyncronous or not. @param Timeout[in] The time to wait for receiving the amount of data the Packet can hold. @retval EFI_SUCCESS The required amount of data is received from the socket. @retval EFI_OUT_OF_RESOURCES Failed to allocate momery. @retval EFI_TIMEOUT Failed to receive the required amount of data in the specified time period. **/ EFI_STATUS Tcp4IoReceive ( IN TCP4_IO *Tcp4Io, IN NET_BUF *Packet, IN BOOLEAN AsyncMode, IN EFI_EVENT Timeout ) { EFI_TCP4_PROTOCOL *Tcp4; EFI_TCP4_RECEIVE_DATA RxData; EFI_STATUS Status; NET_FRAGMENT *Fragment; UINT32 FragmentCount; UINT32 CurrentFragment; FragmentCount = Packet->BlockOpNum; Fragment = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT)); if (Fragment == NULL) { return EFI_OUT_OF_RESOURCES; } // // Build the fragment table. // NetbufBuildExt (Packet, Fragment, &FragmentCount); RxData.FragmentCount = 1; Tcp4Io->RxToken.Packet.RxData = &RxData; CurrentFragment = 0; Tcp4 = Tcp4Io->Tcp4; Status = EFI_SUCCESS; while (CurrentFragment < FragmentCount) { RxData.DataLength = Fragment[CurrentFragment].Len; RxData.FragmentTable[0].FragmentLength = Fragment[CurrentFragment].Len; RxData.FragmentTable[0].FragmentBuffer = Fragment[CurrentFragment].Bulk; Status = Tcp4->Receive (Tcp4, &Tcp4Io->RxToken); if (EFI_ERROR (Status)) { goto ON_EXIT; } while (!Tcp4Io->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { // // Poll until some data is received or something error happens. // Tcp4->Poll (Tcp4); } if (!Tcp4Io->IsRxDone) { // // Timeout occurs, cancel the receive request. // Tcp4->Cancel (Tcp4, &Tcp4Io->RxToken.CompletionToken); Status = EFI_TIMEOUT; goto ON_EXIT; } else { Tcp4Io->IsRxDone = FALSE; } if (EFI_ERROR (Tcp4Io->RxToken.CompletionToken.Status)) { Status = Tcp4Io->RxToken.CompletionToken.Status; goto ON_EXIT; } Fragment[CurrentFragment].Len -= RxData.FragmentTable[0].FragmentLength; if (Fragment[CurrentFragment].Len == 0) { CurrentFragment++; } else { Fragment[CurrentFragment].Bulk += RxData.FragmentTable[0].FragmentLength; } } ON_EXIT: gBS->FreePool (Fragment); return Status; }