]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/Mtftp6Dxe/Mtftp6Support.c
Add NetworkPkg (P.UDK2010.UP3.Network.P1)
[mirror_edk2.git] / NetworkPkg / Mtftp6Dxe / Mtftp6Support.c
diff --git a/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c b/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c
new file mode 100644 (file)
index 0000000..24ce0e8
--- /dev/null
@@ -0,0 +1,1189 @@
+/** @file\r
+  Mtftp6 support functions implementation.\r
+\r
+  Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+\r
+  This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php.\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Mtftp6Impl.h"\r
+\r
+\r
+/**\r
+  Allocate a MTFTP block range, then init it to the range of [Start, End].\r
+\r
+  @param[in]  Start                  The start block number.\r
+  @param[in]  End                    The last block number in the range.\r
+\r
+  @return Range                      The range of the allocated block buffer.\r
+\r
+**/\r
+MTFTP6_BLOCK_RANGE *\r
+Mtftp6AllocateRange (\r
+  IN UINT16                 Start,\r
+  IN UINT16                 End\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE        *Range;\r
+\r
+  Range = AllocateZeroPool (sizeof (MTFTP6_BLOCK_RANGE));\r
+\r
+  if (Range == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  InitializeListHead (&Range->Link);\r
+  Range->Start  = Start;\r
+  Range->End    = End;\r
+  Range->Bound  = End;\r
+\r
+  return Range;\r
+}\r
+\r
+\r
+/**\r
+  Initialize the block range for either RRQ or WRQ. RRQ and WRQ have\r
+  different requirements for Start and End. For example, during startup,\r
+  WRQ initializes its whole valid block range to [0, 0xffff]. This\r
+  is bacause the server will send an ACK0 to inform the user to start the\r
+  upload. When the client receives an ACK0, it will remove 0 from the range,\r
+  get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
+  without option negotiation, the server will directly send the BLOCK1\r
+  in response to the client's RRQ. When received BLOCK1, the client will\r
+  remove it from the block range and send an ACK. It also works if there\r
+  is option negotiation.\r
+\r
+  @param[in]  Head                   The block range head to initialize.\r
+  @param[in]  Start                  The Start block number.\r
+  @param[in]  End                    The last block number.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for initial block range.\r
+  @retval EFI_SUCCESS            The initial block range is created.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6InitBlockRange (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Start,\r
+  IN UINT16                 End\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE        *Range;\r
+\r
+  Range = Mtftp6AllocateRange (Start, End);\r
+\r
+  if (Range == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  InsertTailList (Head, &Range->Link);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Get the first valid block number on the range list.\r
+\r
+  @param[in]  Head                   The block range head.\r
+\r
+  @retval     ==-1                   If the block range is empty.\r
+  @retval     >-1                    The first valid block number.\r
+\r
+**/\r
+INTN\r
+Mtftp6GetNextBlockNum (\r
+  IN LIST_ENTRY              *Head\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE  *Range;\r
+\r
+  if (IsListEmpty (Head)) {\r
+    return -1;\r
+  }\r
+\r
+  Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link);\r
+  return Range->Start;\r
+}\r
+\r
+\r
+/**\r
+  Set the last block number of the block range list. It\r
+  removes all the blocks after the Last. MTFTP initialize the\r
+  block range to the maximum possible range, such as [0, 0xffff]\r
+  for WRQ. When it gets the last block number, it calls\r
+  this function to set the last block number.\r
+\r
+  @param[in]  Head                   The block range list.\r
+  @param[in]  Last                   The last block number.\r
+\r
+**/\r
+VOID\r
+Mtftp6SetLastBlockNum (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Last\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE        *Range;\r
+\r
+  //\r
+  // Iterate from the tail to head to remove the block number\r
+  // after the last.\r
+  //\r
+  while (!IsListEmpty (Head)) {\r
+    Range = NET_LIST_TAIL (Head, MTFTP6_BLOCK_RANGE, Link);\r
+\r
+    if (Range->Start > Last) {\r
+      RemoveEntryList (&Range->Link);\r
+      FreePool (Range);\r
+      continue;\r
+    }\r
+\r
+    if (Range->End > Last) {\r
+      Range->End = Last;\r
+    }\r
+    return ;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Remove the block number from the block range list.\r
+\r
+  @param[in]  Head                   The block range list to remove from.\r
+  @param[in]  Num                    The block number to remove.\r
+  @param[in]  Completed              Whether Num is the last block number\r
+  @param[out] TotalBlock             The continuous block number in all\r
+\r
+  @retval EFI_NOT_FOUND          The block number isn't in the block range list.\r
+  @retval EFI_SUCCESS            The block number has been removed from the list.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6RemoveBlockNum (\r
+  IN LIST_ENTRY             *Head,\r
+  IN UINT16                 Num,\r
+  IN BOOLEAN                Completed,\r
+  OUT UINT64                *TotalBlock\r
+  )\r
+{\r
+  MTFTP6_BLOCK_RANGE        *Range;\r
+  MTFTP6_BLOCK_RANGE        *NewRange;\r
+  LIST_ENTRY                *Entry;\r
+\r
+  NET_LIST_FOR_EACH (Entry, Head) {\r
+\r
+    //\r
+    // Each block represents a hole [Start, End] in the file,\r
+    // skip to the first range with End >= Num\r
+    //\r
+    Range = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);\r
+\r
+    if (Range->End < Num) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // There are three different cases for Start\r
+    // 1. (Start > Num) && (End >= Num):\r
+    //    because all the holes before this one has the condition of\r
+    //    End < Num, so this block number has been removed.\r
+    //\r
+    // 2. (Start == Num) && (End >= Num):\r
+    //    Need to increase the Start by one, and if End == Num, this\r
+    //    hole has been removed completely, remove it.\r
+    //\r
+    // 3. (Start < Num) && (End >= Num):\r
+    //    if End == Num, only need to decrease the End by one because\r
+    //    we have (Start < Num) && (Num == End), so (Start <= End - 1).\r
+    //    if (End > Num), the hold is splited into two holes, with\r
+    //    [Start, Num - 1] and [Num + 1, End].\r
+    //\r
+    if (Range->Start > Num) {\r
+      return EFI_NOT_FOUND;\r
+\r
+    } else if (Range->Start == Num) {\r
+      Range->Start++;\r
+\r
+      //\r
+      // Note that: RFC 1350 does not mention block counter roll-over,\r
+      // but several TFTP hosts implement the roll-over be able to accept\r
+      // transfers of unlimited size. There is no consensus, however, whether\r
+      // the counter should wrap around to zero or to one. Many implementations\r
+      // wrap to zero, because this is the simplest to implement. Here we choose\r
+      // this solution.\r
+      //\r
+      *TotalBlock  = Num;\r
+\r
+      if (Range->Round > 0) {\r
+        *TotalBlock += Range->Bound +  MultU64x32 ((UINT64) (Range->Round -1), (UINT32)(Range->Bound + 1)) + 1;\r
+      }\r
+\r
+      if (Range->Start > Range->Bound) {\r
+        Range->Start = 0;\r
+        Range->Round ++;\r
+      }\r
+\r
+      if ((Range->Start > Range->End) || Completed) {\r
+        RemoveEntryList (&Range->Link);\r
+        FreePool (Range);\r
+      }\r
+\r
+      return EFI_SUCCESS;\r
+\r
+    } else {\r
+      if (Range->End == Num) {\r
+        Range->End--;\r
+      } else {\r
+        NewRange = Mtftp6AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);\r
+\r
+        if (NewRange == NULL) {\r
+          return EFI_OUT_OF_RESOURCES;\r
+        }\r
+\r
+        Range->End = Num - 1;\r
+        NetListInsertAfter (&Range->Link, &NewRange->Link);\r
+      }\r
+\r
+      return EFI_SUCCESS;\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+\r
+/**\r
+  Configure the opened Udp6 instance until the corresponding Ip6 instance\r
+  has been configured.\r
+\r
+  @param[in]  UdpIo                  The pointer to the Udp6 Io.\r
+  @param[in]  UdpCfgData             The pointer to the Udp6 configure data.\r
+\r
+  @retval EFI_SUCCESS            Configure the Udp6 instance successfully.\r
+  @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not\r
+                                 been configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6GetMapping (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN EFI_UDP6_CONFIG_DATA   *UdpCfgData\r
+  )\r
+{\r
+  EFI_IP6_MODE_DATA         Ip6Mode;\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_STATUS                Status;\r
+  EFI_EVENT                 Event;\r
+\r
+  Event  = NULL;\r
+  Udp6   = UdpIo->Protocol.Udp6;\r
+\r
+  //\r
+  // Create a timer to check whether the Ip6 instance configured or not.\r
+  //\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &Event\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  Event,\r
+                  TimerRelative,\r
+                  MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Check the Ip6 mode data till timeout.\r
+  //\r
+  while (EFI_ERROR (gBS->CheckEvent (Event))) {\r
+\r
+    Udp6->Poll (Udp6);\r
+\r
+    Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL);\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+\r
+      if  (Ip6Mode.IsConfigured) {\r
+        //\r
+        // Continue to configure the Udp6 instance.\r
+        //\r
+        Status = Udp6->Configure (Udp6, UdpCfgData);\r
+      } else {\r
+        Status = EFI_NO_MAPPING;\r
+      }\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (Event != NULL) {\r
+    gBS->CloseEvent (Event);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The dummy configure routine for create a new Udp6 Io.\r
+\r
+  @param[in]  UdpIo                  The pointer to the Udp6 Io.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+  @retval EFI_SUCCESS                This value is always returned.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6ConfigDummyUdpIo (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  The configure routine for Mtftp6 instance to transmit/receive.\r
+\r
+  @param[in]  UdpIo                  The pointer to the Udp6 Io.\r
+  @param[in]  ServerIp               The pointer to the server address.\r
+  @param[in]  ServerPort             The pointer to the server port.\r
+  @param[in]  LocalIp                The pointer to the local address.\r
+  @param[in]  LocalPort              The pointer to the local port.\r
+\r
+  @retval EFI_SUCCESS            Configured the Udp6 Io for Mtftp6 successfully.\r
+  @retval EFI_NO_MAPPING         The corresponding Ip6 instance has not been\r
+                                 configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6ConfigUdpIo (\r
+  IN UDP_IO                 *UdpIo,\r
+  IN EFI_IPv6_ADDRESS       *ServerIp,\r
+  IN UINT16                 ServerPort,\r
+  IN EFI_IPv6_ADDRESS       *LocalIp,\r
+  IN UINT16                 LocalPort\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_UDP6_CONFIG_DATA      *Udp6Cfg;\r
+\r
+  Udp6    = UdpIo->Protocol.Udp6;\r
+  Udp6Cfg = &(UdpIo->Config.Udp6);\r
+\r
+  ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA));\r
+\r
+  //\r
+  // Set the Udp6 Io configure data.\r
+  //\r
+  Udp6Cfg->AcceptPromiscuous  = FALSE;\r
+  Udp6Cfg->AcceptAnyPort      = FALSE;\r
+  Udp6Cfg->AllowDuplicatePort = FALSE;\r
+  Udp6Cfg->TrafficClass       = 0;\r
+  Udp6Cfg->HopLimit           = 128;\r
+  Udp6Cfg->ReceiveTimeout     = 0;\r
+  Udp6Cfg->TransmitTimeout    = 0;\r
+  Udp6Cfg->StationPort        = LocalPort;\r
+  Udp6Cfg->RemotePort         = ServerPort;\r
+\r
+  CopyMem (\r
+    &Udp6Cfg->StationAddress,\r
+    LocalIp,\r
+    sizeof (EFI_IPv6_ADDRESS)\r
+    );\r
+\r
+  CopyMem (\r
+    &Udp6Cfg->RemoteAddress,\r
+    ServerIp,\r
+    sizeof (EFI_IPv6_ADDRESS)\r
+    );\r
+\r
+  //\r
+  // Configure the Udp6 instance with current configure data.\r
+  //\r
+  Status = Udp6->Configure (Udp6, Udp6Cfg);\r
+\r
+  if (Status == EFI_NO_MAPPING) {\r
+\r
+    return Mtftp6GetMapping (UdpIo, Udp6Cfg);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Build and transmit the request packet for the Mtftp6 instance.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Operation              The operation code of this packet.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the request.\r
+  @retval EFI_SUCCESS            The request is built and sent.\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendRequest (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 Operation\r
+  )\r
+{\r
+  EFI_MTFTP6_PACKET         *Packet;\r
+  EFI_MTFTP6_OPTION         *Options;\r
+  EFI_MTFTP6_TOKEN          *Token;\r
+  NET_BUF                   *Nbuf;\r
+  UINT8                     *Mode;\r
+  UINT8                     *Cur;\r
+  UINT32                    Len1;\r
+  UINT32                    Len2;\r
+  UINT32                    Len;\r
+  UINTN                     Index;\r
+\r
+  Token   = Instance->Token;\r
+  Options = Token->OptionList;\r
+  Mode    = Token->ModeStr;\r
+\r
+  if (Mode == NULL) {\r
+    Mode = (UINT8 *) "octet";\r
+  }\r
+\r
+  //\r
+  // The header format of RRQ/WRQ packet is:\r
+  //\r
+  //   2 bytes     string    1 byte     string   1 byte\r
+  //   ------------------------------------------------\r
+  //  | Opcode |  Filename  |   0  |    Mode    |   0  |\r
+  //   ------------------------------------------------\r
+  //\r
+  // The common option format is:\r
+  //\r
+  //    string     1 byte     string   1 byte\r
+  //   ---------------------------------------\r
+  //  | OptionStr |   0  |  ValueStr  |   0   |\r
+  //   ---------------------------------------\r
+  //\r
+\r
+  //\r
+  // Compute the size of new Mtftp6 packet.\r
+  //\r
+  Len1 = (UINT32) AsciiStrLen ((CHAR8 *) Token->Filename);\r
+  Len2 = (UINT32) AsciiStrLen ((CHAR8 *) Mode);\r
+  Len  = Len1 + Len2 + 4;\r
+\r
+  for (Index = 0; Index < Token->OptionCount; Index++) {\r
+    Len1  = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
+    Len2  = (UINT32) AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
+    Len  += Len1 + Len2 + 2;\r
+  }\r
+\r
+  //\r
+  // Allocate a packet then copy the data.\r
+  //\r
+  if ((Nbuf = NetbufAlloc (Len)) == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Copy the opcode, filename and mode into packet.\r
+  //\r
+  Packet         = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);\r
+  ASSERT (Packet != NULL);\r
+\r
+  Packet->OpCode = HTONS (Operation);\r
+  Cur            = Packet->Rrq.Filename;\r
+  Cur            = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Token->Filename);\r
+  Cur           += AsciiStrLen ((CHAR8 *) Token->Filename) + 1;\r
+  Cur            = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Mode);\r
+  Cur           += AsciiStrLen ((CHAR8 *) Mode) + 1;\r
+\r
+  //\r
+  // Copy all the extension options into the packet.\r
+  //\r
+  for (Index = 0; Index < Token->OptionCount; ++Index) {\r
+    Cur  = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].OptionStr);\r
+    Cur += AsciiStrLen ((CHAR8 *) Options[Index].OptionStr) + 1;\r
+    Cur  = (UINT8 *) AsciiStrCpy ((CHAR8 *) Cur, (CHAR8 *) Options[Index].ValueStr);\r
+    Cur += AsciiStrLen ((CHAR8 *) (CHAR8 *) Options[Index].ValueStr) + 1;\r
+  }\r
+\r
+  //\r
+  // Save the packet buf for retransmit\r
+  //\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+  }\r
+\r
+  Instance->LastPacket = Nbuf;\r
+  Instance->CurRetry   = 0;\r
+\r
+  return Mtftp6TransmitPacket (Instance, Nbuf);\r
+}\r
+\r
+\r
+/**\r
+  Build and send an error packet.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  ErrCode                The error code in the packet.\r
+  @param[in]  ErrInfo                The error message in the packet.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory for the error packet.\r
+  @retval EFI_SUCCESS            The error packet is transmitted.\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6SendError (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN UINT16                 ErrCode,\r
+  IN UINT8*                 ErrInfo\r
+  )\r
+{\r
+  NET_BUF                   *Nbuf;\r
+  EFI_MTFTP6_PACKET         *TftpError;\r
+  UINT32                    Len;\r
+\r
+  //\r
+  // Allocate a packet then copy the data.\r
+  //\r
+  Len  = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER));\r
+  Nbuf = NetbufAlloc (Len);\r
+\r
+  if (Nbuf == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE);\r
+\r
+  if (TftpError == NULL) {\r
+    NetbufFree (Nbuf);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  TftpError->OpCode          = HTONS (EFI_MTFTP6_OPCODE_ERROR);\r
+  TftpError->Error.ErrorCode = HTONS (ErrCode);\r
+\r
+  AsciiStrCpy ((CHAR8 *) TftpError->Error.ErrorMessage, (CHAR8 *) ErrInfo);\r
+\r
+  //\r
+  // Save the packet buf for retransmit\r
+  //\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+  }\r
+\r
+  Instance->LastPacket = Nbuf;\r
+  Instance->CurRetry   = 0;\r
+\r
+  return Mtftp6TransmitPacket (Instance, Nbuf);\r
+}\r
+\r
+\r
+/**\r
+  The callback function called when the packet is transmitted.\r
+\r
+  @param[in]  Packet                 The pointer to the packet.\r
+  @param[in]  UdpEpt                 The pointer to the Udp6 access point.\r
+  @param[in]  IoStatus               The result of the transmission.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6OnPacketSent (\r
+  IN NET_BUF                   *Packet,\r
+  IN UDP_END_POINT             *UdpEpt,\r
+  IN EFI_STATUS                IoStatus,\r
+  IN VOID                      *Context\r
+  )\r
+{\r
+  NetbufFree (Packet);\r
+  *(BOOLEAN *) Context = TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Send the packet for the Mtftp6 instance.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Packet                 The pointer to the packet to be sent.\r
+\r
+  @retval EFI_SUCCESS            The packet was sent out\r
+  @retval Others                 Failed to transmit the packet.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6TransmitPacket (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN NET_BUF                *Packet\r
+  )\r
+{\r
+  EFI_UDP6_PROTOCOL         *Udp6;\r
+  EFI_UDP6_CONFIG_DATA      Udp6CfgData;\r
+  EFI_STATUS                Status;\r
+  UINT16                    *Temp;\r
+  UINT16                    Value;\r
+  UINT16                    OpCode;\r
+\r
+  ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA));\r
+  Udp6 = Instance->UdpIo->Protocol.Udp6;\r
+\r
+  //\r
+  // Set the live time of the packet.\r
+  //\r
+  Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2);\r
+\r
+  Temp   = (UINT16 *) NetbufGetByte (Packet, 0, NULL);\r
+  ASSERT (Temp != NULL);\r
+\r
+  Value  = *Temp;\r
+  OpCode = NTOHS (Value);\r
+\r
+  if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) {\r
+    //\r
+    // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as\r
+    // (serverip, 69, localip, localport) to send.\r
+    // Usually local address and local port are both default as zero.\r
+    //\r
+    Status = Udp6->Configure (Udp6, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    Status = Mtftp6ConfigUdpIo (\r
+               Instance->UdpIo,\r
+               &Instance->ServerIp,\r
+               Instance->ServerCmdPort,\r
+               &Instance->Config->StationIp,\r
+               Instance->Config->LocalPort\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Get the current local address and port by get Udp6 mode data.\r
+    //\r
+    Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    NET_GET_REF (Packet);\r
+\r
+    Instance->IsTransmitted = FALSE;\r
+\r
+    Status = UdpIoSendDatagram (\r
+               Instance->UdpIo,\r
+               Packet,\r
+               NULL,\r
+               NULL,\r
+               Mtftp6OnPacketSent,\r
+               &Instance->IsTransmitted\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      NET_PUT_REF (Packet);\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Poll till the packet sent out from the ip6 queue.\r
+    //\r
+    gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+    while (!Instance->IsTransmitted) {\r
+      Udp6->Poll (Udp6);\r
+    }\r
+\r
+    Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+    //\r
+    // For the subsequent exchange of such requests, reconfigure the Udp6Io as\r
+    // (serverip, 0, localip, localport) to receive.\r
+    // Currently local address and local port are specified by Udp6 mode data.\r
+    //\r
+    Status = Udp6->Configure (Udp6, NULL);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    Status = Mtftp6ConfigUdpIo (\r
+               Instance->UdpIo,\r
+               &Instance->ServerIp,\r
+               Instance->ServerDataPort,\r
+               &Udp6CfgData.StationAddress,\r
+               Udp6CfgData.StationPort\r
+               );\r
+  } else {\r
+    //\r
+    // For the data exchange, configure the Udp6Io as (serverip, dataport,\r
+    // localip, localport) to send/receive.\r
+    // Currently local address and local port are specified by Udp6 mode data.\r
+    //\r
+    Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    if (Udp6CfgData.RemotePort != Instance->ServerDataPort) {\r
+\r
+      Status = Udp6->Configure (Udp6, NULL);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+\r
+      Status = Mtftp6ConfigUdpIo (\r
+                 Instance->UdpIo,\r
+                 &Instance->ServerIp,\r
+                 Instance->ServerDataPort,\r
+                 &Udp6CfgData.StationAddress,\r
+                 Udp6CfgData.StationPort\r
+                 );\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        return Status;\r
+      }\r
+    }\r
+\r
+    NET_GET_REF (Packet);\r
+\r
+    Instance->IsTransmitted = FALSE;\r
+\r
+    Status = UdpIoSendDatagram (\r
+               Instance->UdpIo,\r
+               Packet,\r
+               NULL,\r
+               NULL,\r
+               Mtftp6OnPacketSent,\r
+               &Instance->IsTransmitted\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      NET_PUT_REF (Packet);\r
+    }\r
+\r
+    //\r
+    // Poll till the packet sent out from the ip6 queue.\r
+    //\r
+    gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+    while (!Instance->IsTransmitted) {\r
+      Udp6->Poll (Udp6);\r
+    }\r
+\r
+    Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Check packet for GetInfo callback routine.\r
+\r
+  GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect\r
+  the first packet from server, then abort the session.\r
+\r
+  @param[in]  This                   The pointer to the Mtftp6 protocol.\r
+  @param[in]  Token                  The pointer to the Mtftp6 token.\r
+  @param[in]  PacketLen              The length of the packet.\r
+  @param[in]  Packet                 The pointer to the received packet.\r
+\r
+  @retval EFI_ABORTED            Abort the Mtftp6 operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+Mtftp6CheckPacket (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token,\r
+  IN UINT16                 PacketLen,\r
+  IN EFI_MTFTP6_PACKET      *Packet\r
+  )\r
+{\r
+  MTFTP6_GETINFO_CONTEXT    *Context;\r
+  UINT16                    OpCode;\r
+\r
+  Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context;\r
+  OpCode  = NTOHS (Packet->OpCode);\r
+\r
+  //\r
+  // Set the GetInfo's return status according to the OpCode.\r
+  //\r
+  switch (OpCode) {\r
+  case EFI_MTFTP6_OPCODE_ERROR:\r
+    Context->Status = EFI_TFTP_ERROR;\r
+    break;\r
+\r
+  case EFI_MTFTP6_OPCODE_OACK:\r
+    Context->Status = EFI_SUCCESS;\r
+    break;\r
+\r
+  default:\r
+    Context->Status = EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // Allocate buffer then copy the packet over. Use gBS->AllocatePool\r
+  // in case NetAllocatePool will implements something tricky.\r
+  //\r
+  *(Context->Packet) = AllocateZeroPool (PacketLen);\r
+\r
+  if (*(Context->Packet) == NULL) {\r
+    Context->Status = EFI_OUT_OF_RESOURCES;\r
+    return EFI_ABORTED;\r
+  }\r
+\r
+  *(Context->PacketLen) = PacketLen;\r
+  CopyMem (*(Context->Packet), Packet, PacketLen);\r
+\r
+  return EFI_ABORTED;\r
+}\r
+\r
+\r
+/**\r
+  Clean up the current Mtftp6 operation.\r
+\r
+  @param[in]  Instance               The pointer to the Mtftp6 instance.\r
+  @param[in]  Result                 The result to be returned to the user.\r
+\r
+**/\r
+VOID\r
+Mtftp6OperationClean (\r
+  IN MTFTP6_INSTANCE        *Instance,\r
+  IN EFI_STATUS             Result\r
+  )\r
+{\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  MTFTP6_BLOCK_RANGE        *Block;\r
+\r
+  //\r
+  // Clean up the current token and event.\r
+  //\r
+  if (Instance->Token != NULL) {\r
+    Instance->Token->Status = Result;\r
+    if (Instance->Token->Event != NULL) {\r
+      gBS->SignalEvent (Instance->Token->Event);\r
+    }\r
+    Instance->Token = NULL;\r
+  }\r
+\r
+  //\r
+  // Clean up the corresponding Udp6Io.\r
+  //\r
+  if (Instance->UdpIo != NULL) {\r
+    UdpIoCleanIo (Instance->UdpIo);\r
+  }\r
+\r
+  if (Instance->McastUdpIo != NULL) {\r
+    UdpIoFreeIo (Instance->McastUdpIo);\r
+    Instance->McastUdpIo = NULL;\r
+  }\r
+\r
+  //\r
+  // Clean up the stored last packet.\r
+  //\r
+  if (Instance->LastPacket != NULL) {\r
+    NetbufFree (Instance->LastPacket);\r
+    Instance->LastPacket = NULL;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) {\r
+    Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link);\r
+    RemoveEntryList (Entry);\r
+    FreePool (Block);\r
+  }\r
+\r
+  //\r
+  // Reinitialize the corresponding fields of the Mtftp6 operation.\r
+  //\r
+  ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO));\r
+  ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS));\r
+  ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  Instance->ServerCmdPort  = 0;\r
+  Instance->ServerDataPort = 0;\r
+  Instance->McastPort      = 0;\r
+  Instance->BlkSize        = 0;\r
+  Instance->LastBlk        = 0;\r
+  Instance->PacketToLive   = 0;\r
+  Instance->MaxRetry       = 0;\r
+  Instance->CurRetry       = 0;\r
+  Instance->Timeout        = 0;\r
+  Instance->IsMaster       = TRUE;\r
+}\r
+\r
+\r
+/**\r
+  Start the Mtftp6 instance to perform the operation, such as read file,\r
+  write file, and read directory.\r
+\r
+  @param[in]  This                   The MTFTP session.\r
+  @param[in]  Token                  The token than encapsues the user's request.\r
+  @param[in]  OpCode                 The operation to perform.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Some of the parameters are invalid.\r
+  @retval EFI_NOT_STARTED        The MTFTP session hasn't been configured.\r
+  @retval EFI_ALREADY_STARTED    There is pending operation for the session.\r
+  @retval EFI_SUCCESS            The operation is successfully started.\r
+\r
+**/\r
+EFI_STATUS\r
+Mtftp6OperationStart (\r
+  IN EFI_MTFTP6_PROTOCOL    *This,\r
+  IN EFI_MTFTP6_TOKEN       *Token,\r
+  IN UINT16                 OpCode\r
+  )\r
+{\r
+  MTFTP6_INSTANCE           *Instance;\r
+  EFI_STATUS                Status;\r
+\r
+  if (This == NULL ||\r
+      Token == NULL ||\r
+      Token->Filename == NULL ||\r
+      (Token->OptionCount != 0 && Token->OptionList == NULL) ||\r
+      (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp))\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // At least define one method to collect the data for download.\r
+  //\r
+  if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) &&\r
+      Token->Buffer == NULL &&\r
+      Token->CheckPacket == NULL\r
+      ) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // At least define one method to provide the data for upload.\r
+  //\r
+  if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Instance = MTFTP6_INSTANCE_FROM_THIS (This);\r
+\r
+  if (Instance->Config == NULL) {\r
+    return EFI_NOT_STARTED;\r
+  }\r
+\r
+  if (Instance->Token != NULL) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  Status           = EFI_SUCCESS;\r
+  Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+\r
+  //\r
+  // Parse the extension options in the request packet.\r
+  //\r
+  if (Token->OptionCount != 0) {\r
+\r
+    Status = Mtftp6ParseExtensionOption (\r
+               Token->OptionList,\r
+               Token->OptionCount,\r
+               TRUE,\r
+               &Instance->ExtInfo\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Initialize runtime data from config data or override data.\r
+  //\r
+  Instance->Token           = Token;\r
+  Instance->ServerCmdPort   = Instance->Config->InitialServerPort;\r
+  Instance->ServerDataPort  = 0;\r
+  Instance->MaxRetry        = Instance->Config->TryCount;\r
+  Instance->Timeout         = Instance->Config->TimeoutValue;\r
+  Instance->IsMaster        = TRUE;\r
+\r
+  CopyMem (\r
+    &Instance->ServerIp,\r
+    &Instance->Config->ServerIp,\r
+    sizeof (EFI_IPv6_ADDRESS)\r
+    );\r
+\r
+  if (Token->OverrideData != NULL) {\r
+    Instance->ServerCmdPort = Token->OverrideData->ServerPort;\r
+    Instance->MaxRetry      = Token->OverrideData->TryCount;\r
+    Instance->Timeout       = Token->OverrideData->TimeoutValue;\r
+\r
+    CopyMem (\r
+      &Instance->ServerIp,\r
+      &Token->OverrideData->ServerIp,\r
+      sizeof (EFI_IPv6_ADDRESS)\r
+      );\r
+  }\r
+\r
+  //\r
+  // Set default value for undefined parameters.\r
+  //\r
+  if (Instance->ServerCmdPort == 0) {\r
+    Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT;\r
+  }\r
+  if (Instance->BlkSize == 0) {\r
+    Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE;\r
+  }\r
+  if (Instance->MaxRetry == 0) {\r
+    Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY;\r
+  }\r
+  if (Instance->Timeout == 0) {\r
+    Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT;\r
+  }\r
+\r
+  Token->Status = EFI_NOT_READY;\r
+\r
+  //\r
+  // Switch the routines by the operation code.\r
+  //\r
+  switch (OpCode) {\r
+  case EFI_MTFTP6_OPCODE_RRQ:\r
+    Status = Mtftp6RrqStart (Instance, OpCode);\r
+    break;\r
+\r
+  case EFI_MTFTP6_OPCODE_DIR:\r
+    Status = Mtftp6RrqStart (Instance, OpCode);\r
+    break;\r
+\r
+  case EFI_MTFTP6_OPCODE_WRQ:\r
+    Status = Mtftp6WrqStart (Instance, OpCode);\r
+    break;\r
+\r
+  default:\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Return immediately for asynchronous or poll the instance for synchronous.\r
+  //\r
+  gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+  if (Token->Event == NULL) {\r
+    while (Token->Status == EFI_NOT_READY) {\r
+      This->Poll (This);\r
+    }\r
+    return Token->Status;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  Mtftp6OperationClean (Instance, Status);\r
+  gBS->RestoreTPL (Instance->OldTpl);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The timer ticking routine for the Mtftp6 instance.\r
+\r
+  @param[in]  Event                  The pointer to the ticking event.\r
+  @param[in]  Context                The pointer to the context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+Mtftp6OnTimerTick (\r
+  IN EFI_EVENT              Event,\r
+  IN VOID                   *Context\r
+  )\r
+{\r
+  MTFTP6_SERVICE            *Service;\r
+  MTFTP6_INSTANCE           *Instance;\r
+  LIST_ENTRY                *Entry;\r
+  LIST_ENTRY                *Next;\r
+  EFI_MTFTP6_TOKEN          *Token;\r
+  EFI_STATUS                Status;\r
+\r
+  Service = (MTFTP6_SERVICE *) Context;\r
+\r
+  //\r
+  // Iterate through all the children of the Mtftp service instance. Time\r
+  // out the packet. If maximum retries reached, clean the session up.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) {\r
+\r
+    Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link);\r
+\r
+    if (Instance->Token == NULL) {\r
+      continue;\r
+    }\r
+\r
+    if (Instance->PacketToLive > 0) {\r
+      Instance->PacketToLive--;\r
+      continue;\r
+    }\r
+\r
+    Instance->CurRetry++;\r
+    Token = Instance->Token;\r
+\r
+    if (Token->TimeoutCallback != NULL) {\r
+      //\r
+      // Call the timeout callback routine if has.\r
+      //\r
+      Status = Token->TimeoutCallback (&Instance->Mtftp6, Token);\r
+\r
+      if (EFI_ERROR (Status)) {\r
+        Mtftp6SendError (\r
+           Instance,\r
+           EFI_MTFTP6_ERRORCODE_REQUEST_DENIED,\r
+           (UINT8 *) "User aborted the transfer in time out"\r
+           );\r
+        Mtftp6OperationClean (Instance, EFI_ABORTED);\r
+        continue;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Retransmit the packet if haven't reach the maxmium retry count,\r
+    // otherwise exit the transfer.\r
+    //\r
+    if (Instance->CurRetry < Instance->MaxRetry) {\r
+      Mtftp6TransmitPacket (Instance, Instance->LastPacket);\r
+    } else {\r
+      Mtftp6OperationClean (Instance, EFI_TIMEOUT);\r
+      continue;\r
+    }\r
+  }\r
+}\r