]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Rrq.c
MdeModulePke/Mtftp4Dxe: Support windowsize in read request operation.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Mtftp4Dxe / Mtftp4Rrq.c
index 3f64bf05d916ea533b0ddee704d647113e8c02c6..fedf1cde460bf1dec3f8864ff417e2aaab256a53 100644 (file)
@@ -1,8 +1,9 @@
 /** @file\r
   Routines to process Rrq (download).\r
-  \r
-Copyright (c) 2006 - 2007, Intel Corporation<BR>\r
-All rights reserved. This program and the accompanying materials\r
+\r
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\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<BR>\r
@@ -20,24 +21,25 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
   The packet process callback for MTFTP download.\r
 \r
   @param  UdpPacket             The packet received\r
-  @param  Points                The local/remote access point of the packet\r
+  @param  EndPoint              The local/remote access point of the packet\r
   @param  IoStatus              The status of the receiving\r
   @param  Context               Opaque parameter, which is the MTFTP session\r
 \r
 **/\r
 VOID\r
+EFIAPI\r
 Mtftp4RrqInput (\r
   IN NET_BUF                *UdpPacket,\r
-  IN UDP_POINTS             *Points,\r
+  IN UDP_END_POINT          *EndPoint,\r
   IN EFI_STATUS             IoStatus,\r
   IN VOID                   *Context\r
   );\r
 \r
 \r
 /**\r
-  Start the MTFTP session to download. \r
-  \r
-  It will first initialize some of the internal states then build and send a RRQ \r
+  Start the MTFTP session to download.\r
+\r
+  It will first initialize some of the internal states then build and send a RRQ\r
   reqeuest packet, at last, it will start receive for the downloading.\r
 \r
   @param  Instance              The Mtftp session\r
@@ -97,9 +99,11 @@ Mtftp4RrqSendAck (
 {\r
   EFI_MTFTP4_PACKET         *Ack;\r
   NET_BUF                   *Packet;\r
+  EFI_STATUS                Status;\r
 \r
-  Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));\r
+  Status = EFI_SUCCESS;\r
 \r
+  Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));\r
   if (Packet == NULL) {\r
     return EFI_OUT_OF_RESOURCES;\r
   }\r
@@ -109,11 +113,17 @@ Mtftp4RrqSendAck (
                                 sizeof (EFI_MTFTP4_ACK_HEADER),\r
                                 FALSE\r
                                 );\r
+  ASSERT (Ack != NULL);\r
 \r
   Ack->Ack.OpCode   = HTONS (EFI_MTFTP4_OPCODE_ACK);\r
   Ack->Ack.Block[0] = HTONS (BlkNo);\r
 \r
-  return Mtftp4SendPacket (Instance, Packet);\r
+  Status = Mtftp4SendPacket (Instance, Packet);\r
+  if (!EFI_ERROR (Status)) {\r
+    Instance->AckedBlock = Instance->TotalBlock;\r
+  }\r
+\r
+  return Status;\r
 }\r
 \r
 \r
@@ -144,15 +154,18 @@ Mtftp4RrqSaveBlock (
   UINT16                    Block;\r
   UINT64                    Start;\r
   UINT32                    DataLen;\r
+  BOOLEAN                   Completed;\r
 \r
-  Token   = Instance->Token;\r
-  Block   = NTOHS (Packet->Data.Block);\r
-  DataLen = Len - MTFTP4_DATA_HEAD_LEN;\r
+  Completed = FALSE;\r
+  Token     = Instance->Token;\r
+  Block     = NTOHS (Packet->Data.Block);\r
+  DataLen   = Len - MTFTP4_DATA_HEAD_LEN;\r
 \r
   //\r
   // This is the last block, save the block no\r
   //\r
   if (DataLen < Instance->BlkSize) {\r
+    Completed = TRUE;\r
     Instance->LastBlock = Block;\r
     Mtftp4SetLastBlockNum (&Instance->Blocks, Block);\r
   }\r
@@ -160,8 +173,11 @@ Mtftp4RrqSaveBlock (
   //\r
   // Remove this block number from the file hole. If Mtftp4RemoveBlockNum\r
   // returns EFI_NOT_FOUND, the block has been saved, don't save it again.\r
+  // Note that : For bigger files, allowing the block counter to roll over\r
+  // to accept transfers of unlimited size. So TotalBlock is memorised as\r
+  // continuous block counter.\r
   //\r
-  Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block);\r
+  Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &Instance->TotalBlock);\r
 \r
   if (Status == EFI_NOT_FOUND) {\r
     return EFI_SUCCESS;\r
@@ -184,7 +200,7 @@ Mtftp4RrqSaveBlock (
   }\r
 \r
   if (Token->Buffer != NULL) {\r
-    Start = MultU64x32 (Block - 1, Instance->BlkSize);\r
+     Start = MultU64x32 (Instance->TotalBlock - 1, Instance->BlkSize);\r
 \r
     if (Start + DataLen <= Token->BufferSize) {\r
       CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);\r
@@ -192,7 +208,7 @@ Mtftp4RrqSaveBlock (
       //\r
       // Update the file size when received the last block\r
       //\r
-      if (Instance->LastBlock == Block) {\r
+      if ((Instance->LastBlock == Block) && Completed) {\r
         Token->BufferSize = Start + DataLen;\r
       }\r
 \r
@@ -219,8 +235,8 @@ Mtftp4RrqSaveBlock (
 \r
 \r
 /**\r
-  Function to process the received data packets. \r
-  \r
+  Function to process the received data packets.\r
+\r
   It will save the block then send back an ACK if it is active.\r
 \r
   @param  Instance              The downloading MTFTP session\r
@@ -248,19 +264,22 @@ Mtftp4RrqHandleData (
   INTN                      Expected;\r
 \r
   *Completed  = FALSE;\r
+  Status      = EFI_SUCCESS;\r
   BlockNum    = NTOHS (Packet->Data.Block);\r
   Expected    = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
 \r
   ASSERT (Expected >= 0);\r
 \r
   //\r
-  // If we are active and received an unexpected packet, retransmit\r
-  // the last ACK then restart receiving. If we are passive, save\r
-  // the block.\r
+  // If we are active and received an unexpected packet, transmit\r
+  // the ACK for the block we received, then restart receiving the\r
+  // expected one. If we are passive, save the block.\r
   //\r
   if (Instance->Master && (Expected != BlockNum)) {\r
-    Mtftp4Retransmit (Instance);\r
-    return EFI_SUCCESS;\r
+    //\r
+    // If Expected is 0, (UINT16) (Expected - 1) is also the expected Ack number (65535).\r
+    //\r
+    return Mtftp4RrqSendAck (Instance,  (UINT16) (Expected - 1));\r
   }\r
 \r
   Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);\r
@@ -300,16 +319,19 @@ Mtftp4RrqHandleData (
       BlockNum = (UINT16) (Expected - 1);\r
     }\r
 \r
-    Mtftp4RrqSendAck (Instance, BlockNum);\r
+    if (Instance->WindowSize == (Instance->TotalBlock - Instance->AckedBlock) || Expected < 0) {\r
+      Status = Mtftp4RrqSendAck (Instance, BlockNum);\r
+    }\r
+\r
   }\r
 \r
-  return EFI_SUCCESS;\r
+  return Status;\r
 }\r
 \r
 \r
 /**\r
   Validate whether the options received in the server's OACK packet is valid.\r
-  \r
+\r
   The options are valid only if:\r
   1. The server doesn't include options not requested by us\r
   2. The server can only use smaller blksize than that is requested\r
@@ -340,11 +362,13 @@ Mtftp4RrqOackValid (
   }\r
 \r
   //\r
-  // Server can only specify a smaller block size to be used and\r
+  // Server can only specify a smaller block size and window size to be used and\r
   // return the timeout matches that requested.\r
   //\r
   if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0)&& (Reply->BlkSize > Request->BlkSize)) ||\r
-      (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {\r
+      (((Reply->Exist & MTFTP4_WINDOWSIZE_EXIST) != 0)&& (Reply->WindowSize > Request->WindowSize)) ||\r
+      (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))\r
+     ) {\r
     return FALSE;\r
   }\r
 \r
@@ -370,7 +394,7 @@ Mtftp4RrqOackValid (
 /**\r
   Configure a UDP IO port to receive the multicast.\r
 \r
-  @param  McastIo               The UDP IO port to configure\r
+  @param  McastIo               The UDP IO to configure\r
   @param  Context               The opaque parameter to the function which is the\r
                                 MTFTP session.\r
 \r
@@ -379,8 +403,9 @@ Mtftp4RrqOackValid (
 \r
 **/\r
 EFI_STATUS\r
