]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c
Update the copyright notice format
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Mtftp4Dxe / Mtftp4Wrq.c
CommitLineData
772db4bb 1/** @file\r
dab714aa 2 Routines to process Wrq (upload).\r
3 \r
e5eed7d3
HT
4Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>\r
5This program and the accompanying materials\r
772db4bb 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
dab714aa 8http://opensource.org/licenses/bsd-license.php<BR>\r
772db4bb 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
772db4bb 13**/\r
14\r
15#include "Mtftp4Impl.h"\r
16\r
772db4bb 17\r
18\r
19/**\r
20 Build then send a MTFTP data packet for the MTFTP upload session.\r
21\r
dab714aa 22 @param Instance The MTFTP upload session.\r
23 @param BlockNum The block number to send.\r
772db4bb 24\r
dab714aa 25 @retval EFI_OUT_OF_RESOURCES Failed to build the packet.\r
772db4bb 26 @retval EFI_ABORTED The consumer of this child directs to abort the\r
dab714aa 27 transmission by return an error through PacketNeeded.\r
772db4bb 28 @retval EFI_SUCCESS The data is sent.\r
29\r
30**/\r
31EFI_STATUS\r
32Mtftp4WrqSendBlock (\r
dab714aa 33 IN OUT MTFTP4_PROTOCOL *Instance,\r
34 IN UINT16 BlockNum\r
772db4bb 35 )\r
36{\r
37 EFI_MTFTP4_PACKET *Packet;\r
38 EFI_MTFTP4_TOKEN *Token;\r
39 NET_BUF *UdpPacket;\r
40 EFI_STATUS Status;\r
41 UINT16 DataLen;\r
42 UINT8 *DataBuf;\r
43 UINT64 Start;\r
44\r
45 //\r
46 // Allocate a buffer to hold the user data\r
47 //\r
48 UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);\r
49\r
50 if (UdpPacket == NULL) {\r
51 return EFI_OUT_OF_RESOURCES;\r
52 }\r
53\r
dab714aa 54 Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);\r
894d038a 55 ASSERT (Packet != NULL);\r
772db4bb 56\r
57 Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);\r
58 Packet->Data.Block = HTONS (BlockNum);\r
59\r
60 //\r
61 // Read the block from either the buffer or PacketNeeded callback\r
62 //\r
63 Token = Instance->Token;\r
64 DataLen = Instance->BlkSize;\r
65\r
66 if (Token->Buffer != NULL) {\r
67 Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);\r
68\r
69 if (Token->BufferSize < Start + Instance->BlkSize) {\r
70 DataLen = (UINT16) (Token->BufferSize - Start);\r
71 Instance->LastBlock = BlockNum;\r
72 Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);\r
73 }\r
74\r
75 if (DataLen > 0) {\r
76 NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
e48e37fc 77 CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);\r
772db4bb 78 }\r
79\r
80 } else {\r
81 //\r
82 // Get data from PacketNeeded\r
83 //\r
84 DataBuf = NULL;\r
dab714aa 85 Status = Token->PacketNeeded (\r
86 &Instance->Mtftp4,\r
87 Token,\r
88 &DataLen,\r
89 (VOID **) &DataBuf\r
90 );\r
772db4bb 91\r
92 if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {\r
93 if (DataBuf != NULL) {\r
766c7483 94 FreePool (DataBuf);\r
772db4bb 95 }\r
96\r
97 Mtftp4SendError (\r
98 Instance,\r
99 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
67a58d0f 100 (UINT8 *) "User aborted the transfer"\r
772db4bb 101 );\r
102\r
103 return EFI_ABORTED;\r
104 }\r
105\r
106 if (DataLen < Instance->BlkSize) {\r
107 Instance->LastBlock = BlockNum;\r
108 Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);\r
109 }\r
110\r
111 if (DataLen > 0) {\r
112 NetbufAllocSpace (UdpPacket, DataLen, FALSE);\r
e48e37fc 113 CopyMem (Packet->Data.Data, DataBuf, DataLen);\r
766c7483 114 FreePool (DataBuf);\r
772db4bb 115 }\r
116 }\r
117\r
118 return Mtftp4SendPacket (Instance, UdpPacket);\r
119}\r
120\r
121\r
122/**\r
dab714aa 123 Function to handle received ACK packet. \r
124 \r
125 If the ACK number matches the expected block number, and there are more \r
126 data pending, send the next block. Otherwise tell the caller that we are done.\r
772db4bb 127\r
128 @param Instance The MTFTP upload session\r
129 @param Packet The MTFTP packet received\r
130 @param Len The packet length\r
131 @param Completed Return whether the upload has finished.\r
132\r
133 @retval EFI_SUCCESS The ACK is successfully processed.\r
134 @retval EFI_TFTP_ERROR The block number loops back.\r
135 @retval Others Failed to transmit the next data packet.\r
136\r
137**/\r
138EFI_STATUS\r
139Mtftp4WrqHandleAck (\r
dab714aa 140 IN MTFTP4_PROTOCOL *Instance,\r
141 IN EFI_MTFTP4_PACKET *Packet,\r
142 IN UINT32 Len,\r
143 OUT BOOLEAN *Completed\r
772db4bb 144 )\r
145{\r
146 UINT16 AckNum;\r
147 INTN Expected;\r
148\r
149 *Completed = FALSE;\r
150 AckNum = NTOHS (Packet->Ack.Block[0]);\r
151 Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
152\r
153 ASSERT (Expected >= 0);\r
154\r
155 //\r
156 // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput\r
157 // restart receive.\r
158 //\r
159 if (Expected != AckNum) {\r
160 return EFI_SUCCESS;\r
161 }\r
162\r
163 //\r
164 // Remove the acked block number, if this is the last block number,\r
165 // tell the Mtftp4WrqInput to finish the transfer. This is the last\r
166 // block number if the block range are empty..\r
167 //\r
168 Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum);\r
169\r
170 Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
171\r
172 if (Expected < 0) {\r
dab714aa 173 \r
772db4bb 174 //\r
175 // The block range is empty. It may either because the the last\r
176 // block has been ACKed, or the sequence number just looped back,\r
177 // that is, there is more than 0xffff blocks.\r
178 //\r
179 if (Instance->LastBlock == AckNum) {\r
180 ASSERT (Instance->LastBlock >= 1);\r
181 *Completed = TRUE;\r
182 return EFI_SUCCESS;\r
183\r
184 } else {\r
185 Mtftp4SendError (\r
186 Instance,\r
187 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
67a58d0f 188 (UINT8 *) "Block number rolls back, not supported, try blksize option"\r
772db4bb 189 );\r
190\r
191 return EFI_TFTP_ERROR;\r
192 }\r
193 }\r
194\r
195 return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);\r
196}\r
197\r
198\r
199/**\r
dab714aa 200 Check whether the received OACK is valid. \r
201 \r
202 The OACK is valid only if:\r
772db4bb 203 1. It only include options requested by us\r
204 2. It can only include a smaller block size\r
205 3. It can't change the proposed time out value.\r
dab714aa 206 4. Other requirements of the individal MTFTP options as required.\r
772db4bb 207\r
208 @param Reply The options included in the OACK\r
209 @param Request The options we requested\r
210\r
dab714aa 211 @retval TRUE The options included in OACK is valid.\r
212 @retval FALSE The options included in OACK is invalid.\r
772db4bb 213\r
214**/\r
215BOOLEAN\r
216Mtftp4WrqOackValid (\r
217 IN MTFTP4_OPTION *Reply,\r
218 IN MTFTP4_OPTION *Request\r
219 )\r
220{\r
221 //\r
222 // It is invalid for server to return options we don't request\r
223 //\r
dab714aa 224 if ((Reply->Exist & ~Request->Exist) != 0) {\r
772db4bb 225 return FALSE;\r
226 }\r
227\r
228 //\r
229 // Server can only specify a smaller block size to be used and\r
230 // return the timeout matches that requested.\r
231 //\r
dab714aa 232 if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) ||\r
233 (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {\r
772db4bb 234 return FALSE;\r
235 }\r
236\r
237 return TRUE;\r
238}\r
239\r
240\r
241/**\r
dab714aa 242 Function to handle the MTFTP OACK packet. \r
243 \r
244 It parses the packet's options, and update the internal states of the session.\r
772db4bb 245\r
246 @param Instance The MTFTP session\r
247 @param Packet The received OACK packet\r
248 @param Len The length of the packet\r
249 @param Completed Whether the transmisson has completed. NOT used by\r
250 this function.\r
251\r
252 @retval EFI_SUCCESS The OACK process is OK\r
253 @retval EFI_TFTP_ERROR Some error occured, and the session reset.\r
254\r
255**/\r
256EFI_STATUS\r
257Mtftp4WrqHandleOack (\r
dab714aa 258 IN OUT MTFTP4_PROTOCOL *Instance,\r
259 IN EFI_MTFTP4_PACKET *Packet,\r
260 IN UINT32 Len,\r
261 OUT BOOLEAN *Completed\r
772db4bb 262 )\r
263{\r
264 MTFTP4_OPTION Reply;\r
265 EFI_MTFTP4_PACKET Bogus;\r
266 EFI_STATUS Status;\r
267 INTN Expected;\r
268\r
269 *Completed = FALSE;\r
270\r
271 //\r
272 // Ignore the OACK if already started the upload\r
273 //\r
274 Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);\r
275\r
276 if (Expected != 0) {\r
277 return EFI_SUCCESS;\r
278 }\r
279\r
280 //\r
281 // Parse and validate the options from server\r
282 //\r
e48e37fc 283 ZeroMem (&Reply, sizeof (MTFTP4_OPTION));\r
772db4bb 284 Status = Mtftp4ParseOptionOack (Packet, Len, &Reply);\r
285\r
286 if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {\r
287 //\r
288 // Don't send a MTFTP error packet when out of resource, it can\r
289 // only make it worse.\r
290 //\r
291 if (Status != EFI_OUT_OF_RESOURCES) {\r
292 Mtftp4SendError (\r
293 Instance,\r
294 EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,\r
67a58d0f 295 (UINT8 *) "Mal-formated OACK packet"\r
772db4bb 296 );\r
297 }\r
298\r
299 return EFI_TFTP_ERROR;\r
300 }\r
301\r
302 if (Reply.BlkSize != 0) {\r
303 Instance->BlkSize = Reply.BlkSize;\r
304 }\r
305\r
306 if (Reply.Timeout != 0) {\r
307 Instance->Timeout = Reply.Timeout;\r
308 }\r
309\r
310 //\r
311 // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,\r
312 // which will start the transmission of the first data block.\r
313 //\r
314 Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);\r
315 Bogus.Ack.Block[0] = 0;\r
316\r
dab714aa 317 Status = Mtftp4WrqHandleAck (\r
318 Instance,\r
319 &Bogus,\r
320 sizeof (EFI_MTFTP4_ACK_HEADER),\r
321 Completed\r
322 );\r
323\r
324 return Status;\r
772db4bb 325}\r
326\r
327\r
328/**\r
329 The input process routine for MTFTP upload.\r
330\r
331 @param UdpPacket The received MTFTP packet.\r
b45b45b2 332 @param EndPoint The local/remote access point\r
772db4bb 333 @param IoStatus The result of the packet receiving\r
334 @param Context Opaque parameter for the callback, which is the\r
335 MTFTP session.\r
772db4bb 336**/\r
337VOID\r
e798cd87 338EFIAPI\r
772db4bb 339Mtftp4WrqInput (\r
340 IN NET_BUF *UdpPacket,\r
b45b45b2 341 IN UDP_END_POINT *EndPoint,\r
772db4bb 342 IN EFI_STATUS IoStatus,\r
343 IN VOID *Context\r
344 )\r
345{\r
346 MTFTP4_PROTOCOL *Instance;\r
347 EFI_MTFTP4_PACKET *Packet;\r
348 BOOLEAN Completed;\r
349 EFI_STATUS Status;\r
350 UINT32 Len;\r
351 UINT16 Opcode;\r
352\r
353 Instance = (MTFTP4_PROTOCOL *) Context;\r
354 NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);\r
355\r
356 Completed = FALSE;\r
357 Packet = NULL;\r
358 Status = EFI_SUCCESS;\r
359\r
360 if (EFI_ERROR (IoStatus)) {\r
361 Status = IoStatus;\r
362 goto ON_EXIT;\r
363 }\r
364\r
365 ASSERT (UdpPacket != NULL);\r
366\r
367 if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {\r
368 goto ON_EXIT;\r
369 }\r
370\r
371 //\r
372 // Client send initial request to server's listening port. Server\r
373 // will select a UDP port to communicate with the client.\r
374 //\r
b45b45b2 375 if (EndPoint->RemotePort != Instance->ConnectedPort) {\r
772db4bb 376 if (Instance->ConnectedPort != 0) {\r
377 goto ON_EXIT;\r
378 } else {\r
b45b45b2 379 Instance->ConnectedPort = EndPoint->RemotePort;\r
772db4bb 380 }\r
381 }\r
382\r
383 //\r
384 // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
385 //\r
386 Len = UdpPacket->TotalSize;\r
387\r
388 if (UdpPacket->BlockOpNum > 1) {\r
e48e37fc 389 Packet = AllocatePool (Len);\r
772db4bb 390\r
391 if (Packet == NULL) {\r
392 Status = EFI_OUT_OF_RESOURCES;\r
393 goto ON_EXIT;\r
394 }\r
395\r
396 NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
397\r
398 } else {\r
399 Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
400 }\r
401\r
402 Opcode = NTOHS (Packet->OpCode);\r
403\r
404 //\r
405 // Call the user's CheckPacket if provided. Abort the transmission\r
406 // if CheckPacket returns an EFI_ERROR code.\r
407 //\r
408 if ((Instance->Token->CheckPacket != NULL) &&\r
409 ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {\r
410\r
411 Status = Instance->Token->CheckPacket (\r
412 &Instance->Mtftp4,\r
413 Instance->Token,\r
414 (UINT16) Len,\r
415 Packet\r
416 );\r
417\r
418 if (EFI_ERROR (Status)) {\r
419 //\r
420 // Send an error message to the server to inform it\r
421 //\r
422 if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {\r
423 Mtftp4SendError (\r
424 Instance,\r
425 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
67a58d0f 426 (UINT8 *) "User aborted the transfer"\r
772db4bb 427 );\r
428 }\r
429\r
430 Status = EFI_ABORTED;\r
431 goto ON_EXIT;\r
432 }\r
433 }\r
434\r
435 switch (Opcode) {\r
436 case EFI_MTFTP4_OPCODE_ACK:\r
437 if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {\r
438 goto ON_EXIT;\r
439 }\r
440\r
441 Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);\r
442 break;\r
443\r
444 case EFI_MTFTP4_OPCODE_OACK:\r
445 if (Len <= MTFTP4_OPCODE_LEN) {\r
446 goto ON_EXIT;\r
447 }\r
448\r
449 Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);\r
450 break;\r
451\r
452 case EFI_MTFTP4_OPCODE_ERROR:\r
453 Status = EFI_TFTP_ERROR;\r
454 break;\r
dab714aa 455 \r
456 default:\r
457 break;\r
772db4bb 458 }\r
459\r
460ON_EXIT:\r
461 //\r
462 // Free the resources, then if !EFI_ERROR (Status) and not completed,\r
463 // restart the receive, otherwise end the session.\r
464 //\r
465 if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {\r
766c7483 466 FreePool (Packet);\r
772db4bb 467 }\r
468\r
469 if (UdpPacket != NULL) {\r
470 NetbufFree (UdpPacket);\r
471 }\r
472\r
473 if (!EFI_ERROR (Status) && !Completed) {\r
474 Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
475 }\r
476\r
477 //\r
478 // Status may have been updated by UdpIoRecvDatagram\r
479 //\r
480 if (EFI_ERROR (Status) || Completed) {\r
481 Mtftp4CleanOperation (Instance, Status);\r
482 }\r
483}\r
dab714aa 484\r
485\r
486\r
487/**\r
488 Start the MTFTP session for upload.\r
489 \r
490 It will first init some states, then send the WRQ request packet, \r
491 and start receiving the packet.\r
492\r
493 @param Instance The MTFTP session\r
494 @param Operation Redundant parameter, which is always\r
495 EFI_MTFTP4_OPCODE_WRQ here.\r
496\r
497 @retval EFI_SUCCESS The upload process has been started.\r
498 @retval Others Failed to start the upload.\r
499\r
500**/\r
501EFI_STATUS\r
502Mtftp4WrqStart (\r
503 IN MTFTP4_PROTOCOL *Instance,\r
504 IN UINT16 Operation\r
505 )\r
506{\r
507 EFI_STATUS Status;\r
508\r
509 //\r
510 // The valid block number range are [0, 0xffff]. For example:\r
511 // the client sends an WRQ request to the server, the server\r
512 // ACK with an ACK0 to let client start transfer the first\r
513 // packet.\r
514 //\r
515 Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);\r
516\r
517 if (EFI_ERROR (Status)) {\r
518 return Status;\r
519 }\r
520\r
521 Status = Mtftp4SendRequest (Instance);\r
522\r
523 if (EFI_ERROR (Status)) {\r
524 return Status;\r
525 }\r
526\r
527 return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
528}\r
529\r