]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Support.c
MdeModulePkg: Removing ipf which is no longer supported from edk2.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Mtftp4Dxe / Mtftp4Support.c
... / ...
CommitLineData
1/** @file\r
2 Support routines for Mtftp.\r
3\r
4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
5This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php<BR>\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "Mtftp4Impl.h"\r
16\r
17\r
18/**\r
19 Allocate a MTFTP4 block range, then init it to the range of [Start, End]\r
20\r
21 @param Start The start block number\r
22 @param End The last block number in the range\r
23\r
24 @return Pointer to the created block range, NULL if failed to allocate memory.\r
25\r
26**/\r
27MTFTP4_BLOCK_RANGE *\r
28Mtftp4AllocateRange (\r
29 IN UINT16 Start,\r
30 IN UINT16 End\r
31 )\r
32{\r
33 MTFTP4_BLOCK_RANGE *Range;\r
34\r
35 Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE));\r
36\r
37 if (Range == NULL) {\r
38 return NULL;\r
39 }\r
40\r
41 InitializeListHead (&Range->Link);\r
42 Range->Start = Start;\r
43 Range->End = End;\r
44 Range->Bound = End;\r
45\r
46 return Range;\r
47}\r
48\r
49\r
50/**\r
51 Initialize the block range for either RRQ or WRQ.\r
52\r
53 RRQ and WRQ have different requirements for Start and End.\r
54 For example, during start up, WRQ initializes its whole valid block range\r
55 to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us\r
56 to start the upload. When the client received ACK0, it will remove 0 from the\r
57 range, get the next block number, which is 1, then upload the BLOCK1. For RRQ\r
58 without option negotiation, the server will directly send us the BLOCK1 in\r
59 response to the client's RRQ. When received BLOCK1, the client will remove\r
60 it from the block range and send an ACK. It also works if there is option\r
61 negotiation.\r
62\r
63 @param Head The block range head to initialize\r
64 @param Start The Start block number.\r
65 @param End The last block number.\r
66\r
67 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range\r
68 @retval EFI_SUCCESS The initial block range is created.\r
69\r
70**/\r
71EFI_STATUS\r
72Mtftp4InitBlockRange (\r
73 IN LIST_ENTRY *Head,\r
74 IN UINT16 Start,\r
75 IN UINT16 End\r
76 )\r
77{\r
78 MTFTP4_BLOCK_RANGE *Range;\r
79\r
80 Range = Mtftp4AllocateRange (Start, End);\r
81\r
82 if (Range == NULL) {\r
83 return EFI_OUT_OF_RESOURCES;\r
84 }\r
85\r
86 InsertTailList (Head, &Range->Link);\r
87 return EFI_SUCCESS;\r
88}\r
89\r
90\r
91/**\r
92 Get the first valid block number on the range list.\r
93\r
94 @param Head The block range head\r
95\r
96 @return The first valid block number, -1 if the block range is empty.\r
97\r
98**/\r
99INTN\r
100Mtftp4GetNextBlockNum (\r
101 IN LIST_ENTRY *Head\r
102 )\r
103{\r
104 MTFTP4_BLOCK_RANGE *Range;\r
105\r
106 if (IsListEmpty (Head)) {\r
107 return -1;\r
108 }\r
109\r
110 Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);\r
111 return Range->Start;\r
112}\r
113\r
114\r
115/**\r
116 Set the last block number of the block range list.\r
117\r
118 It will remove all the blocks after the Last. MTFTP initialize the block range\r
119 to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the\r
120 last block number, it will call this function to set the last block number.\r
121\r
122 @param Head The block range list\r
123 @param Last The last block number\r
124\r
125**/\r
126VOID\r
127Mtftp4SetLastBlockNum (\r
128 IN LIST_ENTRY *Head,\r
129 IN UINT16 Last\r
130 )\r
131{\r
132 MTFTP4_BLOCK_RANGE *Range;\r
133\r
134 //\r
135 // Iterate from the tail to head to remove the block number\r
136 // after the last.\r
137 //\r
138 while (!IsListEmpty (Head)) {\r
139 Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);\r
140\r
141 if (Range->Start > Last) {\r
142 RemoveEntryList (&Range->Link);\r
143 FreePool (Range);\r
144 continue;\r
145 }\r
146\r
147 if (Range->End > Last) {\r
148 Range->End = Last;\r
149 }\r
150\r
151 return ;\r
152 }\r
153}\r
154\r
155\r
156/**\r
157 Remove the block number from the block range list.\r
158\r
159 @param Head The block range list to remove from\r
160 @param Num The block number to remove\r
161 @param Completed Whether Num is the last block number\r
162 @param TotalBlock The continuous block number in all\r
163\r
164 @retval EFI_NOT_FOUND The block number isn't in the block range list\r
165 @retval EFI_SUCCESS The block number has been removed from the list\r
166 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource\r
167\r
168**/\r
169EFI_STATUS\r
170Mtftp4RemoveBlockNum (\r
171 IN LIST_ENTRY *Head,\r
172 IN UINT16 Num,\r
173 IN BOOLEAN Completed,\r
174 OUT UINT64 *TotalBlock\r
175 )\r
176{\r
177 MTFTP4_BLOCK_RANGE *Range;\r
178 MTFTP4_BLOCK_RANGE *NewRange;\r
179 LIST_ENTRY *Entry;\r
180\r
181 NET_LIST_FOR_EACH (Entry, Head) {\r
182\r
183 //\r
184 // Each block represents a hole [Start, End] in the file,\r
185 // skip to the first range with End >= Num\r
186 //\r
187 Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);\r
188\r
189 if (Range->End < Num) {\r
190 continue;\r
191 }\r
192\r
193 //\r
194 // There are three different cases for Start\r
195 // 1. (Start > Num) && (End >= Num):\r
196 // because all the holes before this one has the condition of\r
197 // End < Num, so this block number has been removed.\r
198 //\r
199 // 2. (Start == Num) && (End >= Num):\r
200 // Need to increase the Start by one, and if End == Num, this\r
201 // hole has been removed completely, remove it.\r
202 //\r
203 // 3. (Start < Num) && (End >= Num):\r
204 // if End == Num, only need to decrease the End by one because\r
205 // we have (Start < Num) && (Num == End), so (Start <= End - 1).\r
206 // if (End > Num), the hold is splited into two holes, with\r
207 // [Start, Num - 1] and [Num + 1, End].\r
208 //\r
209 if (Range->Start > Num) {\r
210 return EFI_NOT_FOUND;\r
211\r
212 } else if (Range->Start == Num) {\r
213 Range->Start++;\r
214\r
215 //\r
216 // Note that: RFC 1350 does not mention block counter roll-over,\r
217 // but several TFTP hosts implement the roll-over be able to accept\r
218 // transfers of unlimited size. There is no consensus, however, whether\r
219 // the counter should wrap around to zero or to one. Many implementations\r
220 // wrap to zero, because this is the simplest to implement. Here we choose\r
221 // this solution.\r
222 //\r
223 *TotalBlock = Num;\r
224\r
225 if (Range->Round > 0) {\r
226 *TotalBlock += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;\r
227 }\r
228\r
229 if (Range->Start > Range->Bound) {\r
230 Range->Start = 0;\r
231 Range->Round ++;\r
232 }\r
233\r
234 if ((Range->Start > Range->End) || Completed) {\r
235 RemoveEntryList (&Range->Link);\r
236 FreePool (Range);\r
237 }\r
238\r
239 return EFI_SUCCESS;\r
240\r
241 } else {\r
242 if (Range->End == Num) {\r
243 Range->End--;\r
244 } else {\r
245 NewRange = Mtftp4AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);\r
246\r
247 if (NewRange == NULL) {\r
248 return EFI_OUT_OF_RESOURCES;\r
249 }\r
250\r
251 Range->End = Num - 1;\r
252 NetListInsertAfter (&Range->Link, &NewRange->Link);\r
253 }\r
254\r
255 return EFI_SUCCESS;\r
256 }\r
257 }\r
258\r
259 return EFI_NOT_FOUND;\r
260}\r
261\r
262\r
263/**\r
264 Build then transmit the request packet for the MTFTP session.\r
265\r
266 @param Instance The Mtftp session\r
267\r
268 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request\r
269 @retval EFI_SUCCESS The request is built and sent\r
270 @retval Others Failed to transmit the packet.\r
271\r
272**/\r
273EFI_STATUS\r
274Mtftp4SendRequest (\r
275 IN MTFTP4_PROTOCOL *Instance\r
276 )\r
277{\r
278 EFI_MTFTP4_PACKET *Packet;\r
279 EFI_MTFTP4_OPTION *Options;\r
280 EFI_MTFTP4_TOKEN *Token;\r
281 RETURN_STATUS Status;\r
282 NET_BUF *Nbuf;\r
283 UINT8 *Mode;\r
284 UINT8 *Cur;\r
285 UINTN Index;\r
286 UINT32 BufferLength;\r
287 UINTN FileNameLength;\r
288 UINTN ModeLength;\r
289 UINTN OptionStrLength;\r
290 UINTN ValueStrLength;\r
291\r
292 Token = Instance->Token;\r
293 Options = Token->OptionList;\r
294 Mode = Instance->Token->ModeStr;\r
295\r
296 if (Mode == NULL) {\r
297 Mode = (UINT8 *) "octet";\r
298 }\r
299\r
300 //\r
301 // Compute the packet length\r
302 //\r
303 FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);\r
304 ModeLength = AsciiStrLen ((CHAR8 *) Mode);\r
305 BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4;\r
306\r
307 for (Index = 0; Index < Token->OptionCount; Index++) {\r
308 OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
309 ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
310 BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;\r
311 }\r
312 //\r
313 // Allocate a packet then copy the data over\r
314 //\r
315 if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {\r
316 return EFI_OUT_OF_RESOURCES;\r
317 }\r
318\r
319 Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);\r
320 ASSERT (Packet != NULL);\r
321\r
322 Packet->OpCode = HTONS (Instance->Operation);\r
323 BufferLength -= sizeof (Packet->OpCode);\r
324\r
325 Cur = Packet->Rrq.Filename;\r
326 Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);\r
327 ASSERT_EFI_ERROR (Status);\r
328 BufferLength -= (UINT32) (FileNameLength + 1);\r
329 Cur += FileNameLength + 1;\r
330 Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);\r
331 ASSERT_EFI_ERROR (Status);\r
332 BufferLength -= (UINT32) (ModeLength + 1);\r
333 Cur += ModeLength + 1;\r
334\r
335 for (Index = 0; Index < Token->OptionCount; ++Index) {\r
336 OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);\r
337 ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);\r
338\r
339 Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);\r
340 ASSERT_EFI_ERROR (Status);\r
341 BufferLength -= (UINT32) (OptionStrLength + 1);\r
342 Cur += OptionStrLength + 1;\r
343\r
344 Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);\r
345 ASSERT_EFI_ERROR (Status);\r
346 BufferLength -= (UINT32) (ValueStrLength + 1);\r
347 Cur += ValueStrLength + 1;\r
348\r
349 }\r
350\r
351 return Mtftp4SendPacket (Instance, Nbuf);\r
352}\r
353\r
354\r
355/**\r
356 Build then send an error message.\r
357\r
358 @param Instance The MTFTP session\r
359 @param ErrCode The error code\r
360 @param ErrInfo The error message\r
361\r
362 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet\r
363 @retval EFI_SUCCESS The error packet is transmitted.\r
364 @retval Others Failed to transmit the packet.\r
365\r
366**/\r
367EFI_STATUS\r
368Mtftp4SendError (\r
369 IN MTFTP4_PROTOCOL *Instance,\r
370 IN UINT16 ErrCode,\r
371 IN UINT8 *ErrInfo\r
372 )\r
373{\r
374 NET_BUF *Packet;\r
375 EFI_MTFTP4_PACKET *TftpError;\r
376 UINT32 Len;\r
377\r
378 Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));\r
379 Packet = NetbufAlloc (Len);\r
380 if (Packet == NULL) {\r
381 return EFI_OUT_OF_RESOURCES;\r
382 }\r
383\r
384 TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);\r
385 ASSERT (TftpError != NULL);\r
386\r
387 TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);\r
388 TftpError->Error.ErrorCode = HTONS (ErrCode);\r
389\r
390 AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, Len, (CHAR8 *) ErrInfo);\r
391\r
392 return Mtftp4SendPacket (Instance, Packet);\r
393}\r
394\r
395\r
396/**\r
397 The callback function called when the packet is transmitted.\r
398\r
399 It simply frees the packet.\r
400\r
401 @param Packet The transmitted (or failed to) packet\r
402 @param EndPoint The local and remote UDP access point\r
403 @param IoStatus The result of the transmission\r
404 @param Context Opaque parameter to the callback\r
405\r
406**/\r
407VOID\r
408EFIAPI\r
409Mtftp4OnPacketSent (\r
410 IN NET_BUF *Packet,\r
411 IN UDP_END_POINT *EndPoint,\r
412 IN EFI_STATUS IoStatus,\r
413 IN VOID *Context\r
414 )\r
415{\r
416 NetbufFree (Packet);\r
417}\r
418\r
419\r
420/**\r
421 Set the timeout for the instance. User a longer time for passive instances.\r
422\r
423 @param Instance The Mtftp session to set time out\r
424\r
425**/\r
426VOID\r
427Mtftp4SetTimeout (\r
428 IN OUT MTFTP4_PROTOCOL *Instance\r
429 )\r
430{\r
431 if (Instance->Master) {\r
432 Instance->PacketToLive = Instance->Timeout;\r
433 } else {\r
434 Instance->PacketToLive = Instance->Timeout * 2;\r
435 }\r
436}\r
437\r
438\r
439/**\r
440 Send the packet for the instance.\r
441\r
442 It will first save a reference to the packet for later retransmission.\r
443 Then determine the destination port, listen port for requests, and connected\r
444 port for others. At last, send the packet out.\r
445\r
446 @param Instance The Mtftp instance\r
447 @param Packet The packet to send\r
448\r
449 @retval EFI_SUCCESS The packet is sent out\r
450 @retval Others Failed to transmit the packet.\r
451\r
452**/\r
453EFI_STATUS\r
454Mtftp4SendPacket (\r
455 IN OUT MTFTP4_PROTOCOL *Instance,\r
456 IN OUT NET_BUF *Packet\r
457 )\r
458{\r
459 UDP_END_POINT UdpPoint;\r
460 EFI_STATUS Status;\r
461 UINT16 OpCode;\r
462 UINT8 *Buffer;\r
463\r
464 //\r
465 // Save the packet for retransmission\r
466 //\r
467 if (Instance->LastPacket != NULL) {\r
468 NetbufFree (Instance->LastPacket);\r
469 }\r
470\r
471 Instance->LastPacket = Packet;\r
472\r
473 Instance->CurRetry = 0;\r
474 Mtftp4SetTimeout (Instance);\r
475\r
476 ZeroMem (&UdpPoint, sizeof (UdpPoint));\r
477 UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;\r
478\r
479 //\r
480 // Send the requests to the listening port, other packets\r
481 // to the connected port\r
482 //\r
483 Buffer = NetbufGetByte (Packet, 0, NULL);\r
484 ASSERT (Buffer != NULL);\r
485 OpCode = NTOHS (*(UINT16 *)Buffer);\r
486\r
487 if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) ||\r
488 (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
489 (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {\r
490 UdpPoint.RemotePort = Instance->ListeningPort;\r
491 } else {\r
492 UdpPoint.RemotePort = Instance->ConnectedPort;\r
493 }\r
494\r
495 NET_GET_REF (Packet);\r
496\r
497 Status = UdpIoSendDatagram (\r
498 Instance->UnicastPort,\r
499 Packet,\r
500 &UdpPoint,\r
501 NULL,\r
502 Mtftp4OnPacketSent,\r
503 Instance\r
504 );\r
505\r
506 if (EFI_ERROR (Status)) {\r
507 NET_PUT_REF (Packet);\r
508 }\r
509\r
510 return Status;\r
511}\r
512\r
513\r
514/**\r
515 Retransmit the last packet for the instance.\r
516\r
517 @param Instance The Mtftp instance\r
518\r
519 @retval EFI_SUCCESS The last packet is retransmitted.\r
520 @retval Others Failed to retransmit.\r
521\r
522**/\r
523EFI_STATUS\r
524Mtftp4Retransmit (\r
525 IN MTFTP4_PROTOCOL *Instance\r
526 )\r
527{\r
528 UDP_END_POINT UdpPoint;\r
529 EFI_STATUS Status;\r
530 UINT16 OpCode;\r
531 UINT8 *Buffer;\r
532\r
533 ASSERT (Instance->LastPacket != NULL);\r
534\r
535 ZeroMem (&UdpPoint, sizeof (UdpPoint));\r
536 UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;\r
537\r
538 //\r
539 // Set the requests to the listening port, other packets to the connected port\r
540 //\r
541 Buffer = NetbufGetByte (Instance->LastPacket, 0, NULL);\r
542 ASSERT (Buffer != NULL);\r
543 OpCode = NTOHS (*(UINT16 *) Buffer);\r
544\r
545 if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||\r
546 (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {\r
547 UdpPoint.RemotePort = Instance->ListeningPort;\r
548 } else {\r
549 UdpPoint.RemotePort = Instance->ConnectedPort;\r
550 }\r
551\r
552 NET_GET_REF (Instance->LastPacket);\r
553\r
554 Status = UdpIoSendDatagram (\r
555 Instance->UnicastPort,\r
556 Instance->LastPacket,\r
557 &UdpPoint,\r
558 NULL,\r
559 Mtftp4OnPacketSent,\r
560 Instance\r
561 );\r
562\r
563 if (EFI_ERROR (Status)) {\r
564 NET_PUT_REF (Instance->LastPacket);\r
565 }\r
566\r
567 return Status;\r
568}\r
569\r
570\r
571/**\r
572 The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.\r
573\r
574 @param Event The ticking event\r
575 @param Context The Mtftp service instance\r
576\r
577**/\r
578VOID\r
579EFIAPI\r
580Mtftp4OnTimerTickNotifyLevel (\r
581 IN EFI_EVENT Event,\r
582 IN VOID *Context\r
583 )\r
584{\r
585 MTFTP4_SERVICE *MtftpSb;\r
586 LIST_ENTRY *Entry;\r
587 LIST_ENTRY *Next;\r
588 MTFTP4_PROTOCOL *Instance;\r
589\r
590 MtftpSb = (MTFTP4_SERVICE *) Context;\r
591\r
592 //\r
593 // Iterate through all the children of the Mtftp service instance. Time\r
594 // out the current packet transmit.\r
595 //\r
596 NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {\r
597 Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);\r
598 if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {\r
599 Instance->HasTimeout = FALSE;\r
600 } else {\r
601 Instance->HasTimeout = TRUE;\r
602 }\r
603 }\r
604}\r
605\r
606\r
607/**\r
608 The timer ticking function for the Mtftp service instance.\r
609\r
610 @param Event The ticking event\r
611 @param Context The Mtftp service instance\r
612\r
613**/\r
614VOID\r
615EFIAPI\r
616Mtftp4OnTimerTick (\r
617 IN EFI_EVENT Event,\r
618 IN VOID *Context\r
619 )\r
620{\r
621 MTFTP4_SERVICE *MtftpSb;\r
622 LIST_ENTRY *Entry;\r
623 LIST_ENTRY *Next;\r
624 MTFTP4_PROTOCOL *Instance;\r
625 EFI_MTFTP4_TOKEN *Token;\r
626\r
627 MtftpSb = (MTFTP4_SERVICE *) Context;\r
628\r
629 //\r
630 // Iterate through all the children of the Mtftp service instance.\r
631 //\r
632 NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {\r
633 Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);\r
634 if (!Instance->HasTimeout) {\r
635 continue;\r
636 }\r
637\r
638 Instance->HasTimeout = FALSE;\r
639\r
640 //\r
641 // Call the user's time out handler\r
642 //\r
643 Token = Instance->Token;\r
644\r
645 if (Token != NULL && Token->TimeoutCallback != NULL &&\r
646 EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {\r
647 Mtftp4SendError (\r
648 Instance,\r
649 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
650 (UINT8 *) "User aborted the transfer in time out"\r
651 );\r
652\r
653 Mtftp4CleanOperation (Instance, EFI_ABORTED);\r
654 continue;\r
655 }\r
656\r
657 //\r
658 // Retransmit the packet if haven't reach the maxmium retry count,\r
659 // otherwise exit the transfer.\r
660 //\r
661 if (++Instance->CurRetry < Instance->MaxRetry) {\r
662 Mtftp4Retransmit (Instance);\r
663 Mtftp4SetTimeout (Instance);\r
664 } else {\r
665 Mtftp4CleanOperation (Instance, EFI_TIMEOUT);\r
666 continue;\r
667 }\r
668 }\r
669}\r