--- /dev/null
+/** @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