Merged socket development branch:
[mirror_edk2.git] / AppPkg / Applications / Sockets / TftpServer / TftpServer.c
CommitLineData
7dc13291 1/** @file\r
2 This is a simple TFTP server application\r
3\r
f6e5cdd5 4 Copyright (c) 2011, 2012, Intel Corporation\r
5 All rights reserved. This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
7dc13291 13**/\r
14\r
15#include <TftpServer.h>\r
16\r
f6e5cdd5 17TSDT_TFTP_SERVER mTftpServer; ///< TFTP server's control structure\r
18volatile BOOLEAN mbTftpServerExit; ///< Set TRUE to cause TFTP server to exit\r
7dc13291 19\r
20\r
21/**\r
f6e5cdd5 22 Read file data into a buffer\r
7dc13291 23\r
f6e5cdd5 24 @param [in] pContext Connection context structure address\r
7dc13291 25\r
f6e5cdd5 26 @retval TRUE if a read error occurred\r
7dc13291 27\r
28**/\r
f6e5cdd5 29BOOLEAN\r
30BufferFill (\r
31 IN TSDT_CONNECTION_CONTEXT * pContext\r
7dc13291 32 )\r
33{\r
f6e5cdd5 34 BOOLEAN bReadError;\r
35 size_t BytesRead;\r
36 UINT64 LengthInBytes;\r
7dc13291 37\r
38 DBG_ENTER ( );\r
39\r
40 //\r
f6e5cdd5 41 // Use break instead of goto\r
7dc13291 42 //\r
f6e5cdd5 43 bReadError = FALSE;\r
7dc13291 44 for ( ; ; ) {\r
45 //\r
f6e5cdd5 46 // Determine if there is any work to do\r
47 //\r
48 LengthInBytes = DIM ( pContext->FileData ) >> 1;\r
49 if (( pContext->ValidBytes > LengthInBytes )\r
50 || ( 0 == pContext->BytesRemaining )) {\r
51 break;\r
52 }\r
53\r
54 //\r
55 // Determine the number of bytes to read\r
56 //\r
57 if ( LengthInBytes > pContext->BytesRemaining ) {\r
58 LengthInBytes = pContext->BytesRemaining;\r
59 }\r
60\r
61 //\r
62 // Read in the next portion of the file\r
63 //\r
64 BytesRead = fread ( pContext->pFill,\r
65 1,\r
66 (size_t)LengthInBytes,\r
67 pContext->File );\r
68 if ( -1 == BytesRead ) {\r
69 bReadError = TRUE;\r
7dc13291 70 break;\r
71 }\r
72\r
f6e5cdd5 73 //\r
74 // Account for the file data read\r
75 //\r
76 pContext->BytesRemaining -= BytesRead;\r
77 pContext->ValidBytes += BytesRead;\r
78 DEBUG (( DEBUG_FILE_BUFFER,\r
79 "0x%08x: Buffer filled with %Ld bytes, %Ld bytes ramaining\r\n",\r
80 pContext->pFill,\r
81 BytesRead,\r
82 pContext->BytesRemaining ));\r
83\r
84 //\r
85 // Set the next buffer location\r
86 //\r
87 pContext->pFill += BytesRead;\r
88 if ( pContext->pEnd <= pContext->pFill ) {\r
89 pContext->pFill = &pContext->FileData[ 0 ];\r
90 }\r
91\r
92 //\r
93 // Verify that the end of the buffer is reached\r
94 //\r
95 ASSERT ( 0 == ( DIM ( pContext->FileData ) & 1 ));\r
96 break;\r
97 }\r
98\r
99 //\r
100 // Return the read status\r
101 //\r
102 DBG_EXIT ( );\r
103 return bReadError;\r
104}\r
105\r
106\r
107/**\r
108 Add a connection context to the list of connection contexts.\r
109\r
110 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
111 @param [in] SocketFd Socket file descriptor\r
112\r
113 @retval Context structure address, NULL if allocation fails\r
114\r
115**/\r
116TSDT_CONNECTION_CONTEXT *\r
117ContextAdd (\r
118 IN TSDT_TFTP_SERVER * pTftpServer,\r
119 IN int SocketFd\r
120 )\r
121{\r
122 TSDT_CONNECTION_CONTEXT * pContext;\r
123 TFTP_PACKET * pEnd;\r
124 TFTP_PACKET * pPacket;\r
125\r
126 DBG_ENTER ( );\r
127\r
128 //\r
129 // Allocate a new context\r
130 //\r
131 pContext = (TSDT_CONNECTION_CONTEXT *)AllocateZeroPool ( sizeof ( *pContext ));\r
132 if ( NULL != pContext ) {\r
7dc13291 133 //\r
134 // Initialize the context\r
135 //\r
f6e5cdd5 136 pContext->SocketFd = SocketFd;\r
7dc13291 137 CopyMem ( &pContext->RemoteAddress,\r
138 &pTftpServer->RemoteAddress,\r
139 sizeof ( pContext->RemoteAddress ));\r
f6e5cdd5 140 pContext->BlockSize = 512;\r
141\r
142 //\r
143 // Buffer management\r
144 //\r
145 pContext->pFill = &pContext->FileData[ 0 ];\r
146 pContext->pEnd = &pContext->FileData[ sizeof ( pContext->FileData )];\r
147 pContext->pBuffer = pContext->pFill;\r
148\r
149 //\r
150 // Window management\r
151 //\r
152 pContext->MaxTimeout = MultU64x32 ( PcdGet32 ( Tftp_MaxTimeoutInSec ),\r
153 2 * 1000 * 1000 * 1000 );\r
154 pContext->Rtt2x = pContext->MaxTimeout;\r
155 pContext->WindowSize = MAX_PACKETS;\r
156 WindowTimeout ( pContext );\r
157\r
158 //\r
159 // Place the packets on the free list\r
160 //\r
161 pPacket = &pContext->Tx[ 0 ];\r
162 pEnd = &pPacket[ DIM ( pContext->Tx )];\r
163 while ( pEnd > pPacket ) {\r
164 PacketFree ( pContext, pPacket );\r
165 pPacket += 1;\r
166 }\r
7dc13291 167\r
168 //\r
169 // Display the new context\r
170 //\r
f6e5cdd5 171 if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
172 DEBUG (( DEBUG_PORT_WORK,\r
173 "0x%08x: Context for %d.%d.%d.%d:%d\r\n",\r
174 pContext,\r
175 (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
176 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
177 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
178 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
179 htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
180 }\r
181 else {\r
182 DEBUG (( DEBUG_PORT_WORK,\r
183 "0x%08x: Context for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
184 pContext,\r
185 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
186 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
187 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
188 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
189 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
190 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
191 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
192 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
193 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
194 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
195 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
196 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
197 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
198 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
199 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
200 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
201 htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
202 }\r
7dc13291 203\r
204 //\r
205 // Add the context to the context list\r
206 //\r
207 pContext->pNext = pTftpServer->pContextList;\r
208 pTftpServer->pContextList = pContext;\r
7dc13291 209 }\r
210\r
211 //\r
212 // Return the connection context\r
213 //\r
214 DBG_EXIT_STATUS ( pContext );\r
215 return pContext;\r
216}\r
217\r
218\r
219/**\r
220 Locate a remote connection context.\r
221\r
f6e5cdd5 222 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
7dc13291 223 @param [in] pIpAddress The start of the remote IP address in network order\r
7dc13291 224 @param [in] Port The remote port number\r
225\r
226 @retval Context structure address, NULL if not found\r
227\r
228**/\r
229TSDT_CONNECTION_CONTEXT *\r
230ContextFind (\r
231 IN TSDT_TFTP_SERVER * pTftpServer\r
232 )\r
233{\r
234 TSDT_CONNECTION_CONTEXT * pContext;\r
235\r
236 DBG_ENTER ( );\r
237\r
238 //\r
239 // Walk the list of connection contexts\r
240 //\r
241 pContext = pTftpServer->pContextList;\r
242 while ( NULL != pContext ) {\r
243 //\r
244 // Attempt to locate the remote network connection\r
245 //\r
f6e5cdd5 246 if ( 0 == memcmp ( &pTftpServer->RemoteAddress,\r
247 &pContext->RemoteAddress,\r
248 pTftpServer->RemoteAddress.v6.sin6_len )) {\r
7dc13291 249 //\r
250 // The connection was found\r
251 //\r
252 DEBUG (( DEBUG_TFTP_REQUEST,\r
253 "0x%08x: pContext found\r\n",\r
254 pContext ));\r
255 break;\r
256 }\r
257\r
258 //\r
259 // Set the next context\r
260 //\r
261 pContext = pContext->pNext;\r
262 }\r
263\r
264 //\r
265 // Return the connection context structure address\r
266 //\r
267 DBG_EXIT_HEX ( pContext );\r
268 return pContext;\r
269}\r
270\r
271\r
272/**\r
273 Remove a context from the list.\r
274\r
f6e5cdd5 275 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
276 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
7dc13291 277\r
278**/\r
279VOID\r
280ContextRemove (\r
281 IN TSDT_TFTP_SERVER * pTftpServer,\r
282 IN TSDT_CONNECTION_CONTEXT * pContext\r
283 )\r
284{\r
285 TSDT_CONNECTION_CONTEXT * pNextContext;\r
286 TSDT_CONNECTION_CONTEXT * pPreviousContext;\r
287\r
288 DBG_ENTER ( );\r
289\r
290 //\r
291 // Attempt to locate the context in the list\r
292 //\r
293 pPreviousContext = NULL;\r
294 pNextContext = pTftpServer->pContextList;\r
295 while ( NULL != pNextContext ) {\r
296 //\r
297 // Determine if the context was found\r
298 //\r
299 if ( pNextContext == pContext ) {\r
300 //\r
301 // Remove the context from the list\r
302 //\r
303 if ( NULL == pPreviousContext ) {\r
304 pTftpServer->pContextList = pContext->pNext;\r
305 }\r
306 else {\r
307 pPreviousContext->pNext = pContext->pNext;\r
308 }\r
309 break;\r
310 }\r
311\r
312 //\r
313 // Set the next context\r
314 //\r
315 pPreviousContext = pNextContext;\r
316 pNextContext = pNextContext->pNext;\r
317 }\r
318\r
319 //\r
320 // Determine if the context was found\r
321 //\r
322 if ( NULL != pContext ) {\r
323 //\r
324 // Return the resources\r
325 //\r
326 gBS->FreePool ( pContext );\r
327 }\r
328\r
329 DBG_EXIT ( );\r
330}\r
331\r
332\r
333/**\r
f6e5cdd5 334 Queue data packets for transmission\r
335\r
336 @param [in] pContext Connection context structure address\r
7dc13291 337\r
f6e5cdd5 338 @retval TRUE if a read error occurred\r
7dc13291 339\r
340**/\r
f6e5cdd5 341BOOLEAN\r
342PacketFill (\r
343 IN TSDT_CONNECTION_CONTEXT * pContext\r
7dc13291 344 )\r
345{\r
f6e5cdd5 346 BOOLEAN bReadError;\r
347 UINT64 LengthInBytes;\r
348 UINT8 * pBuffer;\r
349 TFTP_PACKET * pPacket;\r
7dc13291 350\r
351 DBG_ENTER ( );\r
352\r
353 //\r
f6e5cdd5 354 // Use break instead of goto\r
7dc13291 355 //\r
f6e5cdd5 356 bReadError = FALSE;\r
357 for ( ; ; ) {\r
7dc13291 358 //\r
f6e5cdd5 359 // Fill the buffer if necessary\r
7dc13291 360 //\r
f6e5cdd5 361 bReadError = BufferFill ( pContext );\r
362 if ( bReadError ) {\r
7dc13291 363 //\r
f6e5cdd5 364 // File access mode not supported\r
7dc13291 365 //\r
f6e5cdd5 366 DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
367 "ERROR - File read failure!\r\n" ));\r
7dc13291 368\r
369 //\r
f6e5cdd5 370 // Tell the client of the error\r
7dc13291 371 //\r
f6e5cdd5 372 SendError ( pContext,\r
373 TFTP_ERROR_SEE_MSG,\r
374 (UINT8 *)"Read failure" );\r
375 break;\r
7dc13291 376 }\r
f6e5cdd5 377\r
378 //\r
379 // Determine if any packets can be filled\r
380 //\r
381 if ( pContext->bEofSent\r
382 || ( NULL == pContext->pFreeList )) {\r
7dc13291 383 //\r
f6e5cdd5 384 // All of the packets are filled\r
7dc13291 385 //\r
f6e5cdd5 386 break;\r
7dc13291 387 }\r
7dc13291 388\r
7dc13291 389 //\r
f6e5cdd5 390 // Set the TFTP opcode and block number\r
391 //\r
392 pPacket = PacketGet ( pContext );\r
393 pBuffer = &pPacket->TxBuffer[ 0 ];\r
394 *pBuffer++ = 0;\r
395 *pBuffer++ = TFTP_OP_DATA;\r
396 *pBuffer++ = (UINT8)( pContext->BlockNumber >> 8 );\r
397 *pBuffer++ = (UINT8)pContext->BlockNumber;\r
398\r
399 //\r
400 // Determine how much data needs to be sent\r
401 //\r
402 LengthInBytes = pContext->BlockSize;\r
403 if (( pContext->BytesToSend < TFTP_MAX_BLOCK_SIZE )\r
404 && ( LengthInBytes > pContext->BytesToSend )) {\r
405 LengthInBytes = pContext->BytesToSend;\r
406 pContext->bEofSent = TRUE;\r
407 }\r
408 DEBUG (( DEBUG_TX_PACKET,\r
409 "0x%08x: Packet, Block %d filled with %d bytes\r\n",\r
410 pPacket,\r
411 pContext->BlockNumber,\r
412 (UINT32)LengthInBytes ));\r
413 \r
414 //\r
415 // Copy the file data into the packet\r
416 //\r
417 pPacket->TxBytes = (ssize_t)( 2 + 2 + LengthInBytes );\r
418 if ( 0 < LengthInBytes ) {\r
419 CopyMem ( pBuffer,\r
420 pContext->pBuffer,\r
421 (UINTN)LengthInBytes );\r
422 DEBUG (( DEBUG_FILE_BUFFER,\r
423 "0x%08x: Buffer consumed %d bytes of file data\r\n",\r
424 pContext->pBuffer,\r
425 LengthInBytes ));\r
426\r
427 //\r
428 // Account for the file data consumed\r
429 //\r
430 pContext->ValidBytes -= LengthInBytes;\r
431 pContext->BytesToSend -= LengthInBytes;\r
432 pContext->pBuffer += LengthInBytes;\r
433 if ( pContext->pEnd <= pContext->pBuffer ) {\r
434 pContext->pBuffer = &pContext->FileData[ 0 ];\r
435 }\r
436 }\r
437 \r
438 //\r
439 // Queue the packet for transmission\r
7dc13291 440 //\r
f6e5cdd5 441 PacketQueue ( pContext, pPacket );\r
7dc13291 442 }\r
443\r
f6e5cdd5 444 //\r
445 // Return the read status\r
446 //\r
7dc13291 447 DBG_EXIT ( );\r
f6e5cdd5 448 return bReadError;\r
7dc13291 449}\r
450\r
451\r
452/**\r
f6e5cdd5 453 Free the packet\r
7dc13291 454\r
f6e5cdd5 455 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
456 @param [in] pPacket Address of a ::TFTP_PACKET structure\r
7dc13291 457\r
458**/\r
459VOID\r
f6e5cdd5 460PacketFree(\r
461 IN TSDT_CONNECTION_CONTEXT * pContext,\r
462 IN TFTP_PACKET * pPacket\r
7dc13291 463 )\r
464{\r
f6e5cdd5 465 DBG_ENTER ( );\r
7dc13291 466\r
467 //\r
f6e5cdd5 468 // Don't free the error packet\r
7dc13291 469 //\r
f6e5cdd5 470 if ( pPacket != &pContext->ErrorPacket ) {\r
7dc13291 471 //\r
f6e5cdd5 472 // Place the packet on the free list\r
7dc13291 473 //\r
f6e5cdd5 474 pPacket->pNext = pContext->pFreeList;\r
475 pContext->pFreeList = pPacket;\r
476 DEBUG (( DEBUG_TX_PACKET,\r
477 "0x%08x: Packet queued to free list\r\n",\r
478 pPacket ));\r
7dc13291 479 }\r
480\r
f6e5cdd5 481 DBG_EXIT ( );\r
7dc13291 482}\r
483\r
484\r
485/**\r
f6e5cdd5 486 Get a packet from the free list for transmission\r
7dc13291 487\r
f6e5cdd5 488 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
7dc13291 489\r
f6e5cdd5 490 @retval Address of a ::TFTP_PACKET structure\r
7dc13291 491\r
492**/\r
f6e5cdd5 493TFTP_PACKET *\r
494PacketGet (\r
495 IN TSDT_CONNECTION_CONTEXT * pContext\r
7dc13291 496 )\r
497{\r
f6e5cdd5 498 TFTP_PACKET * pPacket;\r
499\r
500 DBG_ENTER ( );\r
501\r
7dc13291 502 //\r
f6e5cdd5 503 // Get the next packet from the free list\r
7dc13291 504 //\r
f6e5cdd5 505 pPacket = pContext->pFreeList;\r
506 if ( NULL != pPacket ) {\r
507 pContext->pFreeList = pPacket->pNext;\r
508 pPacket->RetryCount = 0;\r
509 DEBUG (( DEBUG_TX_PACKET,\r
510 "0x%08x: Packet removed from free list\r\n",\r
511 pPacket ));\r
7dc13291 512 }\r
513\r
514 //\r
f6e5cdd5 515 // Return the packet\r
7dc13291 516 //\r
f6e5cdd5 517 DBG_EXIT_HEX ( pPacket );\r
518 return pPacket;\r
7dc13291 519}\r
520\r
521\r
522/**\r
f6e5cdd5 523 Queue the packet for transmission\r
7dc13291 524\r
f6e5cdd5 525 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
526 @param [in] pPacket Address of a ::TFTP_PACKET structure\r
7dc13291 527\r
f6e5cdd5 528 @retval TRUE if a transmission error has occurred\r
7dc13291 529\r
530**/\r
f6e5cdd5 531BOOLEAN\r
532PacketQueue (\r
533 IN TSDT_CONNECTION_CONTEXT * pContext,\r
534 IN TFTP_PACKET * pPacket\r
7dc13291 535 )\r
536{\r
f6e5cdd5 537 BOOLEAN bTransmitError;\r
538 TFTP_PACKET * pTail;\r
539 EFI_STATUS Status;\r
540\r
541 DBG_ENTER ( );\r
7dc13291 542\r
543 //\r
f6e5cdd5 544 // Account for this data block\r
7dc13291 545 //\r
f6e5cdd5 546 pPacket->BlockNumber = pContext->BlockNumber;\r
547 pContext->BlockNumber += 1;\r
7dc13291 548\r
f6e5cdd5 549 //\r
550 // Queue the packet for transmission\r
551 //\r
552 pTail = pContext->pTxTail;\r
553 if ( NULL == pTail ) {\r
554 pContext->pTxHead = pPacket;\r
555 }\r
556 else {\r
557 pTail->pNext = pPacket;\r
558 }\r
559 pContext->pTxTail = pPacket;\r
560 pPacket->pNext = NULL;\r
561 DEBUG (( DEBUG_TX_PACKET,\r
562 "0x%08x: Packet queued to TX list\r\n",\r
563 pPacket ));\r
7dc13291 564\r
f6e5cdd5 565 //\r
566 // Start the transmission if necessary\r
567 //\r
568 bTransmitError = FALSE;\r
569 if ( pContext->PacketsInWindow < pContext->WindowSize ) {\r
570 Status = PacketTx ( pContext, pPacket );\r
571 bTransmitError = (BOOLEAN)( EFI_ERROR ( Status ));\r
572 }\r
573\r
574 //\r
575 // Return the transmit status\r
576 //\r
577 DBG_EXIT_TF ( bTransmitError );\r
578 return bTransmitError;\r
579}\r
580\r
581\r
582/**\r
583 Remove a packet from the transmit queue\r
584\r
585 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
586\r
587**/\r
588TFTP_PACKET *\r
589PacketRemove(\r
590 IN TSDT_CONNECTION_CONTEXT * pContext\r
591 )\r
592{\r
593 TFTP_PACKET * pNext;\r
594 TFTP_PACKET * pPacket;\r
595\r
596 DBG_ENTER ( );\r
597\r
598 //\r
599 // Remove a packet from the transmit queue\r
600 //\r
601 //\r
602 pPacket = pContext->pTxHead;\r
603 if ( NULL != pPacket ) {\r
604 pNext = pPacket->pNext;\r
605 pContext->pTxHead = pNext;\r
606 if ( NULL == pNext ) {\r
607 pContext->pTxTail = NULL;\r
7dc13291 608 }\r
f6e5cdd5 609 DEBUG (( DEBUG_TX_PACKET,\r
610 "0x%08x: Packet removed from TX list\r\n",\r
611 pPacket ));\r
7dc13291 612\r
613 //\r
f6e5cdd5 614 // Remove this packet from the window\r
7dc13291 615 //\r
f6e5cdd5 616 pContext->PacketsInWindow -= 1;\r
617 }\r
7dc13291 618\r
619 //\r
f6e5cdd5 620 // Return the packet\r
7dc13291 621 //\r
f6e5cdd5 622 DBG_EXIT_HEX ( pPacket );\r
623 return pPacket;\r
7dc13291 624}\r
625\r
626\r
627/**\r
f6e5cdd5 628 Transmit the packet\r
7dc13291 629\r
f6e5cdd5 630 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
631 @param [in] pPacket Address of a ::TFTP_PACKET structure\r
7dc13291 632\r
633 @retval EFI_SUCCESS Message processed successfully\r
634\r
635**/\r
636EFI_STATUS\r
f6e5cdd5 637PacketTx (\r
638 IN TSDT_CONNECTION_CONTEXT * pContext,\r
639 IN TFTP_PACKET * pPacket\r
7dc13291 640 )\r
641{\r
f6e5cdd5 642 ssize_t LengthInBytes;\r
7dc13291 643 EFI_STATUS Status;\r
644\r
f6e5cdd5 645 DBG_ENTER ( );\r
646\r
7dc13291 647 //\r
f6e5cdd5 648 // Assume success\r
7dc13291 649 //\r
f6e5cdd5 650 Status = EFI_SUCCESS;\r
651\r
652 //\r
653 // Determine if this packet should be transmitted\r
654 //\r
655 if ( PcdGet32 ( Tftp_MaxRetry ) >= pPacket->RetryCount ) {\r
656 pPacket->RetryCount += 1;\r
657\r
7dc13291 658 //\r
f6e5cdd5 659 // Display the operation\r
7dc13291 660 //\r
f6e5cdd5 661 DEBUG (( DEBUG_TX_PACKET,\r
662 "0x%08x: Packet transmiting\r\n",\r
663 pPacket ));\r
664 DEBUG (( DEBUG_TX,\r
665 "0x%08x: pContext sending 0x%08x bytes\r\n",\r
666 pContext,\r
667 pPacket->TxBytes ));\r
668\r
7dc13291 669 //\r
f6e5cdd5 670 // Keep track of when the packet was transmitted\r
7dc13291 671 //\r
f6e5cdd5 672 if ( PcdGetBool ( Tftp_HighSpeed )) {\r
673 pPacket->TxTime = GetPerformanceCounter ( );\r
674 }\r
7dc13291 675\r
676 //\r
f6e5cdd5 677 // Send the TFTP packet\r
7dc13291 678 //\r
f6e5cdd5 679 pContext->PacketsInWindow += 1;\r
680 LengthInBytes = sendto ( pContext->SocketFd,\r
681 &pPacket->TxBuffer[ 0 ],\r
682 pPacket->TxBytes,\r
683 0,\r
684 (struct sockaddr *)&pContext->RemoteAddress,\r
685 pContext->RemoteAddress.sin6_len );\r
686 if ( -1 == LengthInBytes ) {\r
687 DEBUG (( DEBUG_ERROR | DEBUG_TX,\r
688 "ERROR - Transmit failure, errno: 0x%08x\r\n",\r
689 errno ));\r
690 pContext->PacketsInWindow -= 1;\r
691 Status = EFI_DEVICE_ERROR;\r
692 }\r
693 }\r
694 else {\r
695 //\r
696 // Too many retries\r
697 //\r
698 Status = EFI_NO_RESPONSE;\r
699 DEBUG (( DEBUG_WARN | DEBUG_WINDOW,\r
700 "WARNING - No response from TFTP client\r\n" ));\r
7dc13291 701 }\r
7dc13291 702\r
703 //\r
704 // Return the operation status\r
705 //\r
f6e5cdd5 706 DBG_EXIT_STATUS ( Status );\r
7dc13291 707 return Status;\r
708}\r
709\r
710\r
711/**\r
f6e5cdd5 712 Process the work for the sockets.\r
7dc13291 713\r
f6e5cdd5 714 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
715 @param [in] pIndex Address of an index into the pollfd array\r
7dc13291 716\r
717**/\r
f6e5cdd5 718VOID\r
719PortWork (\r
720 IN TSDT_TFTP_SERVER * pTftpServer,\r
721 IN int * pIndex\r
7dc13291 722 )\r
723{\r
f6e5cdd5 724 int Index;\r
725 TSDT_CONNECTION_CONTEXT * pContext;\r
726 struct pollfd * pTftpPort;\r
727 socklen_t RemoteAddressLength;\r
728 int revents;\r
7dc13291 729\r
f6e5cdd5 730 DBG_ENTER ( );\r
7dc13291 731\r
732 //\r
f6e5cdd5 733 // Locate the port\r
734 //\r
735 Index = *pIndex;\r
736 if ( -1 != Index ) {\r
737 pTftpPort = &pTftpServer->TftpPort[ *pIndex ];\r
738\r
739 //\r
740 // Handle input events\r
741 //\r
742 revents = pTftpPort->revents;\r
743 pTftpPort->revents = 0;\r
744 if ( 0 != ( revents & POLLRDNORM )) {\r
745 //\r
746 // Receive the message from the remote system\r
747 //\r
748 RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );\r
749 pTftpServer->RxBytes = recvfrom ( pTftpPort->fd,\r
750 &pTftpServer->RxBuffer[ 0 ],\r
751 sizeof ( pTftpServer->RxBuffer ),\r
752 0,\r
753 (struct sockaddr *) &pTftpServer->RemoteAddress,\r
754 &RemoteAddressLength );\r
755 if ( -1 != pTftpServer->RxBytes ) {\r
756 if ( PcdGetBool ( Tftp_HighSpeed )) {\r
757 pTftpServer->RxTime = GetPerformanceCounter ( );\r
758 }\r
759 if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
760 DEBUG (( DEBUG_TFTP_PORT,\r
761 "Received %d bytes from %d.%d.%d.%d:%d\r\n",\r
762 pTftpServer->RxBytes,\r
763 pTftpServer->RemoteAddress.v4.sin_addr.s_addr & 0xff,\r
764 ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ) & 0xff,\r
765 ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ) & 0xff,\r
766 ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ) & 0xff,\r
767 htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
768 }\r
769 else {\r
770 DEBUG (( DEBUG_TFTP_PORT,\r
771 "Received %d bytes from [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
772 pTftpServer->RxBytes,\r
773 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
774 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
775 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
776 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
777 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
778 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
779 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
780 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
781 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
782 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
783 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
784 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
785 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
786 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
787 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
788 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
789 htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
790 }\r
791\r
792 //\r
793 // Lookup connection context using the remote system address and port\r
794 // to determine if an existing connection to this remote\r
795 // system exists\r
796 //\r
797 pContext = ContextFind ( pTftpServer );\r
798\r
799 //\r
800 // Process the received message\r
801 //\r
802 TftpProcessRequest ( pTftpServer, pContext, pTftpPort->fd );\r
803 }\r
804 else {\r
805 //\r
806 // Receive error on the TFTP server port\r
807 // Close the server socket\r
808 //\r
809 DEBUG (( DEBUG_ERROR,\r
810 "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",\r
811 errno ));\r
812 revents |= POLLHUP;\r
813 }\r
814 }\r
815\r
816 //\r
817 // Handle the close event\r
818 //\r
819 if ( 0 != ( revents & POLLHUP )) {\r
820 //\r
821 // Close the port\r
822 //\r
823 close ( pTftpPort->fd );\r
824 pTftpPort->fd = -1;\r
825 *pIndex = -1;\r
826 pTftpServer->Entries -= 1;\r
827 ASSERT ( 0 <= pTftpServer->Entries );\r
828 }\r
829 }\r
830\r
831 DBG_EXIT ( );\r
832}\r
833\r
834\r
835/**\r
836 Build and send an error packet\r
837\r
838 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
839 @param [in] Error Error number for the packet\r
840 @param [in] pError Zero terminated error string address\r
841\r
842 @retval EFI_SUCCESS Message processed successfully\r
843\r
844**/\r
845EFI_STATUS\r
846SendError (\r
847 IN TSDT_CONNECTION_CONTEXT * pContext,\r
848 IN UINT16 Error,\r
849 IN UINT8 * pError\r
850 )\r
851{\r
852 UINT8 Character;\r
853 UINT8 * pBuffer;\r
854 TFTP_PACKET * pPacket;\r
855 EFI_STATUS Status;\r
856\r
857 DBG_ENTER ( );\r
858\r
859 //\r
860 // Build the error packet\r
861 //\r
862 pPacket = &pContext->ErrorPacket;\r
863 pBuffer = &pPacket->TxBuffer[ 0 ];\r
864 pBuffer[ 0 ] = 0;\r
865 pBuffer[ 1 ] = TFTP_OP_ERROR;\r
866 pBuffer[ 2 ] = (UINT8)( Error >> 8 );\r
867 pBuffer[ 3 ] = (UINT8)Error;\r
868\r
869 //\r
870 // Copy the zero terminated string into the buffer\r
871 //\r
872 pBuffer += 4;\r
873 do {\r
874 Character = *pError++;\r
875 *pBuffer++ = Character;\r
876 } while ( 0 != Character );\r
877\r
878 //\r
879 // Send the error message\r
880 //\r
881 pPacket->TxBytes = pBuffer - &pPacket->TxBuffer[ 0 ];\r
882 Status = PacketTx ( pContext, pPacket );\r
883\r
884 //\r
885 // Return the operation status\r
886 //\r
887 DBG_EXIT_STATUS ( Status );\r
888 return Status;\r
889}\r
890\r
891\r
892/**\r
893 Scan the list of sockets and process any pending work\r
894\r
895 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
896\r
897**/\r
898VOID\r
899SocketPoll (\r
900 IN TSDT_TFTP_SERVER * pTftpServer\r
901 )\r
902{\r
903 int FDCount;\r
904\r
905 DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));\r
906\r
907 //\r
908 // Determine if any ports are active\r
909 //\r
910 if ( 0 != pTftpServer->Entries ) {\r
911 FDCount = poll ( &pTftpServer->TftpPort[ 0 ],\r
912 pTftpServer->Entries,\r
913 CLIENT_POLL_DELAY );\r
914 if ( 0 < FDCount ) {\r
915 //\r
916 // Process this port\r
917 //\r
918 PortWork ( pTftpServer, &pTftpServer->Udpv4Index );\r
919 PortWork ( pTftpServer, &pTftpServer->Udpv6Index );\r
920 }\r
921 }\r
922\r
923 DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));\r
924}\r
925\r
926\r
927/**\r
928 Process the ACK\r
929\r
930 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
931 @param [in] pContext Connection context structure address\r
932\r
933 @retval TRUE if the context should be closed\r
934\r
935**/\r
936BOOLEAN\r
937TftpAck (\r
938 IN TSDT_TFTP_SERVER * pTftpServer,\r
939 IN TSDT_CONNECTION_CONTEXT * pContext\r
940 )\r
941{\r
942 INTN AckNumber;\r
943 BOOLEAN bCloseContext;\r
944 UINT16 BlockNumber;\r
945 UINT8 * pBuffer;\r
946 TFTP_PACKET * pPacket;\r
947 EFI_STATUS Status;\r
948\r
949 DBG_ENTER ( );\r
950\r
951 //\r
952 // Use break instead of goto\r
953 //\r
954 bCloseContext = FALSE;\r
955 for ( ; ; ) {\r
956 //\r
957 // Validate the parameters\r
958 //\r
959 if ( NULL == pContext ) {\r
960 if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
961 DEBUG (( DEBUG_ERROR,\r
962 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
963 (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
964 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
965 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
966 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
967 htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
968 }\r
969 else {\r
970 DEBUG (( DEBUG_ERROR,\r
971 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
972 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
973 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
974 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
975 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
976 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
977 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
978 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
979 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
980 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
981 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
982 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
983 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
984 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
985 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
986 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
987 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
988 htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
989 }\r
990 break;\r
991 }\r
992\r
993 //\r
994 // Verify that the ACK was expected\r
995 //\r
996 pPacket = pContext->pTxHead;\r
997 if ( NULL == pPacket ) {\r
998 //\r
999 // ACK not expected!\r
1000 //\r
1001 DEBUG (( DEBUG_ERROR,\r
1002 "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",\r
1003 pContext ));\r
1004 break;\r
1005 }\r
1006\r
1007 //\r
1008 // Get the ACKed block number\r
1009 //\r
1010 pBuffer = &pTftpServer->RxBuffer[ 0 ];\r
1011 BlockNumber = HTONS ( *(UINT16 *)&pBuffer[ 2 ]);\r
1012\r
1013 //\r
1014 // Determine if this is the correct ACK\r
1015 //\r
1016 DEBUG (( DEBUG_TFTP_ACK,\r
1017 "ACK for block 0x%04x received\r\n",\r
1018 BlockNumber ));\r
1019 AckNumber = BlockNumber - pPacket->BlockNumber;\r
1020 if (( 0 > AckNumber ) || ( AckNumber >= (INTN)pContext->PacketsInWindow )){\r
1021 DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,\r
1022 "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",\r
1023 pPacket->BlockNumber,\r
1024 BlockNumber ));\r
1025 break;\r
1026 }\r
1027\r
1028 //\r
1029 // Release the ACKed packets\r
1030 //\r
1031 do {\r
1032 //\r
1033 // Remove the packet from the transmit list and window\r
1034 //\r
1035 pPacket = PacketRemove ( pContext );\r
1036\r
1037 //\r
1038 // Get the block number of this packet\r
1039 //\r
1040 AckNumber = pPacket->BlockNumber;\r
1041\r
1042 //\r
1043 // Increase the size of the transmit window\r
1044 //\r
1045 if ( PcdGetBool ( Tftp_HighSpeed )\r
1046 && ( AckNumber == BlockNumber )) {\r
1047 WindowAck ( pTftpServer, pContext, pPacket );\r
1048 }\r
1049\r
1050 //\r
1051 // Free this packet\r
1052 //\r
1053 PacketFree ( pContext, pPacket );\r
1054 } while (( NULL != pContext->pTxHead ) && ( AckNumber != BlockNumber ));\r
1055\r
1056 //\r
1057 // Fill the window with packets\r
1058 //\r
1059 pPacket = pContext->pTxHead;\r
1060 while (( NULL != pPacket )\r
1061 && ( pContext->PacketsInWindow < pContext->WindowSize )\r
1062 && ( !bCloseContext )) {\r
1063 Status = PacketTx ( pContext, pPacket );\r
1064 bCloseContext = (BOOLEAN)( EFI_ERROR ( Status ));\r
1065 pPacket = pPacket->pNext;\r
1066 }\r
1067 \r
1068 //\r
1069 // Get more packets ready for transmission\r
1070 //\r
1071 PacketFill ( pContext );\r
1072\r
1073 //\r
1074 // Close the context when the last packet is ACKed\r
1075 //\r
1076 if ( 0 == pContext->PacketsInWindow ) {\r
1077 bCloseContext = TRUE;\r
1078\r
1079 //\r
1080 // Display the bandwidth\r
1081 //\r
1082 if ( PcdGetBool ( Tftp_Bandwidth )) {\r
1083 UINT64 Bandwidth;\r
1084 UINT64 DeltaTime;\r
1085 UINT64 NanoSeconds;\r
1086 UINT32 Value;\r
1087\r
1088 //\r
1089 // Compute the download time\r
1090 //\r
1091 DeltaTime = GetPerformanceCounter ( );\r
1092 if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
1093 DeltaTime = DeltaTime - pContext->TimeStart;\r
1094 }\r
1095 else {\r
1096 DeltaTime = pContext->TimeStart - DeltaTime;\r
1097 }\r
1098 NanoSeconds = GetTimeInNanoSecond ( DeltaTime );\r
1099 Bandwidth = pContext->LengthInBytes;\r
1100 DEBUG (( DEBUG_WINDOW,\r
1101 "File Length %Ld, Transfer Time: %d.%03d Sec\r\n",\r
1102 Bandwidth,\r
1103 DivU64x32 ( NanoSeconds, 1000 * 1000 * 1000 ),\r
1104 ((UINT32)DivU64x32 ( NanoSeconds, 1000 * 1000 )) % 1000 ));\r
1105\r
1106 //\r
1107 // Display the round trip time\r
1108 //\r
1109 Bandwidth = MultU64x32 ( Bandwidth, 8 * 1000 * 1000 );\r
1110 Bandwidth /= NanoSeconds;\r
1111 if ( 1000 > Bandwidth ) {\r
1112 Value = (UINT32)Bandwidth;\r
1113 Print ( L"Bandwidth: %d Kbits/Sec\r\n",\r
1114 Value );\r
1115 }\r
1116 else if (( 1000 * 1000 ) > Bandwidth ) {\r
1117 Value = (UINT32)Bandwidth;\r
1118 Print ( L"Bandwidth: %d.%03d Mbits/Sec\r\n",\r
1119 Value / 1000,\r
1120 Value % 1000 );\r
1121 }\r
1122 else {\r
1123 Value = (UINT32)DivU64x32 ( Bandwidth, 1000 );\r
1124 Print ( L"Bandwidth: %d.%03d Gbits/Sec\r\n",\r
1125 Value / 1000,\r
1126 Value % 1000 );\r
1127 }\r
1128 }\r
1129 }\r
1130 break;\r
1131 }\r
1132\r
1133 //\r
1134 // Return the operation status\r
1135 //\r
1136 DBG_EXIT ( );\r
1137 return bCloseContext;\r
1138}\r
1139\r
1140\r
1141/**\r
1142 Get the next TFTP option\r
1143\r
1144 @param [in] pOption Address of a zero terminated option string\r
1145 @param [in] pEnd End of buffer address\r
1146 @param [in] ppNextOption Address to receive the address of the next\r
1147 zero terminated option string\r
1148\r
1149 @retval EFI_SUCCESS Message processed successfully\r
1150\r
1151**/\r
1152EFI_STATUS\r
1153TftpOptionGet (\r
1154 IN UINT8 * pOption,\r
1155 IN UINT8 * pEnd,\r
1156 IN UINT8 ** ppNextOption\r
1157 )\r
1158{\r
1159 UINT8 * pNextOption;\r
1160 EFI_STATUS Status;\r
1161\r
1162 //\r
1163 // Locate the end of the option\r
1164 //\r
1165 pNextOption = pOption;\r
1166 while (( pEnd > pNextOption ) && ( 0 != *pNextOption )) {\r
1167 pNextOption += 1;\r
1168 }\r
1169 if ( pEnd <= pNextOption ) {\r
1170 //\r
1171 // Error - end of buffer reached\r
1172 //\r
1173 DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
1174 "ERROR - Option without zero termination received!\r\n" ));\r
1175 Status = EFI_INVALID_PARAMETER;\r
1176 }\r
1177 else {\r
1178 //\r
1179 // Zero terminated option found\r
1180 //\r
1181 pNextOption += 1;\r
1182\r
1183 //\r
1184 // Display the zero terminated ASCII option string\r
1185 //\r
1186 DEBUG (( DEBUG_TFTP_REQUEST,\r
1187 "Option: %a\r\n",\r
1188 pOption ));\r
1189 Status = EFI_SUCCESS;\r
1190 }\r
1191\r
1192 //\r
1193 // Return the next option address\r
1194 //\r
1195 *ppNextOption = pNextOption;\r
1196\r
1197 //\r
1198 // Return the operation status\r
1199 //\r
1200 return Status;\r
1201}\r
1202\r
1203\r
1204/**\r
1205 Place an option value into the option acknowledgement\r
1206\r
1207 @param [in] pOack Option acknowledgement address\r
1208 @param [in] Value Value to translate into ASCII decimal\r
1209\r
1210 @return Option acknowledgement address\r
1211\r
1212**/\r
1213UINT8 *\r
1214TftpOptionSet (\r
1215 IN UINT8 * pOack,\r
1216 IN UINT64 Value\r
1217 )\r
1218{\r
1219 UINT64 NextValue;\r
1220\r
1221 //\r
1222 // Determine the next value\r
1223 //\r
1224 NextValue = Value / 10;\r
1225\r
1226 //\r
1227 // Supress leading zeros\r
7dc13291 1228 //\r
1229 if ( 0 != NextValue ) {\r
1230 pOack = TftpOptionSet ( pOack, NextValue );\r
1231 }\r
1232\r
1233 //\r
1234 // Output this digit\r
1235 //\r
1236 *pOack++ = (UINT8)( Value - ( NextValue * 10 ) + '0' );\r
1237\r
1238 //\r
1239 // Return the next option acknowledgement location\r
1240 //\r
1241 return pOack;\r
1242}\r
1243\r
1244\r
1245/**\r
1246 Process the TFTP request\r
1247\r
f6e5cdd5 1248 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
7dc13291 1249 @param [in] pOption Address of the first zero terminated option string\r
1250 @param [in] pEnd End of buffer address\r
1251\r
1252**/\r
1253VOID\r
1254TftpOptions (\r
1255 IN TSDT_CONNECTION_CONTEXT * pContext,\r
1256 IN UINT8 * pOption,\r
1257 IN UINT8 * pEnd\r
1258 )\r
1259{\r
1260 UINT8 * pNextOption;\r
1261 UINT8 * pOack;\r
f6e5cdd5 1262 TFTP_PACKET * pPacket;\r
7dc13291 1263 UINT8 * pTemp;\r
1264 UINT8 * pValue;\r
1265 EFI_STATUS Status;\r
1266 INT32 Value;\r
1267\r
f6e5cdd5 1268 //\r
1269 // Get a packet\r
1270 //\r
1271 pPacket = PacketGet ( pContext );\r
1272\r
7dc13291 1273 //\r
1274 // Start the OACK packet\r
1275 // Let the OACK handle the parsing errors\r
1276 // See http://tools.ietf.org/html/rfc2347\r
1277 //\r
f6e5cdd5 1278 pOack = &pPacket->TxBuffer[ 0 ];\r
7dc13291 1279 *pOack++ = 0;\r
1280 *pOack++ = TFTP_OP_OACK;\r
f6e5cdd5 1281 pPacket->TxBytes = 2;\r
1282 pPacket->BlockNumber = 0;\r
7dc13291 1283\r
1284 //\r
1285 // Walk the list of options\r
1286 //\r
1287 do {\r
1288 //\r
1289 // Get the next option, skip junk at end of message\r
1290 //\r
1291 Status = TftpOptionGet ( pOption, pEnd, &pNextOption );\r
1292 if ( !EFI_ERROR ( Status )) {\r
1293 //\r
1294 // Process the option\r
1295 //\r
1296\r
1297 //\r
1298 // blksize - See http://tools.ietf.org/html/rfc2348\r
1299 //\r
1300 pValue = pNextOption;\r
f6e5cdd5 1301 if ( 0 == strcasecmp ((char *)pOption, "blksize" )) {\r
7dc13291 1302 //\r
1303 // Get the value\r
1304 //\r
1305 Status = TftpOptionGet ( pValue, pEnd, &pNextOption );\r
1306 if ( !EFI_ERROR ( Status )) {\r
1307 //\r
1308 // Validate the block size, skip non-numeric block sizes\r
1309 //\r
1310 Status = TftpOptionValue ( pValue, &Value );\r
1311 if ( !EFI_ERROR ( Status )) {\r
1312 //\r
1313 // Propose a smaller block size if necessary\r
1314 //\r
1315 if ( Value > TFTP_MAX_BLOCK_SIZE ) {\r
1316 Value = TFTP_MAX_BLOCK_SIZE;\r
1317 }\r
1318\r
1319 //\r
1320 // Set the new block size\r
1321 //\r
1322 pContext->BlockSize = Value;\r
1323 DEBUG (( DEBUG_TFTP_REQUEST,\r
1324 "Using block size of %d bytes\r\n",\r
1325 pContext->BlockSize ));\r
1326\r
1327 //\r
1328 // Update the OACK\r
1329 //\r
1330 pTemp = pOack;\r
1331 *pOack++ = 'b';\r
1332 *pOack++ = 'l';\r
1333 *pOack++ = 'k';\r
1334 *pOack++ = 's';\r
1335 *pOack++ = 'i';\r
1336 *pOack++ = 'z';\r
1337 *pOack++ = 'e';\r
1338 *pOack++ = 0;\r
1339 pOack = TftpOptionSet ( pOack, pContext->BlockSize );\r
1340 *pOack++ = 0;\r
f6e5cdd5 1341 pPacket->TxBytes += pOack - pTemp;\r
7dc13291 1342 }\r
1343 }\r
1344 }\r
1345\r
1346 //\r
1347 // timeout - See http://tools.ietf.org/html/rfc2349\r
1348 //\r
f6e5cdd5 1349 else if ( 0 == strcasecmp ((char *)pOption, "timeout" )) {\r
7dc13291 1350 //\r
1351 // Get the value\r
1352 //\r
1353 Status = TftpOptionGet ( pValue, pEnd, &pNextOption );\r
1354 if ( !EFI_ERROR ( Status )) {\r
1355 Status = TftpOptionValue ( pValue, &Value );\r
1356 if ( !EFI_ERROR ( Status )) {\r
1357 //\r
1358 // Set the timeout value\r
1359 //\r
f6e5cdd5 1360 pContext->MaxTimeout = Value;\r
7dc13291 1361 DEBUG (( DEBUG_TFTP_REQUEST,\r
1362 "Using timeout of %d seconds\r\n",\r
f6e5cdd5 1363 pContext->MaxTimeout ));\r
7dc13291 1364\r
1365 //\r
1366 // Update the OACK\r
1367 //\r
1368 pTemp = pOack;\r
1369 *pOack++ = 't';\r
1370 *pOack++ = 'i';\r
1371 *pOack++ = 'm';\r
1372 *pOack++ = 'e';\r
1373 *pOack++ = 'o';\r
1374 *pOack++ = 'u';\r
1375 *pOack++ = 't';\r
1376 *pOack++ = 0;\r
f6e5cdd5 1377 pOack = TftpOptionSet ( pOack, pContext->MaxTimeout );\r
7dc13291 1378 *pOack++ = 0;\r
f6e5cdd5 1379 pPacket->TxBytes += pOack - pTemp;\r
7dc13291 1380 }\r
1381 }\r
1382 }\r
1383\r
1384 //\r
1385 // tsize - See http://tools.ietf.org/html/rfc2349\r
1386 //\r
f6e5cdd5 1387 else if ( 0 == strcasecmp ((char *)pOption, "tsize" )) {\r
7dc13291 1388 //\r
1389 // Get the value\r
1390 //\r
1391 Status = TftpOptionGet ( pValue, pEnd, &pNextOption );\r
1392 if ( !EFI_ERROR ( Status )) {\r
1393 Status = TftpOptionValue ( pValue, &Value );\r
1394 if ( !EFI_ERROR ( Status )) {\r
1395 //\r
1396 // Return the file size\r
1397 //\r
1398 DEBUG (( DEBUG_TFTP_REQUEST,\r
1399 "Returning file size of %Ld bytes\r\n",\r
1400 pContext->LengthInBytes ));\r
1401\r
1402 //\r
1403 // Update the OACK\r
1404 //\r
1405 pTemp = pOack;\r
1406 *pOack++ = 't';\r
1407 *pOack++ = 's';\r
1408 *pOack++ = 'i';\r
1409 *pOack++ = 'z';\r
1410 *pOack++ = 'e';\r
1411 *pOack++ = 0;\r
1412 pOack = TftpOptionSet ( pOack, pContext->LengthInBytes );\r
1413 *pOack++ = 0;\r
f6e5cdd5 1414 pPacket->TxBytes += pOack - pTemp;\r
7dc13291 1415 }\r
1416 }\r
1417 }\r
1418 else {\r
1419 //\r
1420 // Unknown option - Ignore it\r
1421 //\r
1422 DEBUG (( DEBUG_WARN | DEBUG_TFTP_REQUEST,\r
1423 "WARNING - Skipping unknown option: %a\r\n",\r
1424 pOption ));\r
1425 }\r
1426 }\r
1427\r
1428 //\r
1429 // Set the next option\r
1430 //\r
1431 pOption = pNextOption;\r
1432 } while ( pEnd > pOption );\r
f6e5cdd5 1433\r
1434 //\r
1435 // Transmit the OACK if necessary\r
1436 //\r
1437 if ( 2 < pPacket->TxBytes ) {\r
1438 PacketQueue ( pContext, pPacket );\r
1439 }\r
1440 else {\r
1441 PacketFree ( pContext, pPacket );\r
1442 }\r
7dc13291 1443}\r
1444\r
1445\r
1446/**\r
1447 Process the TFTP request\r
1448\r
1449 @param [in] pOption Address of the first zero terminated option string\r
1450 @param [in] pValue Address to receive the value\r
1451\r
1452 @retval EFI_SUCCESS Option translated into a value\r
1453\r
1454**/\r
1455EFI_STATUS\r
1456TftpOptionValue (\r
1457 IN UINT8 * pOption,\r
1458 IN INT32 * pValue\r
1459 )\r
1460{\r
1461 UINT8 Digit;\r
1462 EFI_STATUS Status;\r
1463 INT32 Value;\r
1464\r
1465 //\r
1466 // Assume success\r
1467 //\r
1468 Status = EFI_SUCCESS;\r
1469\r
1470 //\r
1471 // Walk the characters in the option\r
1472 //\r
1473 Value = 0;\r
1474 while ( 0 != *pOption ) {\r
1475 //\r
1476 // Convert the next digit to binary\r
1477 //\r
1478 Digit = *pOption++;\r
1479 if (( '0' <= Digit ) && ( '9' >= Digit )) {\r
1480 Value *= 10;\r
1481 Value += Digit - '0';\r
1482 }\r
1483 else {\r
1484 DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
1485 "ERROR - Invalid character '0x%02x' in the value\r\n",\r
1486 Digit ));\r
1487 Status = EFI_INVALID_PARAMETER;\r
1488 break;\r
1489 }\r
1490 }\r
1491\r
1492 //\r
1493 // Return the value\r
1494 //\r
1495 *pValue = Value;\r
1496\r
1497 //\r
1498 // Return the conversion status\r
1499 //\r
1500 return Status;\r
1501}\r
1502\r
1503\r
1504/**\r
1505 Process the TFTP request\r
1506\r
f6e5cdd5 1507 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
1508 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
1509 @param [in] SocketFd Socket file descriptor\r
7dc13291 1510\r
1511**/\r
1512VOID\r
1513TftpProcessRequest (\r
1514 IN TSDT_TFTP_SERVER * pTftpServer,\r
f6e5cdd5 1515 IN TSDT_CONNECTION_CONTEXT * pContext,\r
1516 IN int SocketFd\r
7dc13291 1517 )\r
1518{\r
1519 BOOLEAN bCloseContext;\r
7dc13291 1520 UINT16 Opcode;\r
7dc13291 1521\r
1522 DBG_ENTER ( );\r
1523\r
1524 //\r
1525 // Get the opcode\r
1526 //\r
f6e5cdd5 1527 Opcode = HTONS ( *(UINT16 *)&pTftpServer->RxBuffer[ 0 ]);\r
1528 DEBUG (( DEBUG_TFTP_REQUEST,\r
1529 "TFTP Opcode: 0x%08x\r\n",\r
1530 Opcode ));\r
7dc13291 1531\r
1532 //\r
1533 // Validate the parameters\r
1534 //\r
1535 bCloseContext = FALSE;\r
7dc13291 1536 switch ( Opcode ) {\r
1537 default:\r
1538 DEBUG (( DEBUG_TFTP_REQUEST,\r
1539 "ERROR - Unknown TFTP opcode: %d\r\n",\r
1540 Opcode ));\r
f6e5cdd5 1541 break;\r
1542\r
1543 case TFTP_OP_ACK:\r
1544 bCloseContext = TftpAck ( pTftpServer, pContext );\r
7dc13291 1545 break;\r
1546\r
1547 case TFTP_OP_READ_REQUEST:\r
f6e5cdd5 1548 bCloseContext = TftpRead ( pTftpServer, pContext, SocketFd );\r
7dc13291 1549 break;\r
1550\r
f6e5cdd5 1551\r
1552\r
1553 \r
7dc13291 1554 case TFTP_OP_DATA:\r
1555 if ( NULL == pContext ) {\r
f6e5cdd5 1556 if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
1557 DEBUG (( DEBUG_ERROR,\r
1558 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
1559 (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
1560 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
1561 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
1562 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
1563 htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
1564 }\r
1565 else {\r
1566 DEBUG (( DEBUG_ERROR,\r
1567 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
1568 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
1569 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
1570 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
1571 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
1572 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
1573 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
1574 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
1575 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
1576 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
1577 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
1578 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
1579 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
1580 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
1581 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
1582 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
1583 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
1584 htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
1585 }\r
7dc13291 1586 break;\r
1587 }\r
f6e5cdd5 1588 if ( 0 != pContext->PacketsInWindow ) {\r
7dc13291 1589 DEBUG (( DEBUG_ERROR,\r
1590 "ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",\r
1591 pContext ));\r
7dc13291 1592 break;\r
1593 }\r
59bc0593 1594 if ( pTftpServer->RxBytes > (ssize_t)( pContext->BlockSize + 2 + 2 )) {\r
7dc13291 1595 DEBUG (( DEBUG_ERROR,\r
1596 "ERROR - Receive data length of %d > %d bytes (maximum block size) for pContext 0x%08x\r\n",\r
1597 pTftpServer->RxBytes - 2 - 2,\r
1598 pContext->BlockSize,\r
1599 pContext ));\r
7dc13291 1600 break;\r
1601 }\r
1602 break;\r
1603\r
1604 case TFTP_OP_ERROR:\r
1605 if ( NULL == pContext ) {\r
f6e5cdd5 1606 if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
1607 DEBUG (( DEBUG_ERROR,\r
1608 "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
1609 (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
1610 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
1611 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
1612 (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
1613 htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
7dc13291 1614 }\r
1615 else {\r
f6e5cdd5 1616 DEBUG (( DEBUG_ERROR,\r
1617 "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
1618 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
1619 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
1620 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
1621 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
1622 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
1623 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
1624 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
1625 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
1626 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
1627 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
1628 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
1629 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
1630 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
1631 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
1632 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
1633 pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
1634 htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
7dc13291 1635 }\r
7dc13291 1636 }\r
f6e5cdd5 1637 break;\r
7dc13291 1638 }\r
1639\r
1640 //\r
1641 // Determine if the context should be closed\r
1642 //\r
1643 if ( bCloseContext ) {\r
1644 ContextRemove ( pTftpServer, pContext );\r
1645 }\r
1646\r
1647 DBG_EXIT ( );\r
1648}\r
1649\r
1650\r
1651/**\r
f6e5cdd5 1652 Process the read request\r
7dc13291 1653\r
f6e5cdd5 1654 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
1655 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
1656 @param [in] SocketFd Socket file descriptor\r
7dc13291 1657\r
f6e5cdd5 1658 @retval TRUE if the context should be closed\r
7dc13291 1659\r
1660**/\r
f6e5cdd5 1661BOOLEAN\r
1662TftpRead (\r
7dc13291 1663 IN TSDT_TFTP_SERVER * pTftpServer,\r
1664 IN TSDT_CONNECTION_CONTEXT * pContext,\r
f6e5cdd5 1665 IN int SocketFd\r
7dc13291 1666 )\r
1667{\r
f6e5cdd5 1668 BOOLEAN bCloseContext;\r
1669 struct stat FileStatus;\r
7dc13291 1670 UINT8 * pBuffer;\r
f6e5cdd5 1671 UINT8 * pEnd;\r
1672 UINT8 * pFileName;\r
1673 UINT8 * pMode;\r
1674 UINT8 * pOption;\r
1675 CHAR8 * pReadMode;\r
1676 UINT64 TimeStart;\r
7dc13291 1677\r
1678 DBG_ENTER ( );\r
1679\r
1680 //\r
f6e5cdd5 1681 // Log the receive time\r
7dc13291 1682 //\r
f6e5cdd5 1683 TimeStart = 0;\r
1684 if ( PcdGetBool ( Tftp_Bandwidth )) {\r
1685 TimeStart = GetPerformanceCounter ( );\r
1686 }\r
7dc13291 1687\r
1688 //\r
f6e5cdd5 1689 // Close the context if necessary\r
7dc13291 1690 //\r
f6e5cdd5 1691 bCloseContext = FALSE;\r
1692 if ( NULL != pContext ) {\r
1693 ContextRemove ( pTftpServer, pContext );\r
1694 }\r
7dc13291 1695\r
1696 //\r
f6e5cdd5 1697 // Use break instead of goto\r
7dc13291 1698 //\r
f6e5cdd5 1699 for ( ; ; ) {\r
1700 //\r
1701 // Create the connection context\r
1702 //\r
1703 pContext = ContextAdd ( pTftpServer, SocketFd );\r
1704 if ( NULL == pContext ) {\r
1705 break;\r
1706 }\r
7dc13291 1707\r
f6e5cdd5 1708 //\r
1709 // Set the start time\r
1710 //\r
1711 if ( PcdGetBool ( Tftp_Bandwidth )) {\r
1712 pContext->TimeStart = TimeStart;\r
1713 }\r
7dc13291 1714\r
f6e5cdd5 1715 //\r
1716 // Locate the mode\r
1717 //\r
1718 pBuffer = &pTftpServer->RxBuffer[ 0 ];\r
1719 pEnd = &pBuffer[ pTftpServer->RxBytes ];\r
1720 pFileName = &pBuffer[ 2 ];\r
1721 pMode = pFileName;\r
1722 while (( pEnd > pMode ) && ( 0 != *pMode )) {\r
1723 pMode += 1;\r
1724 }\r
1725 if ( pEnd <= pMode ) {\r
1726 //\r
1727 // Mode not found\r
1728 //\r
1729 DEBUG (( DEBUG_ERROR | DEBUG_RX,\r
1730 "ERROR - File mode not found\r\n" ));\r
1731 //\r
1732 // Tell the client of the error\r
1733 //\r
1734 SendError ( pContext,\r
1735 TFTP_ERROR_SEE_MSG,\r
1736 (UINT8 *)"File open mode not found" );\r
1737 break;\r
1738 }\r
1739 pMode += 1;\r
1740 DEBUG (( DEBUG_TFTP_REQUEST,\r
1741 "TFTP - FileName: %a\r\n",\r
1742 pFileName ));\r
7dc13291 1743\r
f6e5cdd5 1744 //\r
1745 // Locate the options\r
1746 //\r
1747 pOption = pMode;\r
1748 while (( pEnd > pOption ) && ( 0 != *pOption )) {\r
1749 pOption += 1;\r
1750 }\r
1751 if ( pEnd <= pOption ) {\r
1752 //\r
1753 // End of mode not found\r
1754 //\r
1755 DEBUG (( DEBUG_ERROR | DEBUG_RX,\r
1756 "ERROR - File mode not valid\r\n" ));\r
1757 //\r
1758 // Tell the client of the error\r
1759 //\r
1760 SendError ( pContext,\r
1761 TFTP_ERROR_SEE_MSG,\r
1762 (UINT8 *)"File open mode not valid" );\r
1763 break;\r
1764 }\r
1765 pOption += 1;\r
1766 DEBUG (( DEBUG_TFTP_REQUEST,\r
1767 "TFTP - Mode: %a\r\n",\r
1768 pMode ));\r
7dc13291 1769\r
f6e5cdd5 1770 //\r
1771 // Verify the mode is supported\r
1772 //\r
1773 pReadMode = "r";\r
1774 if ( 0 == strcasecmp ((char *)pMode, "octet" )) {\r
1775 //\r
1776 // Read the file as binary input\r
1777 //\r
1778 pReadMode = "rb";\r
1779 }\r
7dc13291 1780\r
f6e5cdd5 1781 //\r
1782 // Determine the file length\r
1783 //\r
1784 pContext->File = fopen ( pFileName, pReadMode );\r
1785 if (( NULL == pContext->File )\r
1786 || ( -1 == stat ( pFileName, &FileStatus ))) {\r
1787 //\r
1788 // File not found\r
1789 //\r
1790 DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
1791 ( NULL == pContext->File )\r
1792 ? "ERROR - File not found!\r\n"\r
1793 : "ERROR - Unable to determine file %a size!\r\n",\r
1794 pFileName ));\r
7dc13291 1795\r
f6e5cdd5 1796 //\r
1797 // Tell the client of the error\r
1798 //\r
1799 SendError ( pContext,\r
1800 TFTP_ERROR_NOT_FOUND,\r
1801 (UINT8 *)"File not found" );\r
1802 break;\r
1803 }\r
1804 pContext->LengthInBytes = FileStatus.st_size;\r
1805 pContext->BytesRemaining = pContext->LengthInBytes;\r
1806 pContext->BytesToSend = pContext->LengthInBytes;\r
7dc13291 1807\r
f6e5cdd5 1808 //\r
1809 // Display the file size\r
1810 //\r
1811 DEBUG_CODE_BEGIN ( );\r
1812 UINT32 Value;\r
1813\r
1814 if ( 1024 > pContext->LengthInBytes ) {\r
1815 Value = (UINT32)pContext->LengthInBytes;\r
1816 DEBUG (( DEBUG_FILE_BUFFER,\r
1817 "%a size: %d Bytes\r\n",\r
1818 pFileName,\r
1819 Value ));\r
1820 }\r
1821 else if (( 1024 * 1024 ) > pContext->LengthInBytes ) {\r
1822 Value = (UINT32)pContext->LengthInBytes;\r
1823 DEBUG (( DEBUG_FILE_BUFFER,\r
1824 "%a size: %d.%03d KiBytes (%Ld Bytes)\r\n",\r
1825 pFileName,\r
1826 Value / 1024,\r
1827 (( Value % 1024 ) * 1000 ) / 1024,\r
1828 pContext->LengthInBytes ));\r
1829 }\r
1830 else if (( 1024 * 1024 * 1024 ) > pContext->LengthInBytes ) {\r
1831 Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 );\r
1832 DEBUG (( DEBUG_FILE_BUFFER,\r
1833 "%a size: %d.%03d MiBytes (%Ld Bytes)\r\n",\r
1834 pFileName,\r
1835 Value / 1024,\r
1836 (( Value % 1024 ) * 1000 ) / 1024,\r
1837 pContext->LengthInBytes ));\r
1838 }\r
1839 else {\r
1840 Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 * 1024 );\r
1841 DEBUG (( DEBUG_FILE_BUFFER,\r
1842 "%a size: %d.%03d GiBytes (%Ld Bytes)\r\n",\r
1843 pFileName,\r
1844 Value / 1024,\r
1845 (( Value % 1024 ) * 1000 ) / 1024,\r
1846 pContext->LengthInBytes ));\r
1847 }\r
1848 DEBUG_CODE_END ( );\r
7dc13291 1849\r
f6e5cdd5 1850 //\r
1851 // Process the options\r
1852 //\r
1853 if ( pEnd > pOption ) {\r
1854 TftpOptions ( pContext, pOption, pEnd );\r
1855 }\r
1856 else {\r
1857 //\r
1858 // Skip the open ACK\r
1859 //\r
1860 pContext->BlockNumber = 1;\r
1861 }\r
7dc13291 1862\r
f6e5cdd5 1863 //\r
1864 // Send the first packet (OACK or data block)\r
1865 //\r
1866 bCloseContext = PacketFill ( pContext );\r
1867 break;\r
7dc13291 1868 }\r
1869\r
1870 //\r
f6e5cdd5 1871 // Return the close status\r
7dc13291 1872 //\r
f6e5cdd5 1873 DBG_EXIT ( );\r
1874 return bCloseContext;\r
7dc13291 1875}\r
1876\r
1877\r
1878/**\r
1879 Create the port for the TFTP server\r
1880\r
1881 This routine polls the network layer to create the TFTP port for the\r
1882 TFTP server. More than one attempt may be necessary since it may take\r
1883 some time to get the IP address and initialize the upper layers of\r
1884 the network stack.\r
1885\r
f6e5cdd5 1886 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
1887 @param [in] AddressFamily The address family to use for the conection.\r
1888 @param [in] pIndex Address of the index into the port array\r
7dc13291 1889\r
1890**/\r
1891VOID\r
f6e5cdd5 1892TftpServerSocket (\r
1893 IN TSDT_TFTP_SERVER * pTftpServer,\r
1894 IN sa_family_t AddressFamily,\r
1895 IN int * pIndex\r
7dc13291 1896 )\r
1897{\r
7dc13291 1898 int SocketStatus;\r
f6e5cdd5 1899 struct pollfd * pTftpPort;\r
1900 UINT16 TftpPort;\r
1901 union {\r
1902 struct sockaddr_in v4;\r
1903 struct sockaddr_in6 v6;\r
1904 } TftpServerAddress;\r
7dc13291 1905\r
f6e5cdd5 1906 DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerListen\r\n" ));\r
7dc13291 1907\r
1908 //\r
f6e5cdd5 1909 // Determine if the socket is already initialized\r
7dc13291 1910 //\r
f6e5cdd5 1911 if ( -1 == *pIndex ) {\r
7dc13291 1912 //\r
1913 // Attempt to create the socket for the TFTP server\r
1914 //\r
f6e5cdd5 1915 pTftpPort = &pTftpServer->TftpPort[ pTftpServer->Entries ];\r
1916 pTftpPort->fd = socket ( AddressFamily,\r
1917 SOCK_DGRAM,\r
1918 IPPROTO_UDP );\r
1919 if ( -1 != pTftpPort->fd ) {\r
1920 //\r
1921 // Initialize the poll structure\r
1922 //\r
1923 pTftpPort->events = POLLRDNORM | POLLHUP;\r
1924 pTftpPort->revents = 0;\r
1925\r
7dc13291 1926 //\r
1927 // Set the socket address\r
1928 //\r
7dc13291 1929 TftpPort = 69;\r
f6e5cdd5 1930 ZeroMem ( &TftpServerAddress, sizeof ( TftpServerAddress ));\r
1931 TftpServerAddress.v4.sin_port = htons ( TftpPort );\r
1932 if ( AF_INET == AddressFamily ) {\r
1933 TftpServerAddress.v4.sin_len = sizeof ( TftpServerAddress.v4 );\r
1934 TftpServerAddress.v4.sin_family = AF_INET;\r
1935 }\r
1936 else {\r
1937 TftpServerAddress.v6.sin6_len = sizeof ( TftpServerAddress.v6 );\r
1938 TftpServerAddress.v6.sin6_family = AF_INET6;\r
1939 }\r
7dc13291 1940\r
1941 //\r
1942 // Bind the socket to the TFTP port\r
1943 //\r
f6e5cdd5 1944 SocketStatus = bind ( pTftpPort->fd,\r
1945 (struct sockaddr *) &TftpServerAddress,\r
1946 TftpServerAddress.v6.sin6_len );\r
7dc13291 1947 if ( -1 != SocketStatus ) {\r
1948 DEBUG (( DEBUG_TFTP_PORT,\r
1949 "0x%08x: Socket bound to port %d\r\n",\r
f6e5cdd5 1950 pTftpPort->fd,\r
7dc13291 1951 TftpPort ));\r
f6e5cdd5 1952\r
1953 //\r
1954 // Account for this connection\r
1955 //\r
1956 *pIndex = pTftpServer->Entries;\r
1957 pTftpServer->Entries += 1;\r
1958 ASSERT ( DIM ( pTftpServer->TftpPort ) >= pTftpServer->Entries );\r
7dc13291 1959 }\r
1960\r
1961 //\r
1962 // Release the socket if necessary\r
1963 //\r
1964 if ( -1 == SocketStatus ) {\r
f6e5cdd5 1965 close ( pTftpPort->fd );\r
1966 pTftpPort->fd = -1;\r
7dc13291 1967 }\r
1968 }\r
f6e5cdd5 1969 }\r
7dc13291 1970\r
f6e5cdd5 1971 DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerListen\r\n" ));\r
7dc13291 1972}\r
1973\r
1974\r
1975/**\r
f6e5cdd5 1976 Update the window due to the ACK\r
7dc13291 1977\r
f6e5cdd5 1978 @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
1979 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
1980 @param [in] pPacket Address of a ::TFTP_PACKET structure\r
7dc13291 1981\r
1982**/\r
f6e5cdd5 1983VOID\r
1984WindowAck (\r
1985 IN TSDT_TFTP_SERVER * pTftpServer,\r
1986 IN TSDT_CONNECTION_CONTEXT * pContext,\r
1987 IN TFTP_PACKET * pPacket\r
7dc13291 1988 )\r
1989{\r
f6e5cdd5 1990 if ( PcdGetBool ( Tftp_HighSpeed )) {\r
1991 UINT64 DeltaTime;\r
1992 UINT64 NanoSeconds;\r
7dc13291 1993\r
f6e5cdd5 1994 DBG_ENTER ( );\r
7dc13291 1995\r
7dc13291 1996 //\r
f6e5cdd5 1997 // Compute the round trip time\r
7dc13291 1998 //\r
f6e5cdd5 1999 if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
2000 DeltaTime = pTftpServer->RxTime - pPacket->TxTime;\r
7dc13291 2001 }\r
2002 else {\r
f6e5cdd5 2003 DeltaTime = pPacket->TxTime - pTftpServer->RxTime;\r
7dc13291 2004 }\r
7dc13291 2005\r
f6e5cdd5 2006 //\r
2007 // Adjust the round trip time\r
2008 //\r
2009 NanoSeconds = GetTimeInNanoSecond ( DeltaTime );\r
2010 DeltaTime = RShiftU64 ( pContext->Rtt2x, ACK_SHIFT );\r
2011 pContext->Rtt2x += NanoSeconds + NanoSeconds - DeltaTime;\r
2012 if ( pContext->Rtt2x > pContext->MaxTimeout ) {\r
2013 pContext->Rtt2x = pContext->MaxTimeout;\r
2014 }\r
2015\r
2016 //\r
2017 // Account for the ACK\r
2018 //\r
2019 if ( pContext->WindowSize < MAX_PACKETS ) {\r
2020 pContext->AckCount -= 1;\r
2021 if ( 0 == pContext->AckCount ) {\r
2022 //\r
2023 // Increase the window\r
2024 //\r
2025 pContext->WindowSize += 1;\r
2026\r
2027 //\r
2028 // Set the ACK count\r
2029 //\r
2030 if ( pContext->WindowSize < pContext->Threshold ) {\r
2031 pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );\r
2032 }\r
2033 else {\r
2034 pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;\r
2035 }\r
2036\r
2037 //\r
2038 // Display the round trip time\r
2039 //\r
2040 DEBUG_CODE_BEGIN ( );\r
2041 UINT32 Value;\r
2042 \r
2043 DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );\r
2044 if ( 1000 > DeltaTime ) {\r
2045 DEBUG (( DEBUG_WINDOW,\r
2046 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",\r
2047 pContext->WindowSize,\r
2048 pContext->Threshold,\r
2049 pContext->AckCount,\r
2050 DeltaTime ));\r
2051 }\r
2052 else if (( 1000 * 1000 ) > DeltaTime ) {\r
2053 Value = (UINT32)DeltaTime;\r
2054 DEBUG (( DEBUG_WINDOW,\r
2055 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",\r
2056 pContext->WindowSize,\r
2057 pContext->Threshold,\r
2058 pContext->AckCount,\r
2059 Value / 1000,\r
2060 Value % 1000 ));\r
2061 }\r
2062 else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {\r
2063 Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );\r
2064 DEBUG (( DEBUG_WINDOW,\r
2065 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",\r
2066 pContext->WindowSize,\r
2067 pContext->Threshold,\r
2068 pContext->AckCount,\r
2069 Value / 1000,\r
2070 Value % 1000 ));\r
2071 }\r
2072 else {\r
2073 Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );\r
2074 DEBUG (( DEBUG_WINDOW,\r
2075 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",\r
2076 pContext->WindowSize,\r
2077 pContext->Threshold,\r
2078 pContext->AckCount,\r
2079 Value / 1000,\r
2080 Value % 1000 ));\r
2081 }\r
2082 DEBUG_CODE_END ( );\r
2083 }\r
2084 }\r
2085\r
2086 DBG_EXIT ( );\r
2087 }\r
7dc13291 2088}\r
2089\r
2090\r
2091/**\r
f6e5cdd5 2092 A timeout has occurred, close the window\r
7dc13291 2093\r
f6e5cdd5 2094 @param [in] pContext Address of a ::TSDT_CONNECTION_CONTEXT structure\r
7dc13291 2095\r
2096**/\r
f6e5cdd5 2097VOID\r
2098WindowTimeout (\r
2099 IN TSDT_CONNECTION_CONTEXT * pContext\r
7dc13291 2100 )\r
2101{\r
f6e5cdd5 2102 if ( PcdGetBool ( Tftp_HighSpeed )) {\r
2103 TFTP_PACKET * pPacket;\r
7dc13291 2104\r
f6e5cdd5 2105 DBG_ENTER ( );\r
7dc13291 2106\r
7dc13291 2107 //\r
f6e5cdd5 2108 // Set the threshold at half the previous window size\r
7dc13291 2109 //\r
f6e5cdd5 2110 pContext->Threshold = ( pContext->WindowSize + 1 ) >> 1;\r
7dc13291 2111\r
f6e5cdd5 2112 //\r
2113 // Close the transmit window\r
2114 //\r
2115 pContext->WindowSize = 1;\r
2116 pContext->PacketsInWindow = 0;\r
2117\r
2118 //\r
2119 // Double the round trip time\r
2120 //\r
2121 pContext->Rtt2x = LShiftU64 ( pContext->Rtt2x, 1 );\r
2122 if ( pContext->Rtt2x > pContext->MaxTimeout ) {\r
2123 pContext->Rtt2x = pContext->MaxTimeout;\r
2124 }\r
2125\r
2126 //\r
2127 // Set the ACK count\r
2128 //\r
2129 if ( pContext->WindowSize < pContext->Threshold ) {\r
2130 pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );\r
7dc13291 2131 }\r
2132 else {\r
f6e5cdd5 2133 pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;\r
7dc13291 2134 }\r
7dc13291 2135\r
f6e5cdd5 2136 //\r
2137 // Display the round trip time\r
2138 //\r
2139 DEBUG_CODE_BEGIN ( );\r
2140 UINT64 DeltaTime;\r
2141 UINT32 Value;\r
2142 \r
2143 DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );\r
2144 if ( 1000 > DeltaTime ) {\r
2145 DEBUG (( DEBUG_WINDOW,\r
2146 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",\r
2147 pContext->WindowSize,\r
2148 pContext->Threshold,\r
2149 pContext->AckCount,\r
2150 DeltaTime ));\r
2151 }\r
2152 else if (( 1000 * 1000 ) > DeltaTime ) {\r
2153 Value = (UINT32)DeltaTime;\r
2154 DEBUG (( DEBUG_WINDOW,\r
2155 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",\r
2156 pContext->WindowSize,\r
2157 pContext->Threshold,\r
2158 pContext->AckCount,\r
2159 Value / 1000,\r
2160 Value % 1000 ));\r
2161 }\r
2162 else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {\r
2163 Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );\r
2164 DEBUG (( DEBUG_WINDOW,\r
2165 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",\r
2166 pContext->WindowSize,\r
2167 pContext->Threshold,\r
2168 pContext->AckCount,\r
2169 Value / 1000,\r
2170 Value % 1000 ));\r
2171 }\r
2172 else {\r
2173 Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );\r
2174 DEBUG (( DEBUG_WINDOW,\r
2175 "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",\r
2176 pContext->WindowSize,\r
2177 pContext->Threshold,\r
2178 pContext->AckCount,\r
2179 Value / 1000,\r
2180 Value % 1000 ));\r
2181 }\r
2182 DEBUG_CODE_END ( );\r
7dc13291 2183\r
f6e5cdd5 2184 //\r
2185 // Retransmit the first packet in the window\r
2186 //\r
2187 pPacket = pContext->pTxHead;\r
2188 if ( NULL != pPacket ) {\r
2189 PacketTx ( pContext, pPacket );\r
2190 }\r
2191 \r
2192 DBG_EXIT ( );\r
7dc13291 2193 }\r
7dc13291 2194}\r
2195\r
2196\r
2197/**\r
2198 Entry point for the TFTP server application.\r
2199\r
2200 @param [in] Argc The number of arguments\r
2201 @param [in] Argv The argument value array\r
2202\r
2203 @retval 0 The application exited normally.\r
2204 @retval Other An error occurred.\r
2205**/\r
2206int\r
2207main (\r
2208 IN int Argc,\r
2209 IN char **Argv\r
2210 )\r
2211{\r
f6e5cdd5 2212 UINTN Index;\r
7dc13291 2213 TSDT_TFTP_SERVER * pTftpServer;\r
2214 EFI_STATUS Status;\r
f6e5cdd5 2215 UINT64 TriggerTime;\r
7dc13291 2216\r
2217 //\r
f6e5cdd5 2218 // Get the performance counter characteristics\r
7dc13291 2219 //\r
2220 pTftpServer = &mTftpServer;\r
f6e5cdd5 2221 if ( PcdGetBool ( Tftp_HighSpeed )\r
2222 || PcdGetBool ( Tftp_Bandwidth )) {\r
2223 pTftpServer->ClockFrequency = GetPerformanceCounterProperties ( &pTftpServer->Time1,\r
2224 &pTftpServer->Time2 );\r
2225 }\r
2226\r
2227 //\r
2228 // Create a timer event to start TFTP port\r
2229 //\r
7dc13291 2230 Status = gBS->CreateEvent ( EVT_TIMER,\r
2231 TPL_TFTP_SERVER,\r
2232 NULL,\r
2233 NULL,\r
2234 &pTftpServer->TimerEvent );\r
2235 if ( !EFI_ERROR ( Status )) {\r
f6e5cdd5 2236 //\r
2237 // Compute the poll interval\r
2238 //\r
2239 TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );\r
2240 Status = gBS->SetTimer ( pTftpServer->TimerEvent,\r
2241 TimerPeriodic,\r
2242 TriggerTime );\r
7dc13291 2243 if ( !EFI_ERROR ( Status )) {\r
f6e5cdd5 2244 DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));\r
2245\r
7dc13291 2246 //\r
2247 // Run the TFTP server forever\r
2248 //\r
f6e5cdd5 2249 pTftpServer->Udpv4Index = -1;\r
2250 pTftpServer->Udpv6Index = -1;\r
2251 do {\r
7dc13291 2252 //\r
2253 // Poll the network layer to create the TFTP port\r
2254 // for the tftp server. More than one attempt may\r
2255 // be necessary since it may take some time to get\r
2256 // the IP address and initialize the upper layers\r
2257 // of the network stack.\r
2258 //\r
f6e5cdd5 2259 if ( DIM ( pTftpServer->TftpPort ) != pTftpServer->Entries ) {\r
2260 do {\r
2261 //\r
2262 // Wait a while before polling for a connection\r
2263 //\r
2264 if ( EFI_SUCCESS != gBS->CheckEvent ( pTftpServer->TimerEvent )) {\r
2265 if ( 0 == pTftpServer->Entries ) {\r
2266 break;\r
2267 }\r
2268 gBS->WaitForEvent ( 1, &pTftpServer->TimerEvent, &Index );\r
2269 }\r
2270\r
2271 //\r
2272 // Poll for a network connection\r
2273 //\r
2274 TftpServerSocket ( pTftpServer,\r
2275 AF_INET,\r
2276 &pTftpServer->Udpv4Index );\r
2277 TftpServerSocket ( pTftpServer,\r
2278 AF_INET6,\r
2279 &pTftpServer->Udpv6Index );\r
2280 } while ( 0 == pTftpServer->Entries );\r
2281 }\r
7dc13291 2282\r
2283 //\r
2284 // Poll the socket for activity\r
2285 //\r
2286 do {\r
2287 SocketPoll ( pTftpServer );\r
7dc13291 2288\r
f6e5cdd5 2289 //\r
2290 // Normal TFTP lets the client request the retransmit by\r
2291 // sending another ACK for the previous packet\r
2292 //\r
2293 if ( PcdGetBool ( Tftp_HighSpeed )) {\r
2294 UINT64 CurrentTime;\r
2295 UINT64 ElapsedTime;\r
2296 TSDT_CONNECTION_CONTEXT * pContext;\r
2297 TFTP_PACKET * pPacket;\r
2298\r
2299 //\r
2300 // High speed TFTP uses an agressive retransmit to\r
2301 // get the TFTP client moving again when the ACK or\r
2302 // previous data packet was lost.\r
2303 //\r
2304 // Get the current time\r
2305 //\r
2306 CurrentTime = GetPerformanceCounter ( );\r
2307\r
2308 //\r
2309 // Walk the list of contexts\r
2310 //\r
2311 pContext = pTftpServer->pContextList;\r
2312 while ( NULL != pContext )\r
2313 {\r
2314 //\r
2315 // Check for a transmit timeout\r
2316 //\r
2317 pPacket = pContext->pTxHead;\r
2318 if ( NULL != pPacket ) {\r
2319 //\r
2320 // Compute the elapsed time\r
2321 //\r
2322 if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
2323 ElapsedTime = CurrentTime - pPacket->TxTime;\r
2324 }\r
2325 else {\r
2326 ElapsedTime = pPacket->TxTime - CurrentTime;\r
2327 }\r
2328 ElapsedTime = GetTimeInNanoSecond ( ElapsedTime );\r
2329\r
2330 //\r
2331 // Determine if a retransmission is necessary\r
2332 //\r
2333 if ( ElapsedTime >= pContext->Rtt2x ) {\r
2334 DEBUG (( DEBUG_WINDOW,\r
2335 "0x%08x: Context TX timeout for packet 0x%08x, Window: %d\r\n",\r
2336 pContext,\r
2337 pPacket,\r
2338 pContext->WindowSize ));\r
2339 WindowTimeout ( pContext );\r
2340 }\r
2341 }\r
2342\r
2343 //\r
2344 // Set the next context\r
2345 //\r
2346 pContext = pContext->pNext;\r
2347 }\r
2348 }\r
2349 } while ( DIM ( pTftpServer->TftpPort ) == pTftpServer->Entries );\r
2350 } while ( !mbTftpServerExit );\r
7dc13291 2351\r
2352 //\r
2353 // Done with the timer event\r
2354 //\r
f6e5cdd5 2355 gBS->SetTimer ( pTftpServer->TimerEvent,\r
2356 TimerCancel,\r
2357 0 );\r
7dc13291 2358 }\r
f6e5cdd5 2359 gBS->CloseEvent ( pTftpServer->TimerEvent );\r
7dc13291 2360 }\r
2361\r
2362 //\r
2363 // Return the final status\r
2364 //\r
2365 DBG_EXIT_STATUS ( Status );\r
2366 return Status;\r
2367}\r