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