+EFIAPI\r
 Mtftp4RrqConfigMcastPort (\r
-  IN UDP_IO_PORT            *McastIo,\r
+  IN UDP_IO                 *McastIo,\r
   IN VOID                   *Context\r
   )\r
 {\r
@@ -404,36 +429,36 @@ Mtftp4RrqConfigMcastPort (
   UdpConfig.ReceiveTimeout     = 0;\r
   UdpConfig.TransmitTimeout    = 0;\r
   UdpConfig.UseDefaultAddress  = Config->UseDefaultSetting;\r
-  UdpConfig.StationAddress     = Config->StationIp;\r
-  UdpConfig.SubnetMask         = Config->SubnetMask;\r
+  IP4_COPY_ADDRESS (&UdpConfig.StationAddress, &Config->StationIp);\r
+  IP4_COPY_ADDRESS (&UdpConfig.SubnetMask, &Config->SubnetMask);\r
   UdpConfig.StationPort        = Instance->McastPort;\r
   UdpConfig.RemotePort         = 0;\r
 \r
   Ip = HTONL (Instance->ServerIp);\r
-  CopyMem (&UdpConfig.RemoteAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+  IP4_COPY_ADDRESS (&UdpConfig.RemoteAddress, &Ip);\r
 \r
-  Status = McastIo->Udp->Configure (McastIo->Udp, &UdpConfig);\r
+  Status = McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, &UdpConfig);\r
 \r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
-  if (!Config->UseDefaultSetting && \r
+  if (!Config->UseDefaultSetting &&\r
       !EFI_IP4_EQUAL (&mZeroIp4Addr, &Config->GatewayIp)) {\r
     //\r
     // The station IP address is manually configured and the Gateway IP is not 0.\r
     // Add the default route for this UDP instance.\r
     //\r
-    Status = McastIo->Udp->Routes (\r
-                             McastIo->Udp, \r
-                             FALSE,\r
-                             &mZeroIp4Addr,\r
-                             &mZeroIp4Addr,\r
-                             &Config->GatewayIp\r
-                             );\r
-                             \r
+    Status = McastIo->Protocol.Udp4->Routes (\r
+                                       McastIo->Protocol.Udp4,\r
+                                       FALSE,\r
+                                       &mZeroIp4Addr,\r
+                                       &mZeroIp4Addr,\r
+                                       &Config->GatewayIp\r
+                                       );\r
+\r
     if (EFI_ERROR (Status)) {\r
-      McastIo->Udp->Configure (McastIo->Udp, NULL);\r
+      McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, NULL);\r
       return Status;\r
     }\r
   }\r
@@ -442,15 +467,15 @@ Mtftp4RrqConfigMcastPort (
   // join the multicast group\r
   //\r
   Ip = HTONL (Instance->McastIp);\r
-  CopyMem (&Group, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
+  IP4_COPY_ADDRESS (&Group, &Ip);\r
 \r
-  return McastIo->Udp->Groups (McastIo->Udp, TRUE, &Group);\r
+  return McastIo->Protocol.Udp4->Groups (McastIo->Protocol.Udp4, TRUE, &Group);\r
 }\r
 \r
 \r
 /**\r
-  Function to process the OACK. \r
-  \r
+  Function to process the OACK.\r
+\r
   It will first validate the OACK packet, then update the various negotiated parameters.\r
 \r
   @param  Instance              The download MTFTP session\r
@@ -477,6 +502,7 @@ Mtftp4RrqHandleOack (
   MTFTP4_OPTION             Reply;\r
   EFI_STATUS                Status;\r
   INTN                      Expected;\r
+  EFI_UDP4_PROTOCOL         *Udp4;\r
 \r
   *Completed = FALSE;\r
 \r
@@ -496,7 +522,7 @@ Mtftp4RrqHandleOack (
   //\r
   ZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
 \r
-  Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);\r
+  Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);\r
 \r
   if (EFI_ERROR (Status) ||\r
       !Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {\r
@@ -518,7 +544,7 @@ Mtftp4RrqHandleOack (
 \r
     //\r
     // Save the multicast info. Always update the Master, only update the\r
-    // multicast IP address, block size, timeoute at the first time. If IP\r
+    // multicast IP address, block size, window size, timeoute at the first time. If IP\r
     // address is updated, create a UDP child to receive the multicast.\r
     //\r
     Instance->Master = Reply.Master;\r
@@ -539,12 +565,31 @@ Mtftp4RrqHandleOack (
       //\r
       Instance->McastIp      = Reply.McastIp;\r
       Instance->McastPort    = Reply.McastPort;\r
-      Instance->McastUdpPort = UdpIoCreatePort (\r
-                                 Instance->Service->Controller,\r
-                                 Instance->Service->Image,\r
-                                 Mtftp4RrqConfigMcastPort,\r
-                                 Instance\r
-                                 );\r
+      if (Instance->McastUdpPort == NULL) {\r
+        Instance->McastUdpPort = UdpIoCreateIo (\r
+                                   Instance->Service->Controller,\r
+                                   Instance->Service->Image,\r
+                                   Mtftp4RrqConfigMcastPort,\r
+                                   UDP_IO_UDP4_VERSION,\r
+                                   Instance\r
+                                   );\r
+        if (Instance->McastUdpPort != NULL) {\r
+          Status = gBS->OpenProtocol (\r
+                          Instance->McastUdpPort->UdpHandle,\r
+                          &gEfiUdp4ProtocolGuid,\r
+                          (VOID **) &Udp4,\r
+                          Instance->Service->Image,\r
+                          Instance->Handle,\r
+                          EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                          );\r
+          if (EFI_ERROR (Status)) {\r
+            UdpIoFreeIo (Instance->McastUdpPort);\r
+            Instance->McastUdpPort = NULL;\r
+            return EFI_DEVICE_ERROR;\r
+          }\r
+        }\r
+      }\r
+\r
 \r
       if (Instance->McastUdpPort == NULL) {\r
         return EFI_DEVICE_ERROR;\r
@@ -561,31 +606,39 @@ Mtftp4RrqHandleOack (
 \r
         return Status;\r
       }\r
-    \r
+\r
       //\r
       // Update the parameters used.\r
       //\r
       if (Reply.BlkSize != 0) {\r
         Instance->BlkSize = Reply.BlkSize;\r
       }\r
-      \r
+\r
+      if (Reply.WindowSize != 0) {\r
+        Instance->WindowSize = Reply.WindowSize;\r
+      }\r
+\r
       if (Reply.Timeout != 0) {\r
         Instance->Timeout = Reply.Timeout;\r
-      }  \r
-    }    \r
-    \r
+      }\r
+    }\r
+\r
   } else {\r
     Instance->Master = TRUE;\r
-    \r
+\r
     if (Reply.BlkSize != 0) {\r
       Instance->BlkSize = Reply.BlkSize;\r
     }\r
 \r
+    if (Reply.WindowSize != 0) {\r
+      Instance->WindowSize = Reply.WindowSize;\r
+    }\r
+\r
     if (Reply.Timeout != 0) {\r
       Instance->Timeout = Reply.Timeout;\r
     }\r
   }\r
-  \r
+\r
   //\r
   // Send an ACK to (Expected - 1) which is 0 for unicast download,\r
   // or tell the server we want to receive the Expected block.\r
@@ -598,15 +651,16 @@ Mtftp4RrqHandleOack (
   The packet process callback for MTFTP download.\r
 \r
   @param  UdpPacket             The packet received\r
-  @param  Points                The local/remote access point of the packet\r
+  @param  EndPoint              The local/remote access point of the packet\r
   @param  IoStatus              The status of the receiving\r
   @param  Context               Opaque parameter, which is the MTFTP session\r
 \r
 **/\r
 VOID\r
+EFIAPI\r
 Mtftp4RrqInput (\r
   IN NET_BUF                *UdpPacket,\r
-  IN UDP_POINTS             *Points,\r
+  IN UDP_END_POINT          *EndPoint,\r
   IN EFI_STATUS             IoStatus,\r
   IN VOID                   *Context\r
   )\r
@@ -637,7 +691,7 @@ Mtftp4RrqInput (
   //\r
   // Find the port this packet is from to restart receive correctly.\r
   //\r
-  Multicast = (BOOLEAN) (Points->LocalAddr == Instance->McastIp);\r
+  Multicast = (BOOLEAN) (EndPoint->LocalAddr.Addr[0] == Instance->McastIp);\r
 \r
   if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {\r
     goto ON_EXIT;\r
@@ -649,11 +703,11 @@ Mtftp4RrqInput (
   // is required to use the same port as RemotePort to multicast the\r
   // data.\r
   //\r
-  if (Points->RemotePort != Instance->ConnectedPort) {\r
+  if (EndPoint->RemotePort != Instance->ConnectedPort) {\r
     if (Instance->ConnectedPort != 0) {\r
       goto ON_EXIT;\r
     } else {\r
-      Instance->ConnectedPort = Points->RemotePort;\r
+      Instance->ConnectedPort = EndPoint->RemotePort;\r
     }\r
   }\r
 \r
@@ -674,6 +728,7 @@ Mtftp4RrqInput (
 \r
   } else {\r
     Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
+    ASSERT (Packet != NULL);\r
   }\r
 \r
   Opcode = NTOHS (Packet->OpCode);\r
@@ -730,7 +785,7 @@ Mtftp4RrqInput (
   case EFI_MTFTP4_OPCODE_ERROR:\r
     Status = EFI_TFTP_ERROR;\r
     break;\r
-    \r
+\r
   default:\r
     break;\r
   }\r
@@ -742,7 +797,7 @@ ON_EXIT:
   // receive, otherwise end the session.\r
   //\r
   if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {\r
-    gBS->FreePool (Packet);\r
+    FreePool (Packet);\r
   }\r
 \r
   if (UdpPacket != NULL) {\r