Home | History | Annotate | Download | only in DxeTcpIoLib
      1 /** @file
      2   This library is used to share code between UEFI network stack modules.
      3   It provides the helper routines to access TCP service.
      4 
      5 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions of the BSD License
      8 which accompanies this distribution.  The full text of the license may be found at<BR>
      9 http://opensource.org/licenses/bsd-license.php
     10 
     11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include <Uefi.h>
     17 
     18 #include <Library/TcpIoLib.h>
     19 #include <Library/BaseLib.h>
     20 #include <Library/DebugLib.h>
     21 #include <Library/UefiBootServicesTableLib.h>
     22 #include <Library/MemoryAllocationLib.h>
     23 #include <Library/BaseMemoryLib.h>
     24 
     25 /**
     26   The common notify function associated with various TcpIo events.
     27 
     28   @param[in]  Event   The event signaled.
     29   @param[in]  Context The context.
     30 
     31 **/
     32 VOID
     33 EFIAPI
     34 TcpIoCommonNotify (
     35   IN EFI_EVENT  Event,
     36   IN VOID       *Context
     37   )
     38 {
     39   if ((Event == NULL) || (Context == NULL)) {
     40     return ;
     41   }
     42 
     43   *((BOOLEAN *) Context) = TRUE;
     44 }
     45 
     46 /**
     47   The internal function for delay configuring TCP6 when IP6 driver is still in DAD.
     48 
     49   @param[in]  Tcp6               The EFI_TCP6_PROTOCOL protocol instance.
     50   @param[in]  Tcp6ConfigData     The Tcp6 configuration data.
     51 
     52   @retval EFI_SUCCESS            The operational settings successfully
     53                                  completed.
     54   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
     55   @retval Others                 Failed to finish the operation.
     56 
     57 **/
     58 EFI_STATUS
     59 TcpIoGetMapping (
     60   IN EFI_TCP6_PROTOCOL    *Tcp6,
     61   IN EFI_TCP6_CONFIG_DATA *Tcp6ConfigData
     62   )
     63 {
     64   EFI_STATUS              Status;
     65   EFI_EVENT               Event;
     66 
     67   if ((Tcp6 == NULL) || (Tcp6ConfigData == NULL)) {
     68     return EFI_INVALID_PARAMETER;
     69   }
     70 
     71   Event  = NULL;
     72   Status = gBS->CreateEvent (
     73                   EVT_TIMER,
     74                   TPL_CALLBACK,
     75                   NULL,
     76                   NULL,
     77                   &Event
     78                   );
     79   if (EFI_ERROR (Status)) {
     80     goto ON_EXIT;
     81   }
     82 
     83   Status = gBS->SetTimer (
     84                   Event,
     85                   TimerRelative,
     86                   TCP_GET_MAPPING_TIMEOUT
     87                   );
     88 
     89   if (EFI_ERROR (Status)) {
     90     goto ON_EXIT;
     91   }
     92 
     93   while (EFI_ERROR (gBS->CheckEvent (Event))) {
     94 
     95     Tcp6->Poll (Tcp6);
     96 
     97     Status = Tcp6->Configure (Tcp6, Tcp6ConfigData);
     98 
     99     if (!EFI_ERROR (Status)) {
    100       break;
    101     }
    102   }
    103 
    104 ON_EXIT:
    105 
    106   if (Event != NULL) {
    107     gBS->CloseEvent (Event);
    108   }
    109 
    110   return Status;
    111 }
    112 
    113 /**
    114   Create a TCP socket with the specified configuration data.
    115 
    116   @param[in]  Image      The handle of the driver image.
    117   @param[in]  Controller The handle of the controller.
    118   @param[in]  TcpVersion The version of Tcp, TCP_VERSION_4 or TCP_VERSION_6.
    119   @param[in]  ConfigData The Tcp configuration data.
    120   @param[out] TcpIo      The TcpIo.
    121 
    122   @retval EFI_SUCCESS            The TCP socket is created and configured.
    123   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
    124   @retval EFI_UNSUPPORTED        One or more of the control options are not
    125                                  supported in the implementation.
    126   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
    127   @retval Others                 Failed to create the TCP socket or configure it.
    128 
    129 **/
    130 EFI_STATUS
    131 EFIAPI
    132 TcpIoCreateSocket (
    133   IN EFI_HANDLE             Image,
    134   IN EFI_HANDLE             Controller,
    135   IN UINT8                  TcpVersion,
    136   IN TCP_IO_CONFIG_DATA     *ConfigData,
    137   OUT TCP_IO                *TcpIo
    138   )
    139 {
    140   EFI_STATUS                Status;
    141   EFI_EVENT                 Event;
    142   EFI_GUID                  *ServiceBindingGuid;
    143   EFI_GUID                  *ProtocolGuid;
    144   VOID                      **Interface;
    145   EFI_TCP4_OPTION           ControlOption;
    146   EFI_TCP4_CONFIG_DATA      Tcp4ConfigData;
    147   EFI_TCP4_ACCESS_POINT     *AccessPoint4;
    148   EFI_TCP4_PROTOCOL         *Tcp4;
    149   EFI_TCP6_CONFIG_DATA      Tcp6ConfigData;
    150   EFI_TCP6_ACCESS_POINT     *AccessPoint6;
    151   EFI_TCP6_PROTOCOL         *Tcp6;
    152   EFI_TCP4_RECEIVE_DATA     *RxData;
    153 
    154   if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (TcpIo == NULL)) {
    155     return EFI_INVALID_PARAMETER;
    156   }
    157 
    158   Tcp4 = NULL;
    159   Tcp6 = NULL;
    160 
    161   ZeroMem (TcpIo, sizeof (TCP_IO));
    162 
    163   if (TcpVersion == TCP_VERSION_4) {
    164     ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
    165     ProtocolGuid       = &gEfiTcp4ProtocolGuid;
    166     Interface          = (VOID **) (&TcpIo->Tcp.Tcp4);
    167   } else if (TcpVersion == TCP_VERSION_6) {
    168     ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
    169     ProtocolGuid       = &gEfiTcp6ProtocolGuid;
    170     Interface          = (VOID **) (&TcpIo->Tcp.Tcp6);
    171   } else {
    172     return EFI_UNSUPPORTED;
    173   }
    174 
    175   TcpIo->TcpVersion = TcpVersion;
    176 
    177   //
    178   // Create the TCP child instance and get the TCP protocol.
    179   //
    180   Status = NetLibCreateServiceChild (
    181              Controller,
    182              Image,
    183              ServiceBindingGuid,
    184              &TcpIo->Handle
    185              );
    186   if (EFI_ERROR (Status)) {
    187     return Status;
    188   }
    189 
    190   Status = gBS->OpenProtocol (
    191                   TcpIo->Handle,
    192                   ProtocolGuid,
    193                   Interface,
    194                   Image,
    195                   Controller,
    196                   EFI_OPEN_PROTOCOL_BY_DRIVER
    197                   );
    198   if (EFI_ERROR (Status) || (*Interface == NULL)) {
    199     goto ON_ERROR;
    200   }
    201 
    202   if (TcpVersion == TCP_VERSION_4) {
    203     Tcp4             = TcpIo->Tcp.Tcp4;
    204   } else {
    205     Tcp6             = TcpIo->Tcp.Tcp6;
    206   }
    207 
    208   TcpIo->Image       = Image;
    209   TcpIo->Controller  = Controller;
    210 
    211   //
    212   // Set the configuration parameters.
    213   //
    214   ControlOption.ReceiveBufferSize       = 0x200000;
    215   ControlOption.SendBufferSize          = 0x200000;
    216   ControlOption.MaxSynBackLog           = 0;
    217   ControlOption.ConnectionTimeout       = 0;
    218   ControlOption.DataRetries             = 6;
    219   ControlOption.FinTimeout              = 0;
    220   ControlOption.TimeWaitTimeout         = 0;
    221   ControlOption.KeepAliveProbes         = 4;
    222   ControlOption.KeepAliveTime           = 0;
    223   ControlOption.KeepAliveInterval       = 0;
    224   ControlOption.EnableNagle             = FALSE;
    225   ControlOption.EnableTimeStamp         = FALSE;
    226   ControlOption.EnableWindowScaling     = TRUE;
    227   ControlOption.EnableSelectiveAck      = FALSE;
    228   ControlOption.EnablePathMtuDiscovery  = FALSE;
    229 
    230   if (TcpVersion == TCP_VERSION_4) {
    231     Tcp4ConfigData.TypeOfService        = 8;
    232     Tcp4ConfigData.TimeToLive           = 255;
    233     Tcp4ConfigData.ControlOption        = &ControlOption;
    234 
    235     AccessPoint4                        = &Tcp4ConfigData.AccessPoint;
    236 
    237     ZeroMem (AccessPoint4, sizeof (EFI_TCP4_ACCESS_POINT));
    238     AccessPoint4->StationPort           = ConfigData->Tcp4IoConfigData.StationPort;
    239     AccessPoint4->RemotePort            = ConfigData->Tcp4IoConfigData.RemotePort;
    240     AccessPoint4->ActiveFlag            = ConfigData->Tcp4IoConfigData.ActiveFlag;
    241 
    242     CopyMem (
    243       &AccessPoint4->StationAddress,
    244       &ConfigData->Tcp4IoConfigData.LocalIp,
    245       sizeof (EFI_IPv4_ADDRESS)
    246       );
    247     CopyMem (
    248       &AccessPoint4->SubnetMask,
    249       &ConfigData->Tcp4IoConfigData.SubnetMask,
    250       sizeof (EFI_IPv4_ADDRESS)
    251       );
    252     CopyMem (
    253       &AccessPoint4->RemoteAddress,
    254       &ConfigData->Tcp4IoConfigData.RemoteIp,
    255       sizeof (EFI_IPv4_ADDRESS)
    256       );
    257 
    258     ASSERT (Tcp4 != NULL);
    259 
    260     //
    261     // Configure the TCP4 protocol.
    262     //
    263     Status = Tcp4->Configure (Tcp4, &Tcp4ConfigData);
    264     if (EFI_ERROR (Status)) {
    265       goto ON_ERROR;
    266     }
    267 
    268     if (!EFI_IP4_EQUAL (&ConfigData->Tcp4IoConfigData.Gateway, &mZeroIp4Addr)) {
    269       //
    270       // The gateway is not zero. Add the default route manually.
    271       //
    272       Status = Tcp4->Routes (
    273                        Tcp4,
    274                        FALSE,
    275                        &mZeroIp4Addr,
    276                        &mZeroIp4Addr,
    277                        &ConfigData->Tcp4IoConfigData.Gateway
    278                        );
    279       if (EFI_ERROR (Status)) {
    280         goto ON_ERROR;
    281       }
    282     }
    283   } else {
    284     Tcp6ConfigData.TrafficClass         = 0;
    285     Tcp6ConfigData.HopLimit             = 255;
    286     Tcp6ConfigData.ControlOption        = (EFI_TCP6_OPTION *) &ControlOption;
    287 
    288     AccessPoint6                        = &Tcp6ConfigData.AccessPoint;
    289 
    290     ZeroMem (AccessPoint6, sizeof (EFI_TCP6_ACCESS_POINT));
    291     AccessPoint6->StationPort           = ConfigData->Tcp6IoConfigData.StationPort;
    292     AccessPoint6->RemotePort            = ConfigData->Tcp6IoConfigData.RemotePort;
    293     AccessPoint6->ActiveFlag            = ConfigData->Tcp6IoConfigData.ActiveFlag;
    294 
    295     IP6_COPY_ADDRESS (&AccessPoint6->RemoteAddress, &ConfigData->Tcp6IoConfigData.RemoteIp);
    296 
    297 
    298     ASSERT (Tcp6 != NULL);
    299     //
    300     // Configure the TCP6 protocol.
    301     //
    302     Status = Tcp6->Configure (Tcp6, &Tcp6ConfigData);
    303     if (Status == EFI_NO_MAPPING) {
    304       Status = TcpIoGetMapping (Tcp6, &Tcp6ConfigData);
    305     }
    306 
    307     if (EFI_ERROR (Status)) {
    308       goto ON_ERROR;
    309     }
    310   }
    311 
    312   //
    313   // Create events for variuos asynchronous operations.
    314   //
    315   Status = gBS->CreateEvent (
    316                   EVT_NOTIFY_SIGNAL,
    317                   TPL_NOTIFY,
    318                   TcpIoCommonNotify,
    319                   &TcpIo->IsConnDone,
    320                   &Event
    321                   );
    322   if (EFI_ERROR (Status)) {
    323     goto ON_ERROR;
    324   }
    325 
    326   TcpIo->ConnToken.Tcp4Token.CompletionToken.Event = Event;
    327 
    328   Status = gBS->CreateEvent (
    329                   EVT_NOTIFY_SIGNAL,
    330                   TPL_NOTIFY,
    331                   TcpIoCommonNotify,
    332                   &TcpIo->IsListenDone,
    333                   &Event
    334                   );
    335   if (EFI_ERROR (Status)) {
    336     goto ON_ERROR;
    337   }
    338 
    339   TcpIo->ListenToken.Tcp4Token.CompletionToken.Event = Event;
    340 
    341   Status = gBS->CreateEvent (
    342                   EVT_NOTIFY_SIGNAL,
    343                   TPL_NOTIFY,
    344                   TcpIoCommonNotify,
    345                   &TcpIo->IsTxDone,
    346                   &Event
    347                   );
    348   if (EFI_ERROR (Status)) {
    349     goto ON_ERROR;
    350   }
    351 
    352   TcpIo->TxToken.Tcp4Token.CompletionToken.Event = Event;
    353 
    354 
    355   Status = gBS->CreateEvent (
    356                   EVT_NOTIFY_SIGNAL,
    357                   TPL_NOTIFY,
    358                   TcpIoCommonNotify,
    359                   &TcpIo->IsRxDone,
    360                   &Event
    361                   );
    362   if (EFI_ERROR (Status)) {
    363     goto ON_ERROR;
    364   }
    365 
    366   TcpIo->RxToken.Tcp4Token.CompletionToken.Event = Event;
    367 
    368   RxData = (EFI_TCP4_RECEIVE_DATA *) AllocateZeroPool (sizeof (EFI_TCP4_RECEIVE_DATA));
    369   if (RxData == NULL) {
    370     Status = EFI_OUT_OF_RESOURCES;
    371     goto ON_ERROR;
    372   }
    373 
    374   TcpIo->RxToken.Tcp4Token.Packet.RxData = RxData;
    375 
    376   Status = gBS->CreateEvent (
    377                   EVT_NOTIFY_SIGNAL,
    378                   TPL_NOTIFY,
    379                   TcpIoCommonNotify,
    380                   &TcpIo->IsCloseDone,
    381                   &Event
    382                   );
    383   if (EFI_ERROR (Status)) {
    384     goto ON_ERROR;
    385   }
    386 
    387   TcpIo->CloseToken.Tcp4Token.CompletionToken.Event = Event;
    388 
    389 
    390   return EFI_SUCCESS;
    391 
    392 ON_ERROR:
    393 
    394   TcpIoDestroySocket (TcpIo);
    395 
    396   return Status;
    397 }
    398 
    399 /**
    400   Destroy the socket.
    401 
    402   @param[in]  TcpIo The TcpIo which wraps the socket to be destroyed.
    403 
    404 **/
    405 VOID
    406 EFIAPI
    407 TcpIoDestroySocket (
    408   IN TCP_IO                 *TcpIo
    409   )
    410 {
    411   EFI_EVENT                 Event;
    412   EFI_TCP4_PROTOCOL         *Tcp4;
    413   EFI_TCP6_PROTOCOL         *Tcp6;
    414   UINT8                     TcpVersion;
    415   EFI_GUID                  *ServiceBindingGuid;
    416   EFI_GUID                  *ProtocolGuid;
    417   EFI_HANDLE                ChildHandle;
    418 
    419   if (TcpIo == NULL) {
    420     return ;
    421   }
    422 
    423   TcpVersion = TcpIo->TcpVersion;
    424 
    425   if ((TcpVersion != TCP_VERSION_4) && (TcpVersion != TCP_VERSION_6)) {
    426     return ;
    427   }
    428 
    429   Event = TcpIo->ConnToken.Tcp4Token.CompletionToken.Event;
    430 
    431   if (Event != NULL) {
    432     gBS->CloseEvent (Event);
    433   }
    434 
    435   Event = TcpIo->ListenToken.Tcp4Token.CompletionToken.Event;
    436 
    437   if (Event != NULL) {
    438     gBS->CloseEvent (Event);
    439   }
    440 
    441   Event = TcpIo->TxToken.Tcp4Token.CompletionToken.Event;
    442 
    443   if (Event != NULL) {
    444     gBS->CloseEvent (Event);
    445   }
    446 
    447   Event = TcpIo->RxToken.Tcp4Token.CompletionToken.Event;
    448 
    449   if (Event != NULL) {
    450     gBS->CloseEvent (Event);
    451   }
    452 
    453   Event = TcpIo->CloseToken.Tcp4Token.CompletionToken.Event;
    454 
    455   if (Event != NULL) {
    456     gBS->CloseEvent (Event);
    457   }
    458 
    459   if (TcpIo->RxToken.Tcp4Token.Packet.RxData != NULL) {
    460     FreePool (TcpIo->RxToken.Tcp4Token.Packet.RxData);
    461   }
    462 
    463   Tcp4 = NULL;
    464   Tcp6 = NULL;
    465 
    466 
    467   if (TcpVersion == TCP_VERSION_4) {
    468     ServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
    469     ProtocolGuid       = &gEfiTcp4ProtocolGuid;
    470     Tcp4 = TcpIo->Tcp.Tcp4;
    471     if (Tcp4 != NULL) {
    472       Tcp4->Configure (Tcp4, NULL);
    473     }
    474   } else {
    475     ServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
    476     ProtocolGuid       = &gEfiTcp6ProtocolGuid;
    477     Tcp6 = TcpIo->Tcp.Tcp6;
    478     if (Tcp6 != NULL) {
    479       Tcp6->Configure (Tcp6, NULL);
    480     }
    481   }
    482 
    483   if ((Tcp4 != NULL) || (Tcp6 != NULL)) {
    484 
    485     gBS->CloseProtocol (
    486            TcpIo->Handle,
    487            ProtocolGuid,
    488            TcpIo->Image,
    489            TcpIo->Controller
    490            );
    491   }
    492 
    493   ChildHandle = NULL;
    494 
    495   if (TcpIo->IsListenDone) {
    496     if (TcpVersion == TCP_VERSION_4) {
    497       Tcp4 = TcpIo->NewTcp.Tcp4;
    498       if (Tcp4 != NULL) {
    499         Tcp4->Configure (Tcp4, NULL);
    500         ChildHandle = TcpIo->ListenToken.Tcp4Token.NewChildHandle;
    501       }
    502     } else {
    503       Tcp6 = TcpIo->NewTcp.Tcp6;
    504       if (Tcp6 != NULL) {
    505         Tcp6->Configure (Tcp6, NULL);
    506         ChildHandle = TcpIo->ListenToken.Tcp6Token.NewChildHandle;
    507       }
    508     }
    509 
    510     if (ChildHandle != NULL) {
    511 
    512       gBS->CloseProtocol (
    513              ChildHandle,
    514              ProtocolGuid,
    515              TcpIo->Image,
    516              TcpIo->Controller
    517              );
    518     }
    519   }
    520 
    521   NetLibDestroyServiceChild (
    522     TcpIo->Controller,
    523     TcpIo->Image,
    524     ServiceBindingGuid,
    525     TcpIo->Handle
    526     );
    527 }
    528 
    529 /**
    530   Connect to the other endpoint of the TCP socket.
    531 
    532   @param[in, out]  TcpIo     The TcpIo wrapping the TCP socket.
    533   @param[in]       Timeout   The time to wait for connection done.
    534 
    535   @retval EFI_SUCCESS            Connect to the other endpoint of the TCP socket
    536                                  successfully.
    537   @retval EFI_TIMEOUT            Failed to connect to the other endpoint of the
    538                                  TCP socket in the specified time period.
    539   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
    540   @retval EFI_UNSUPPORTED        One or more of the control options are not
    541                                  supported in the implementation.
    542   @retval Others                 Other errors as indicated.
    543 
    544 **/
    545 EFI_STATUS
    546 EFIAPI
    547 TcpIoConnect (
    548   IN OUT TCP_IO             *TcpIo,
    549   IN     EFI_EVENT          Timeout
    550   )
    551 {
    552   EFI_TCP4_PROTOCOL         *Tcp4;
    553   EFI_TCP6_PROTOCOL         *Tcp6;
    554   EFI_STATUS                Status;
    555 
    556   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
    557     return EFI_INVALID_PARAMETER;
    558   }
    559 
    560   TcpIo->IsConnDone = FALSE;
    561 
    562   Tcp4 = NULL;
    563   Tcp6 = NULL;
    564 
    565   if (TcpIo->TcpVersion == TCP_VERSION_4) {
    566     Tcp4   = TcpIo->Tcp.Tcp4;
    567     Status = Tcp4->Connect (Tcp4, &TcpIo->ConnToken.Tcp4Token);
    568   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
    569     Tcp6   = TcpIo->Tcp.Tcp6;
    570     Status = Tcp6->Connect (Tcp6, &TcpIo->ConnToken.Tcp6Token);
    571   } else {
    572     return EFI_UNSUPPORTED;
    573   }
    574 
    575   if (EFI_ERROR (Status)) {
    576     return Status;
    577   }
    578 
    579   while (!TcpIo->IsConnDone && EFI_ERROR (gBS->CheckEvent (Timeout))) {
    580     if (TcpIo->TcpVersion == TCP_VERSION_4) {
    581       Tcp4->Poll (Tcp4);
    582     } else {
    583       Tcp6->Poll (Tcp6);
    584     }
    585   }
    586 
    587   if (!TcpIo->IsConnDone) {
    588     Status = EFI_TIMEOUT;
    589   } else {
    590     Status = TcpIo->ConnToken.Tcp4Token.CompletionToken.Status;
    591   }
    592 
    593   return Status;
    594 }
    595 
    596 /**
    597   Accept the incomding request from the other endpoint of the TCP socket.
    598 
    599   @param[in, out]  TcpIo     The TcpIo wrapping the TCP socket.
    600   @param[in]       Timeout   The time to wait for connection done.
    601 
    602 
    603   @retval EFI_SUCCESS            Connect to the other endpoint of the TCP socket
    604                                  successfully.
    605   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
    606   @retval EFI_UNSUPPORTED        One or more of the control options are not
    607                                  supported in the implementation.
    608 
    609   @retval EFI_TIMEOUT            Failed to connect to the other endpoint of the
    610                                  TCP socket in the specified time period.
    611   @retval Others                 Other errors as indicated.
    612 
    613 **/
    614 EFI_STATUS
    615 EFIAPI
    616 TcpIoAccept (
    617   IN OUT TCP_IO             *TcpIo,
    618   IN     EFI_EVENT          Timeout
    619   )
    620 {
    621   EFI_STATUS                Status;
    622   EFI_GUID                  *ProtocolGuid;
    623   EFI_TCP4_PROTOCOL         *Tcp4;
    624   EFI_TCP6_PROTOCOL         *Tcp6;
    625 
    626   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
    627     return EFI_INVALID_PARAMETER;
    628   }
    629 
    630   TcpIo->IsListenDone = FALSE;
    631 
    632   Tcp4 = NULL;
    633   Tcp6 = NULL;
    634 
    635   if (TcpIo->TcpVersion == TCP_VERSION_4) {
    636     Tcp4   = TcpIo->Tcp.Tcp4;
    637     Status = Tcp4->Accept (Tcp4, &TcpIo->ListenToken.Tcp4Token);
    638   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
    639     Tcp6   = TcpIo->Tcp.Tcp6;
    640     Status = Tcp6->Accept (Tcp6, &TcpIo->ListenToken.Tcp6Token);
    641   } else {
    642     return EFI_UNSUPPORTED;
    643   }
    644 
    645   if (EFI_ERROR (Status)) {
    646     return Status;
    647   }
    648 
    649   while (!TcpIo->IsListenDone && EFI_ERROR (gBS->CheckEvent (Timeout))) {
    650     if (TcpIo->TcpVersion == TCP_VERSION_4) {
    651       Tcp4->Poll (Tcp4);
    652     } else {
    653       Tcp6->Poll (Tcp6);
    654     }
    655   }
    656 
    657   if (!TcpIo->IsListenDone) {
    658     Status = EFI_TIMEOUT;
    659   } else {
    660     Status = TcpIo->ListenToken.Tcp4Token.CompletionToken.Status;
    661   }
    662 
    663   //
    664   // The new TCP instance handle created for the established connection is
    665   // in ListenToken.
    666   //
    667   if (!EFI_ERROR (Status)) {
    668     if (TcpIo->TcpVersion == TCP_VERSION_4) {
    669       ProtocolGuid = &gEfiTcp4ProtocolGuid;
    670     } else {
    671       ProtocolGuid = &gEfiTcp6ProtocolGuid;
    672     }
    673 
    674     Status = gBS->OpenProtocol (
    675                     TcpIo->ListenToken.Tcp4Token.NewChildHandle,
    676                     ProtocolGuid,
    677                     (VOID **) (&TcpIo->NewTcp.Tcp4),
    678                     TcpIo->Image,
    679                     TcpIo->Controller,
    680                     EFI_OPEN_PROTOCOL_BY_DRIVER
    681                     );
    682 
    683   }
    684 
    685   return Status;
    686 }
    687 
    688 /**
    689   Reset the socket.
    690 
    691   @param[in, out]  TcpIo The TcpIo wrapping the TCP socket.
    692 
    693 **/
    694 VOID
    695 EFIAPI
    696 TcpIoReset (
    697   IN OUT TCP_IO             *TcpIo
    698   )
    699 {
    700   EFI_TCP4_PROTOCOL         *Tcp4;
    701   EFI_TCP6_PROTOCOL         *Tcp6;
    702   EFI_STATUS                Status;
    703 
    704   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)) {
    705     return ;
    706   }
    707 
    708   TcpIo->IsCloseDone = FALSE;
    709   Tcp4               = NULL;
    710   Tcp6               = NULL;
    711 
    712   if (TcpIo->TcpVersion == TCP_VERSION_4) {
    713     TcpIo->CloseToken.Tcp4Token.AbortOnClose = TRUE;
    714     Tcp4 = TcpIo->Tcp.Tcp4;
    715     Status = Tcp4->Close (Tcp4, &TcpIo->CloseToken.Tcp4Token);
    716   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
    717     TcpIo->CloseToken.Tcp6Token.AbortOnClose = TRUE;
    718     Tcp6 = TcpIo->Tcp.Tcp6;
    719     Status = Tcp6->Close (Tcp6, &TcpIo->CloseToken.Tcp6Token);
    720   } else {
    721     return ;
    722   }
    723 
    724   if (EFI_ERROR (Status)) {
    725     return ;
    726   }
    727 
    728   while (!TcpIo->IsCloseDone) {
    729     if (TcpIo->TcpVersion == TCP_VERSION_4) {
    730       Tcp4->Poll (Tcp4);
    731     } else {
    732       Tcp6->Poll (Tcp6);
    733     }
    734   }
    735 }
    736 
    737 
    738 /**
    739   Transmit the Packet to the other endpoint of the socket.
    740 
    741   @param[in]   TcpIo           The TcpIo wrapping the TCP socket.
    742   @param[in]   Packet          The packet to transmit.
    743 
    744   @retval EFI_SUCCESS            The packet is trasmitted.
    745   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
    746   @retval EFI_UNSUPPORTED        One or more of the control options are not
    747                                  supported in the implementation.
    748   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
    749   @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
    750   @retval Others                 Other errors as indicated.
    751 
    752 **/
    753 EFI_STATUS
    754 EFIAPI
    755 TcpIoTransmit (
    756   IN TCP_IO                 *TcpIo,
    757   IN NET_BUF                *Packet
    758   )
    759 {
    760   EFI_STATUS                Status;
    761   VOID                      *Data;
    762   EFI_TCP4_PROTOCOL         *Tcp4;
    763   EFI_TCP6_PROTOCOL         *Tcp6;
    764   UINTN                     Size;
    765 
    766   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)|| (Packet == NULL)) {
    767     return EFI_INVALID_PARAMETER;
    768   }
    769 
    770   if (TcpIo->TcpVersion == TCP_VERSION_4) {
    771 
    772     Size = sizeof (EFI_TCP4_TRANSMIT_DATA) +
    773            (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA);
    774   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
    775     Size = sizeof (EFI_TCP6_TRANSMIT_DATA) +
    776            (Packet->BlockOpNum - 1) * sizeof (EFI_TCP6_FRAGMENT_DATA);
    777   } else {
    778     return EFI_UNSUPPORTED;
    779   }
    780 
    781   Data = AllocatePool (Size);
    782   if (Data == NULL) {
    783     return EFI_OUT_OF_RESOURCES;
    784   }
    785 
    786   ((EFI_TCP4_TRANSMIT_DATA *) Data)->Push        = TRUE;
    787   ((EFI_TCP4_TRANSMIT_DATA *) Data)->Urgent      = FALSE;
    788   ((EFI_TCP4_TRANSMIT_DATA *) Data)->DataLength  = Packet->TotalSize;
    789 
    790   //
    791   // Build the fragment table.
    792   //
    793   ((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount = Packet->BlockOpNum;
    794 
    795   NetbufBuildExt (
    796     Packet,
    797     (NET_FRAGMENT *) &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentTable[0],
    798     &((EFI_TCP4_TRANSMIT_DATA *) Data)->FragmentCount
    799     );
    800 
    801   Tcp4   = NULL;
    802   Tcp6   = NULL;
    803   Status = EFI_DEVICE_ERROR;
    804 
    805   //
    806   // Trasnmit the packet.
    807   //
    808   if (TcpIo->TcpVersion == TCP_VERSION_4) {
    809     TcpIo->TxToken.Tcp4Token.Packet.TxData = (EFI_TCP4_TRANSMIT_DATA *) Data;
    810     Tcp4    = TcpIo->Tcp.Tcp4;
    811     if (TcpIo->IsListenDone) {
    812       Tcp4 = TcpIo->NewTcp.Tcp4;
    813     }
    814 
    815     if (Tcp4 == NULL) {
    816       goto ON_EXIT;
    817     }
    818 
    819     Status  = Tcp4->Transmit (Tcp4, &TcpIo->TxToken.Tcp4Token);
    820   } else {
    821     TcpIo->TxToken.Tcp6Token.Packet.TxData = (EFI_TCP6_TRANSMIT_DATA *) Data;
    822     Tcp6    = TcpIo->Tcp.Tcp6;
    823     if (TcpIo->IsListenDone) {
    824       Tcp6 = TcpIo->NewTcp.Tcp6;
    825     }
    826 
    827     if (Tcp6 == NULL) {
    828       goto ON_EXIT;
    829     }
    830 
    831     Status  = Tcp6->Transmit (Tcp6, &TcpIo->TxToken.Tcp6Token);
    832   }
    833 
    834   if (EFI_ERROR (Status)) {
    835     goto ON_EXIT;
    836   }
    837 
    838   while (!TcpIo->IsTxDone) {
    839     if (TcpIo->TcpVersion == TCP_VERSION_4) {
    840       Tcp4->Poll (Tcp4);
    841     } else {
    842       Tcp6->Poll (Tcp6);
    843     }
    844   }
    845 
    846   TcpIo->IsTxDone  = FALSE;
    847   Status           = TcpIo->TxToken.Tcp4Token.CompletionToken.Status;
    848 
    849 ON_EXIT:
    850 
    851   FreePool (Data);
    852 
    853   return Status;
    854 }
    855 
    856 /**
    857   Receive data from the socket.
    858 
    859   @param[in, out]  TcpIo       The TcpIo which wraps the socket to be destroyed.
    860   @param[in]       Packet      The buffer to hold the data copy from the socket rx buffer.
    861   @param[in]       AsyncMode   Is this receive asyncronous or not.
    862   @param[in]       Timeout     The time to wait for receiving the amount of data the Packet
    863                                can hold.
    864 
    865   @retval EFI_SUCCESS            The required amount of data is received from the socket.
    866   @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
    867   @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
    868   @retval EFI_OUT_OF_RESOURCES   Failed to allocate momery.
    869   @retval EFI_TIMEOUT            Failed to receive the required amount of data in the
    870                                  specified time period.
    871   @retval Others                 Other errors as indicated.
    872 
    873 **/
    874 EFI_STATUS
    875 EFIAPI
    876 TcpIoReceive (
    877   IN OUT TCP_IO             *TcpIo,
    878   IN     NET_BUF            *Packet,
    879   IN     BOOLEAN            AsyncMode,
    880   IN     EFI_EVENT          Timeout
    881   )
    882 {
    883   EFI_TCP4_PROTOCOL         *Tcp4;
    884   EFI_TCP6_PROTOCOL         *Tcp6;
    885   EFI_TCP4_RECEIVE_DATA     *RxData;
    886   EFI_STATUS                Status;
    887   NET_FRAGMENT              *Fragment;
    888   UINT32                    FragmentCount;
    889   UINT32                    CurrentFragment;
    890 
    891   if ((TcpIo == NULL) || (TcpIo->Tcp.Tcp4 == NULL)|| (Packet == NULL)) {
    892     return EFI_INVALID_PARAMETER;
    893   }
    894 
    895   RxData = TcpIo->RxToken.Tcp4Token.Packet.RxData;
    896   if (RxData == NULL) {
    897     return EFI_INVALID_PARAMETER;
    898   }
    899 
    900   Tcp4 = NULL;
    901   Tcp6 = NULL;
    902 
    903   if (TcpIo->TcpVersion == TCP_VERSION_4) {
    904     Tcp4 = TcpIo->Tcp.Tcp4;
    905 
    906     if (TcpIo->IsListenDone) {
    907       Tcp4 = TcpIo->NewTcp.Tcp4;
    908     }
    909 
    910     if (Tcp4 == NULL) {
    911       return EFI_DEVICE_ERROR;
    912     }
    913 
    914   } else if (TcpIo->TcpVersion == TCP_VERSION_6) {
    915     Tcp6 = TcpIo->Tcp.Tcp6;
    916 
    917     if (TcpIo->IsListenDone) {
    918       Tcp6 = TcpIo->NewTcp.Tcp6;
    919     }
    920 
    921     if (Tcp6 == NULL) {
    922       return EFI_DEVICE_ERROR;
    923     }
    924 
    925   } else {
    926     return EFI_UNSUPPORTED;
    927   }
    928 
    929   FragmentCount = Packet->BlockOpNum;
    930   Fragment      = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT));
    931   if (Fragment == NULL) {
    932     Status = EFI_OUT_OF_RESOURCES;
    933     goto ON_EXIT;
    934   }
    935   //
    936   // Build the fragment table.
    937   //
    938   NetbufBuildExt (Packet, Fragment, &FragmentCount);
    939 
    940   RxData->FragmentCount         = 1;
    941   CurrentFragment               = 0;
    942   Status                        = EFI_SUCCESS;
    943 
    944   while (CurrentFragment < FragmentCount) {
    945     RxData->DataLength                       = Fragment[CurrentFragment].Len;
    946     RxData->FragmentTable[0].FragmentLength  = Fragment[CurrentFragment].Len;
    947     RxData->FragmentTable[0].FragmentBuffer  = Fragment[CurrentFragment].Bulk;
    948 
    949     if (TcpIo->TcpVersion == TCP_VERSION_4) {
    950       Status = Tcp4->Receive (Tcp4, &TcpIo->RxToken.Tcp4Token);
    951     } else {
    952       Status = Tcp6->Receive (Tcp6, &TcpIo->RxToken.Tcp6Token);
    953     }
    954 
    955     if (EFI_ERROR (Status)) {
    956       goto ON_EXIT;
    957     }
    958 
    959     while (!TcpIo->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
    960       //
    961       // Poll until some data is received or an error occurs.
    962       //
    963       if (TcpIo->TcpVersion == TCP_VERSION_4) {
    964         Tcp4->Poll (Tcp4);
    965       } else {
    966         Tcp6->Poll (Tcp6);
    967       }
    968     }
    969 
    970     if (!TcpIo->IsRxDone) {
    971       //
    972       // Timeout occurs, cancel the receive request.
    973       //
    974       if (TcpIo->TcpVersion == TCP_VERSION_4) {
    975         Tcp4->Cancel (Tcp4, &TcpIo->RxToken.Tcp4Token.CompletionToken);
    976       } else {
    977         Tcp6->Cancel (Tcp6, &TcpIo->RxToken.Tcp6Token.CompletionToken);
    978       }
    979 
    980       Status = EFI_TIMEOUT;
    981       goto ON_EXIT;
    982     } else {
    983       TcpIo->IsRxDone = FALSE;
    984     }
    985 
    986     Status = TcpIo->RxToken.Tcp4Token.CompletionToken.Status;
    987 
    988     if (EFI_ERROR (Status)) {
    989       goto ON_EXIT;
    990     }
    991 
    992     Fragment[CurrentFragment].Len -= RxData->FragmentTable[0].FragmentLength;
    993     if (Fragment[CurrentFragment].Len == 0) {
    994       CurrentFragment++;
    995     } else {
    996       Fragment[CurrentFragment].Bulk += RxData->FragmentTable[0].FragmentLength;
    997     }
    998   }
    999 
   1000 ON_EXIT:
   1001 
   1002   if (Fragment != NULL) {
   1003     FreePool (Fragment);
   1004   }
   1005 
   1006   return Status;
   1007 }
   1008