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