]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Mtftp4Dxe/Mtftp4Wrq.c
Updated EFI_MTFTP4_TOKEN structure to add member Context. It's an incompatible change...
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Mtftp4Dxe / Mtftp4Wrq.c
CommitLineData
772db4bb 1/** @file\r
dab714aa 2 Routines to process Wrq (upload).\r
3 \r
894d038a 4Copyright (c) 2006 - 2009, Intel Corporation<BR>\r
772db4bb 5All rights reserved. This 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
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
94 gBS->FreePool (DataBuf);\r
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
772db4bb 114 gBS->FreePool (DataBuf);\r
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
332 @param Points The local/remote access point\r
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
338Mtftp4WrqInput (\r
339 IN NET_BUF *UdpPacket,\r
340 IN UDP_POINTS *Points,\r
341 IN EFI_STATUS IoStatus,\r
342 IN VOID *Context\r
343 )\r
344{\r
345 MTFTP4_PROTOCOL *Instance;\r
346 EFI_MTFTP4_PACKET *Packet;\r
347 BOOLEAN Completed;\r
348 EFI_STATUS Status;\r
349 UINT32 Len;\r
350 UINT16 Opcode;\r
351\r
352 Instance = (MTFTP4_PROTOCOL *) Context;\r
353 NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);\r
354\r
355 Completed = FALSE;\r
356 Packet = NULL;\r
357 Status = EFI_SUCCESS;\r
358\r
359 if (EFI_ERROR (IoStatus)) {\r
360 Status = IoStatus;\r
361 goto ON_EXIT;\r
362 }\r
363\r
364 ASSERT (UdpPacket != NULL);\r
365\r
366 if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {\r
367 goto ON_EXIT;\r
368 }\r
369\r
370 //\r
371 // Client send initial request to server's listening port. Server\r
372 // will select a UDP port to communicate with the client.\r
373 //\r
374 if (Points->RemotePort != Instance->ConnectedPort) {\r
375 if (Instance->ConnectedPort != 0) {\r
376 goto ON_EXIT;\r
377 } else {\r
378 Instance->ConnectedPort = Points->RemotePort;\r
379 }\r
380 }\r
381\r
382 //\r
383 // Copy the MTFTP packet to a continuous buffer if it isn't already so.\r
384 //\r
385 Len = UdpPacket->TotalSize;\r
386\r
387 if (UdpPacket->BlockOpNum > 1) {\r
e48e37fc 388 Packet = AllocatePool (Len);\r
772db4bb 389\r
390 if (Packet == NULL) {\r
391 Status = EFI_OUT_OF_RESOURCES;\r
392 goto ON_EXIT;\r
393 }\r
394\r
395 NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);\r
396\r
397 } else {\r
398 Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);\r
399 }\r
400\r
401 Opcode = NTOHS (Packet->OpCode);\r
402\r
403 //\r
404 // Call the user's CheckPacket if provided. Abort the transmission\r
405 // if CheckPacket returns an EFI_ERROR code.\r
406 //\r
407 if ((Instance->Token->CheckPacket != NULL) &&\r
408 ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {\r
409\r
410 Status = Instance->Token->CheckPacket (\r
411 &Instance->Mtftp4,\r
412 Instance->Token,\r
413 (UINT16) Len,\r
414 Packet\r
415 );\r
416\r
417 if (EFI_ERROR (Status)) {\r
418 //\r
419 // Send an error message to the server to inform it\r
420 //\r
421 if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {\r
422 Mtftp4SendError (\r
423 Instance,\r
424 EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,\r
67a58d0f 425 (UINT8 *) "User aborted the transfer"\r
772db4bb 426 );\r
427 }\r
428\r
429 Status = EFI_ABORTED;\r
430 goto ON_EXIT;\r
431 }\r
432 }\r
433\r
434 switch (Opcode) {\r
435 case EFI_MTFTP4_OPCODE_ACK:\r
436 if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {\r
437 goto ON_EXIT;\r
438 }\r
439\r
440 Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);\r
441 break;\r
442\r
443 case EFI_MTFTP4_OPCODE_OACK:\r
444 if (Len <= MTFTP4_OPCODE_LEN) {\r
445 goto ON_EXIT;\r
446 }\r
447\r
448 Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);\r
449 break;\r
450\r
451 case EFI_MTFTP4_OPCODE_ERROR:\r
452 Status = EFI_TFTP_ERROR;\r
453 break;\r
dab714aa 454 \r
455 default:\r
456 break;\r
772db4bb 457 }\r
458\r
459ON_EXIT:\r
460 //\r
461 // Free the resources, then if !EFI_ERROR (Status) and not completed,\r
462 // restart the receive, otherwise end the session.\r
463 //\r
464 if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {\r
e48e37fc 465 gBS->FreePool (Packet);\r
772db4bb 466 }\r
467\r
468 if (UdpPacket != NULL) {\r
469 NetbufFree (UdpPacket);\r
470 }\r
471\r
472 if (!EFI_ERROR (Status) && !Completed) {\r
473 Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
474 }\r
475\r
476 //\r
477 // Status may have been updated by UdpIoRecvDatagram\r
478 //\r
479 if (EFI_ERROR (Status) || Completed) {\r
480 Mtftp4CleanOperation (Instance, Status);\r
481 }\r
482}\r
dab714aa 483\r
484\r
485\r
486/**\r
487 Start the MTFTP session for upload.\r
488 \r
489 It will first init some states, then send the WRQ request packet, \r
490 and start receiving the packet.\r
491\r
492 @param Instance The MTFTP session\r
493 @param Operation Redundant parameter, which is always\r
494 EFI_MTFTP4_OPCODE_WRQ here.\r
495\r
496 @retval EFI_SUCCESS The upload process has been started.\r
497 @retval Others Failed to start the upload.\r
498\r
499**/\r
500EFI_STATUS\r
501Mtftp4WrqStart (\r
502 IN MTFTP4_PROTOCOL *Instance,\r
503 IN UINT16 Operation\r
504 )\r
505{\r
506 EFI_STATUS Status;\r
507\r
508 //\r
509 // The valid block number range are [0, 0xffff]. For example:\r
510 // the client sends an WRQ request to the server, the server\r
511 // ACK with an ACK0 to let client start transfer the first\r
512 // packet.\r
513 //\r
514 Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);\r
515\r
516 if (EFI_ERROR (Status)) {\r
517 return Status;\r
518 }\r
519\r
520 Status = Mtftp4SendRequest (Instance);\r
521\r
522 if (EFI_ERROR (Status)) {\r
523 return Status;\r
524 }\r
525\r
526 return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);\r
527}\r
528\r