]> git.proxmox.com Git - mirror_edk2.git/blame - StdLib/EfiSocketLib/Tcp4.c
Only use ports with a network connection (media present) when connecting to a remote...
[mirror_edk2.git] / StdLib / EfiSocketLib / Tcp4.c
CommitLineData
d7ce7006 1/** @file\r
2 Implement the TCP4 driver support for the socket layer.\r
3\r
4 Copyright (c) 2011, 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
a88c3163 13\r
14 \section ConnectionManagement Connection Management\r
15 \r
16 The ::EslTcp4Listen routine initially places the SOCK_STREAM or\r
17 SOCK_SEQPACKET socket into a listen state. When a remote machine\r
18 makes a connection to the socket, the TCPv4 network layer calls\r
19 ::EslTcp4ListenComplete to complete the connection processing.\r
20 EslTcp4ListenComplete manages the connections by placing them in\r
21 FIFO order in a queue to be serviced by the application. When the\r
22 number of connections exceeds the backlog (ESL_SOCKET::MaxFifoDepth),\r
23 the new connection is closed. Eventually, the application indirectly\r
24 calls ::EslTcp4Accept to remove the next connection from the queue\r
25 and get the associated socket.\r
26\r
d7ce7006 27**/\r
28\r
29#include "Socket.h"\r
30\r
31\r
a88c3163 32/**\r
33 Attempt to connect to a remote TCP port\r
34\r
35 This routine starts the connection processing for a SOCK_STREAM\r
36 or SOCK_SEQPAKCET socket using the TCPv4 network layer. It\r
37 configures the local TCPv4 connection point and then attempts to\r
38 connect to a remote system. Upon completion, the\r
39 ::EslTcp4ConnectComplete routine gets called with the connection\r
40 status.\r
41\r
42 This routine is called by ::EslSocketConnect to initiate the TCPv4\r
43 network specific connect operations. The connection processing is\r
44 initiated by this routine and finished by ::EslTcp4ConnectComplete.\r
45 This pair of routines walks through the list of local TCPv4\r
46 connection points until a connection to the remote system is\r
47 made.\r
48\r
49 @param [in] pSocket Address of an ::ESL_SOCKET structure.\r
50\r
51 @retval EFI_SUCCESS The connection was successfully established.\r
52 @retval EFI_NOT_READY The connection is in progress, call this routine again.\r
53 @retval Others The connection attempt failed.\r
54\r
55 **/\r
56EFI_STATUS\r
57EslTcp4ConnectStart (\r
58 IN ESL_SOCKET * pSocket\r
59 );\r
60\r
61\r
62/**\r
63 Process the connection attempt\r
64\r
65 A system has initiated a connection attempt with a socket in the\r
66 listen state. Attempt to complete the connection.\r
67\r
68 The TCPv4 layer calls this routine when a connection is made to\r
69 the socket in the listen state. See the\r
70 \ref ConnectionManagement section.\r
71\r
72 @param [in] Event The listen completion event\r
73\r
74 @param [in] pPort Address of an ::ESL_PORT structure.\r
75\r
76**/\r
77VOID\r
78EslTcp4ListenComplete (\r
79 IN EFI_EVENT Event,\r
80 IN ESL_PORT * pPort\r
81 );\r
82\r
83\r
d7ce7006 84/**\r
85 Accept a network connection.\r
86\r
a88c3163 87 This routine waits for a network connection to the socket and\r
88 returns the remote network address to the caller if requested.\r
89\r
90 This routine is called by ::EslSocketAccept to handle the TCPv4 protocol\r
91 specific accept operations for SOCK_STREAM and SOCK_SEQPACKET sockets.\r
92 See the \ref ConnectionManagement section.\r
d7ce7006 93\r
a88c3163 94 @param [in] pSocket Address of an ::ESL_SOCKET structure.\r
d7ce7006 95\r
96 @param [in] pSockAddr Address of a buffer to receive the remote\r
97 network address.\r
98\r
99 @param [in, out] pSockAddrLength Length in bytes of the address buffer.\r
100 On output specifies the length of the\r
101 remote network address.\r
102\r
103 @retval EFI_SUCCESS Remote address is available\r
104 @retval Others Remote address not available\r
105\r
106 **/\r
107EFI_STATUS\r
a88c3163 108EslTcp4Accept (\r
109 IN ESL_SOCKET * pSocket,\r
d7ce7006 110 IN struct sockaddr * pSockAddr,\r
111 IN OUT socklen_t * pSockAddrLength\r
112 )\r
113{\r
a88c3163 114 ESL_PORT * pPort;\r
d7ce7006 115 struct sockaddr_in * pRemoteAddress;\r
a88c3163 116 ESL_TCP4_CONTEXT * pTcp4;\r
d7ce7006 117 UINT32 RemoteAddress;\r
118 EFI_STATUS Status;\r
119\r
120 DBG_ENTER ( );\r
121\r
122 //\r
123 // Validate the socket length\r
124 //\r
125 pRemoteAddress = (struct sockaddr_in *) pSockAddr;\r
126 if (( NULL == pSockAddrLength )\r
127 || ( sizeof ( *pRemoteAddress ) > *pSockAddrLength )) {\r
128 //\r
129 // Invalid socket address\r
130 //\r
131 Status = EFI_INVALID_PARAMETER;\r
132 pSocket->errno = EINVAL;\r
133 DEBUG (( DEBUG_ACCEPT,\r
134 "ERROR - Invalid address length\r\n" ));\r
135 }\r
136 else {\r
137 //\r
138 // Assume success\r
139 //\r
140 Status = EFI_SUCCESS;\r
141\r
142 //\r
143 // Locate the address context\r
144 //\r
145 pPort = pSocket->pPortList;\r
146 pTcp4 = &pPort->Context.Tcp4;\r
147\r
148 //\r
149 // Fill-in the remote address structure\r
150 //\r
151 ZeroMem ( pRemoteAddress, sizeof ( *pRemoteAddress ));\r
152 pRemoteAddress->sin_len = sizeof ( *pRemoteAddress );\r
153 pRemoteAddress->sin_family = AF_INET;\r
154 pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort );\r
155 RemoteAddress = pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3];\r
156 RemoteAddress <<= 8;\r
157 RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2];\r
158 RemoteAddress <<= 8;\r
159 RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1];\r
160 RemoteAddress <<= 8;\r
161 RemoteAddress |= pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0];\r
162 pRemoteAddress->sin_addr.s_addr = RemoteAddress;\r
163 }\r
164\r
165 //\r
166 // Return the operation status\r
167 //\r
168 DBG_EXIT_STATUS ( Status );\r
169 return Status;\r
170}\r
171\r
172\r
173/**\r
a88c3163 174 Process the remote connection completion event.\r
d7ce7006 175\r
a88c3163 176 This routine handles the completion of a connection attempt. It\r
177 releases the port (TCPv4 adapter connection) in the case of an\r
d7ce7006 178 error and start a connection attempt on the next port. If the\r
a88c3163 179 connection attempt was successful then this routine releases all\r
180 of the other ports.\r
d7ce7006 181\r
a88c3163 182 This routine is called by the TCPv4 layer when a connect request\r
183 completes. It sets the ESL_SOCKET::bConnected flag to notify the\r
184 ::EslTcp4ConnectComplete routine that the connection is available.\r
185 The flag is set when the connection is established or no more ports\r
186 exist in the list. The connection status is passed via\r
187 ESL_SOCKET::ConnectStatus.\r
d7ce7006 188\r
a88c3163 189 @param [in] Event The connect completion event\r
190\r
191 @param [in] pPort Address of an ::ESL_PORT structure.\r
d7ce7006 192\r
193**/\r
194VOID\r
a88c3163 195EslTcp4ConnectComplete (\r
d7ce7006 196 IN EFI_EVENT Event,\r
a88c3163 197 IN ESL_PORT * pPort\r
d7ce7006 198 )\r
199{\r
200 BOOLEAN bRemoveFirstPort;\r
201 BOOLEAN bRemovePorts;\r
a88c3163 202 ESL_PORT * pNextPort;\r
203 ESL_SOCKET * pSocket;\r
204 ESL_TCP4_CONTEXT * pTcp4;\r
d7ce7006 205 EFI_STATUS Status;\r
206\r
207 DBG_ENTER ( );\r
208\r
209 //\r
210 // Locate the TCP context\r
211 //\r
212 pSocket = pPort->pSocket;\r
213 pTcp4 = &pPort->Context.Tcp4;\r
214\r
215 //\r
216 // Get the connection status\r
217 //\r
218 bRemoveFirstPort = FALSE;\r
219 bRemovePorts = FALSE;\r
220 Status = pTcp4->ConnectToken.CompletionToken.Status;\r
221 pSocket->ConnectStatus = Status;\r
222 if ( !EFI_ERROR ( Status )) {\r
223 //\r
224 // The connection was successful\r
225 //\r
226 DEBUG (( DEBUG_CONNECT,\r
227 "0x%08x: Port connected to %d.%d.%d.%d:%d\r\n",\r
228 pPort,\r
a88c3163 229 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0],\r
230 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1],\r
231 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2],\r
232 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3],\r
d7ce7006 233 pTcp4->ConfigData.AccessPoint.RemotePort ));\r
234\r
235 //\r
236 // Remove the rest of the ports\r
237 //\r
238 bRemovePorts = TRUE;\r
239 }\r
240 else {\r
241 //\r
242 // The connection failed\r
243 //\r
244 DEBUG (( DEBUG_CONNECT,\r
245 "0x%08x: Port connection to %d.%d.%d.%d:%d failed, Status: %r\r\n",\r
246 pPort,\r
a88c3163 247 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0],\r
248 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1],\r
249 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2],\r
250 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3],\r
d7ce7006 251 pTcp4->ConfigData.AccessPoint.RemotePort,\r
252 Status ));\r
253\r
254 //\r
255 // Close the current port\r
256 //\r
a88c3163 257 Status = EslSocketPortClose ( pPort );\r
d7ce7006 258 if ( !EFI_ERROR ( Status )) {\r
259 DEBUG (( DEBUG_CONNECT,\r
260 "0x%08x: Port closed\r\n",\r
261 pPort ));\r
262 }\r
263 else {\r
264 DEBUG (( DEBUG_CONNECT,\r
265 "ERROR - Failed to close port 0x%08x, Status: %r\r\n",\r
266 pPort,\r
267 Status ));\r
268 }\r
269\r
270 //\r
271 // Try to connect using the next port\r
272 //\r
a88c3163 273 Status = EslTcp4ConnectStart ( pSocket );\r
d7ce7006 274 if ( EFI_NOT_READY != Status ) {\r
275 pSocket->ConnectStatus = Status;\r
276 bRemoveFirstPort = TRUE;\r
277 }\r
278 }\r
279\r
280 //\r
281 // Remove the ports if necessary\r
282 //\r
283 if ( bRemoveFirstPort || bRemovePorts ) {\r
284 //\r
285 // Remove the first port if necessary\r
286 //\r
287 pPort = pSocket->pPortList;\r
288 if (( !bRemoveFirstPort ) && ( NULL != pPort )) {\r
289 pPort = pPort->pLinkSocket;\r
290 }\r
291\r
292 //\r
293 // Remove the rest of the list\r
294 //\r
295 while ( NULL != pPort ) {\r
296 pNextPort = pPort->pLinkSocket;\r
a88c3163 297 EslSocketPortClose ( pPort );\r
d7ce7006 298 if ( !EFI_ERROR ( Status )) {\r
299 DEBUG (( DEBUG_CONNECT,\r
300 "0x%08x: Port closed\r\n",\r
301 pPort ));\r
302 }\r
303 else {\r
304 DEBUG (( DEBUG_CONNECT,\r
305 "ERROR - Failed to close port 0x%08x, Status: %r\r\n",\r
306 pPort,\r
307 Status ));\r
308 }\r
309 pPort = pNextPort;\r
310 }\r
311\r
312 //\r
313 // Notify the poll routine\r
314 //\r
315 pSocket->bConnected = TRUE;\r
316 }\r
317\r
318 DBG_EXIT ( );\r
319}\r
320\r
321\r
322/**\r
323 Poll for completion of the connection attempt.\r
324\r
a88c3163 325 This routine polls the ESL_SOCKET::bConnected flag to determine\r
326 when the connection attempt is complete.\r
327\r
328 This routine is called from ::EslSocketConnect to determine when\r
329 the connection is complete. The ESL_SOCKET::bConnected flag is\r
330 set by ::EslTcp4ConnectComplete when the TCPv4 layer establishes\r
331 a connection or runs out of local network adapters. This routine\r
332 gets the connection status from ESL_SOCKET::ConnectStatus.\r
d7ce7006 333\r
a88c3163 334 @param [in] pSocket Address of an ::ESL_SOCKET structure.\r
d7ce7006 335\r
336 @retval EFI_SUCCESS The connection was successfully established.\r
337 @retval EFI_NOT_READY The connection is in progress, call this routine again.\r
338 @retval Others The connection attempt failed.\r
339\r
340 **/\r
341EFI_STATUS\r
a88c3163 342EslTcp4ConnectPoll (\r
343 IN ESL_SOCKET * pSocket\r
d7ce7006 344 )\r
345{\r
346 EFI_STATUS Status;\r
347\r
348 DBG_ENTER ( );\r
349\r
350 //\r
351 // Determine if the connection is complete\r
352 //\r
353 if ( !pSocket->bConnected ) {\r
354 //\r
355 // Not connected\r
356 //\r
357 pSocket->errno = EAGAIN;\r
358 Status = EFI_NOT_READY;\r
359 }\r
360 else {\r
361 //\r
362 // The connection processing is complete\r
363 //\r
364 pSocket->bConnected = FALSE;\r
365\r
366 //\r
367 // Translate the connection status\r
368 //\r
369 Status = pSocket->ConnectStatus;\r
370 switch ( Status ) {\r
371 default:\r
372 case EFI_DEVICE_ERROR:\r
373 pSocket->errno = EIO;\r
374 break;\r
375\r
376 case EFI_ABORTED:\r
377 pSocket->errno = ECONNREFUSED;\r
378 break;\r
379\r
380 case EFI_INVALID_PARAMETER:\r
381 pSocket->errno = EINVAL;\r
382 break;\r
383\r
384 case EFI_NO_MAPPING:\r
385 case EFI_NO_RESPONSE:\r
386 pSocket->errno = EHOSTUNREACH;\r
387 break;\r
388\r
389 case EFI_NO_MEDIA:\r
390 pSocket->errno = ENETDOWN;\r
391 break;\r
392\r
393 case EFI_OUT_OF_RESOURCES:\r
394 pSocket->errno = ENOMEM;\r
395 break;\r
396\r
397 case EFI_SUCCESS:\r
398 pSocket->errno = 0;\r
399 pSocket->bConfigured = TRUE;\r
400 break;\r
401\r
402 case EFI_TIMEOUT:\r
403 pSocket->errno = ETIMEDOUT;\r
404 break;\r
405\r
406 case EFI_UNSUPPORTED:\r
407 pSocket->errno = ENOTSUP;\r
408 break;\r
409\r
410 case 0x80000069:\r
411 pSocket->errno = ECONNRESET;\r
412 break;\r
413 }\r
414 }\r
415\r
416 //\r
417 // Return the initialization status\r
418 //\r
419 DBG_EXIT_STATUS ( Status );\r
420 return Status;\r
421}\r
422\r
423\r
424/**\r
a88c3163 425 Attempt to connect to a remote TCP port\r
d7ce7006 426\r
a88c3163 427 This routine starts the connection processing for a SOCK_STREAM\r
428 or SOCK_SEQPAKCET socket using the TCPv4 network layer. It\r
429 configures the local TCPv4 connection point and then attempts to\r
430 connect to a remote system. Upon completion, the\r
431 ::EslTcp4ConnectComplete routine gets called with the connection\r
432 status.\r
d7ce7006 433\r
a88c3163 434 This routine is called by ::EslSocketConnect to initiate the TCPv4\r
435 network specific connect operations. The connection processing is\r
436 initiated by this routine and finished by ::EslTcp4ConnectComplete.\r
437 This pair of routines walks through the list of local TCPv4\r
438 connection points until a connection to the remote system is\r
439 made.\r
440\r
441 @param [in] pSocket Address of an ::ESL_SOCKET structure.\r
d7ce7006 442\r
d7ce7006 443 @retval EFI_SUCCESS The connection was successfully established.\r
444 @retval EFI_NOT_READY The connection is in progress, call this routine again.\r
445 @retval Others The connection attempt failed.\r
446\r
447 **/\r
448EFI_STATUS\r
a88c3163 449EslTcp4ConnectStart (\r
450 IN ESL_SOCKET * pSocket\r
d7ce7006 451 )\r
452{\r
a88c3163 453 ESL_PORT * pPort;\r
454 ESL_TCP4_CONTEXT * pTcp4;\r
455 EFI_TCP4_PROTOCOL * pTcp4Protocol;\r
d6f19057 456 EFI_SIMPLE_NETWORK_MODE SnpModeData;\r
d7ce7006 457 EFI_STATUS Status;\r
458\r
459 DBG_ENTER ( );\r
a88c3163 460 \r
d7ce7006 461 //\r
a88c3163 462 // Determine if any more local adapters are available\r
d7ce7006 463 //\r
a88c3163 464 pPort = pSocket->pPortList;\r
465 if ( NULL != pPort ) {\r
d7ce7006 466 //\r
a88c3163 467 // Configure the port\r
d7ce7006 468 //\r
a88c3163 469 pTcp4 = &pPort->Context.Tcp4;\r
470 pTcp4->ConfigData.AccessPoint.ActiveFlag = TRUE;\r
471 pTcp4->ConfigData.TimeToLive = 255;\r
472 pTcp4Protocol = pPort->pProtocol.TCPv4;\r
473 Status = pTcp4Protocol->Configure ( pTcp4Protocol,\r
474 &pTcp4->ConfigData );\r
475 if ( EFI_ERROR ( Status )) {\r
476 DEBUG (( DEBUG_CONNECT,\r
477 "ERROR - Failed to configure the Tcp4 port, Status: %r\r\n",\r
478 Status ));\r
479 switch ( Status ) {\r
480 case EFI_ACCESS_DENIED:\r
481 pSocket->errno = EACCES;\r
482 break;\r
483 \r
484 default:\r
485 case EFI_DEVICE_ERROR:\r
486 pSocket->errno = EIO;\r
487 break;\r
488 \r
489 case EFI_INVALID_PARAMETER:\r
490 pSocket->errno = EADDRNOTAVAIL;\r
491 break;\r
492 \r
493 case EFI_NO_MAPPING:\r
494 pSocket->errno = EAFNOSUPPORT;\r
495 break;\r
496 \r
497 case EFI_OUT_OF_RESOURCES:\r
498 pSocket->errno = ENOBUFS;\r
499 break;\r
500 \r
501 case EFI_UNSUPPORTED:\r
502 pSocket->errno = EOPNOTSUPP;\r
503 break;\r
504 }\r
d7ce7006 505 }\r
506 else {\r
a88c3163 507 DEBUG (( DEBUG_CONNECT,\r
508 "0x%08x: Port configured\r\n",\r
509 pPort ));\r
510 pPort->bConfigured = TRUE;\r
d7ce7006 511\r
a88c3163 512 //\r
d6f19057 513 // Verify the port connection\r
a88c3163 514 //\r
d6f19057 515 Status = pTcp4Protocol->GetModeData ( pTcp4Protocol,\r
516 NULL,\r
517 NULL,\r
518 NULL,\r
519 NULL,\r
520 &SnpModeData );\r
521 if ( !EFI_ERROR ( Status )) {\r
522 if ( SnpModeData.MediaPresentSupported\r
523 && ( !SnpModeData.MediaPresent )) {\r
524 //\r
525 // Port is not connected to the network\r
526 //\r
3a2fc878 527 pTcp4->ConnectToken.CompletionToken.Status = EFI_NO_MEDIA;\r
528\r
529 //\r
530 // Continue with the next port\r
531 //\r
532 gBS->CheckEvent ( pTcp4->ConnectToken.CompletionToken.Event );\r
533 gBS->SignalEvent ( pTcp4->ConnectToken.CompletionToken.Event );\r
534\r
535 //\r
536 // Connection in progress\r
537 //\r
538 Status = EFI_SUCCESS;\r
d6f19057 539 }\r
540 else {\r
541 //\r
542 // Attempt the connection to the remote system\r
543 //\r
544 Status = pTcp4Protocol->Connect ( pTcp4Protocol,\r
545 &pTcp4->ConnectToken );\r
546 }\r
547 }\r
a88c3163 548 if ( !EFI_ERROR ( Status )) {\r
549 //\r
550 // Connection in progress\r
551 //\r
552 pSocket->errno = EINPROGRESS;\r
553 Status = EFI_NOT_READY;\r
554 DEBUG (( DEBUG_CONNECT,\r
555 "0x%08x: Port attempting connection to %d.%d.%d.%d:%d\r\n",\r
556 pPort,\r
557 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0],\r
558 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1],\r
559 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2],\r
560 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3],\r
561 pTcp4->ConfigData.AccessPoint.RemotePort ));\r
562 }\r
563 else {\r
564 //\r
565 // Connection error\r
566 //\r
567 DEBUG (( DEBUG_CONNECT,\r
568 "ERROR - Port 0x%08x not connected, Status: %r\r\n",\r
569 pPort,\r
570 Status ));\r
571 //\r
572 // Determine the errno value\r
573 //\r
574 switch ( Status ) {\r
575 default:\r
576 pSocket->errno = EIO;\r
577 break;\r
d7ce7006 578\r
a88c3163 579 case EFI_OUT_OF_RESOURCES:\r
580 pSocket->errno = ENOBUFS;\r
581 break;\r
d7ce7006 582\r
a88c3163 583 case EFI_TIMEOUT:\r
584 pSocket->errno = ETIMEDOUT;\r
585 break;\r
d7ce7006 586\r
d6f19057 587 case EFI_NO_MEDIA:\r
a88c3163 588 case EFI_NETWORK_UNREACHABLE:\r
589 pSocket->errno = ENETDOWN;\r
590 break;\r
d7ce7006 591\r
a88c3163 592 case EFI_HOST_UNREACHABLE:\r
593 pSocket->errno = EHOSTUNREACH;\r
594 break;\r
d7ce7006 595\r
a88c3163 596 case EFI_PORT_UNREACHABLE:\r
597 case EFI_PROTOCOL_UNREACHABLE:\r
598 case EFI_CONNECTION_REFUSED:\r
599 pSocket->errno = ECONNREFUSED;\r
600 break;\r
d7ce7006 601\r
a88c3163 602 case EFI_CONNECTION_RESET:\r
603 pSocket->errno = ECONNRESET;\r
604 break;\r
605 }\r
606 }\r
d7ce7006 607 }\r
608 }\r
609 else {\r
a88c3163 610 //\r
611 // No more local adapters available\r
612 //\r
613 pSocket->errno = ENETUNREACH;\r
614 Status = EFI_NO_RESPONSE;\r
d7ce7006 615 }\r
a88c3163 616\r
d7ce7006 617 //\r
618 // Return the operation status\r
619 //\r
620 DBG_EXIT_STATUS ( Status );\r
621 return Status;\r
622}\r
623\r
624\r
625/**\r
626 Establish the known port to listen for network connections.\r
627\r
a88c3163 628 This routine places the port into a state that enables connection\r
629 attempts.\r
630\r
631 This routine is called by ::EslSocketListen to handle the network\r
632 specifics of the listen operation for SOCK_STREAM and SOCK_SEQPACKET\r
633 sockets. See the \ref ConnectionManagement section.\r
d7ce7006 634\r
a88c3163 635 @param [in] pSocket Address of an ::ESL_SOCKET structure.\r
d7ce7006 636\r
637 @retval EFI_SUCCESS - Socket successfully created\r
638 @retval Other - Failed to enable the socket for listen\r
639\r
640**/\r
641EFI_STATUS\r
a88c3163 642EslTcp4Listen (\r
643 IN ESL_SOCKET * pSocket\r
d7ce7006 644 )\r
645{\r
a88c3163 646 ESL_PORT * pNextPort;\r
647 ESL_PORT * pPort;\r
648 ESL_TCP4_CONTEXT * pTcp4;\r
d7ce7006 649 EFI_TCP4_PROTOCOL * pTcp4Protocol;\r
650 EFI_STATUS Status;\r
651\r
652 DBG_ENTER ( );\r
653\r
654 //\r
655 // Verify the socket layer synchronization\r
656 //\r
657 VERIFY_TPL ( TPL_SOCKETS );\r
658\r
659 //\r
660 // Use for/break instead of goto\r
661 //\r
662 for ( ; ; ) {\r
663 //\r
664 // Assume no ports are available\r
665 //\r
666 pSocket->errno = EOPNOTSUPP;\r
667 Status = EFI_NOT_READY;\r
668\r
669 //\r
670 // Walk the list of ports\r
671 //\r
672 pPort = pSocket->pPortList;\r
673 while ( NULL != pPort ) {\r
674 //\r
675 // Assume success\r
676 //\r
677 pSocket->errno = 0;\r
678\r
679 //\r
680 // Use for/break insteak of goto\r
681 //\r
682 for ( ; ; ) {\r
683 //\r
684 // Create the listen completion event\r
685 //\r
686 pTcp4 = &pPort->Context.Tcp4;\r
687 Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,\r
688 TPL_SOCKETS,\r
a88c3163 689 (EFI_EVENT_NOTIFY)EslTcp4ListenComplete,\r
d7ce7006 690 pPort,\r
691 &pTcp4->ListenToken.CompletionToken.Event );\r
692 if ( EFI_ERROR ( Status )) {\r
693 DEBUG (( DEBUG_ERROR | DEBUG_LISTEN,\r
694 "ERROR - Failed to create the listen completion event, Status: %r\r\n",\r
695 Status ));\r
696 pSocket->errno = ENOMEM;\r
697 break;\r
698 }\r
699 DEBUG (( DEBUG_POOL,\r
700 "0x%08x: Created listen completion event\r\n",\r
701 pTcp4->ListenToken.CompletionToken.Event ));\r
702\r
703 //\r
704 // Configure the port\r
705 //\r
a88c3163 706 pTcp4Protocol = pPort->pProtocol.TCPv4;\r
d7ce7006 707 Status = pTcp4Protocol->Configure ( pTcp4Protocol,\r
708 &pTcp4->ConfigData );\r
709 if ( EFI_ERROR ( Status )) {\r
710 DEBUG (( DEBUG_LISTEN,\r
711 "ERROR - Failed to configure the Tcp4 port, Status: %r\r\n",\r
712 Status ));\r
713 switch ( Status ) {\r
714 case EFI_ACCESS_DENIED:\r
715 pSocket->errno = EACCES;\r
716 break;\r
717\r
718 default:\r
719 case EFI_DEVICE_ERROR:\r
720 pSocket->errno = EIO;\r
721 break;\r
722\r
723 case EFI_INVALID_PARAMETER:\r
724 pSocket->errno = EADDRNOTAVAIL;\r
725 break;\r
726\r
727 case EFI_NO_MAPPING:\r
728 pSocket->errno = EAFNOSUPPORT;\r
729 break;\r
730\r
731 case EFI_OUT_OF_RESOURCES:\r
732 pSocket->errno = ENOBUFS;\r
733 break;\r
734\r
735 case EFI_UNSUPPORTED:\r
736 pSocket->errno = EOPNOTSUPP;\r
737 break;\r
738 }\r
739 break;\r
740 }\r
741 DEBUG (( DEBUG_LISTEN,\r
742 "0x%08x: Port configured\r\n",\r
743 pPort ));\r
a88c3163 744 pPort->bConfigured = TRUE;\r
d7ce7006 745\r
746 //\r
747 // Start the listen operation on the port\r
748 //\r
749 Status = pTcp4Protocol->Accept ( pTcp4Protocol,\r
750 &pTcp4->ListenToken );\r
751 if ( EFI_ERROR ( Status )) {\r
752 DEBUG (( DEBUG_LISTEN,\r
753 "ERROR - Failed Tcp4 accept, Status: %r\r\n",\r
754 Status ));\r
755 switch ( Status ) {\r
756 case EFI_ACCESS_DENIED:\r
757 pSocket->errno = EACCES;\r
758 break;\r
759\r
760 default:\r
761 case EFI_DEVICE_ERROR:\r
762 pSocket->errno = EIO;\r
763 break;\r
764\r
765 case EFI_INVALID_PARAMETER:\r
766 pSocket->errno = EADDRNOTAVAIL;\r
767 break;\r
768\r
769 case EFI_NOT_STARTED:\r
770 pSocket->errno = ENETDOWN;\r
771 break;\r
772\r
773 case EFI_OUT_OF_RESOURCES:\r
774 pSocket->errno = ENOBUFS;\r
775 break;\r
776 }\r
777 break;\r
778 }\r
779 DEBUG (( DEBUG_LISTEN,\r
780 "0x%08x: Listen pending on Port\r\n",\r
781 pPort ));\r
782\r
783 //\r
784 // Listen is pending on this port\r
785 //\r
786 break;\r
787 }\r
788\r
789 //\r
790 // Get the next port\r
791 //\r
792 pNextPort = pPort->pLinkSocket;\r
793\r
794 //\r
795 // Close the port upon error\r
796 //\r
a88c3163 797 if ( EFI_ERROR ( Status )) {\r
798 EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN );\r
d7ce7006 799 }\r
800\r
801 //\r
802 // Set the next port\r
803 //\r
804 pPort = pNextPort;\r
805 }\r
806 \r
807 //\r
808 // Determine if any ports are in the listen state\r
809 //\r
810 if ( NULL == pSocket->pPortList ) {\r
811 //\r
812 // No ports in the listen state\r
813 //\r
814 pSocket->MaxFifoDepth = 0;\r
815\r
816 //\r
817 // Return the last error detected\r
818 //\r
819 break;\r
820 }\r
821\r
822 //\r
823 // Mark the socket as configured\r
824 //\r
825 pSocket->bConfigured = TRUE;\r
826\r
827 //\r
828 // All done\r
829 //\r
830 DEBUG (( DEBUG_LISTEN,\r
831 "0x%08x: pSocket - Listen pending on socket\r\n",\r
832 pSocket ));\r
833 break;\r
834 }\r
835\r
836 //\r
837 // Return the operation status\r
838 //\r
839 DBG_EXIT_STATUS ( Status );\r
840 return Status;\r
841}\r
842\r
843\r
844/**\r
845 Process the connection attempt\r
846\r
847 A system has initiated a connection attempt with a socket in the\r
848 listen state. Attempt to complete the connection.\r
849\r
a88c3163 850 The TCPv4 layer calls this routine when a connection is made to\r
851 the socket in the listen state. See the\r
852 \ref ConnectionManagement section.\r
853\r
854 @param [in] Event The listen completion event\r
d7ce7006 855\r
a88c3163 856 @param [in] pPort Address of an ::ESL_PORT structure.\r
d7ce7006 857\r
858**/\r
859VOID\r
a88c3163 860EslTcp4ListenComplete (\r
d7ce7006 861 IN EFI_EVENT Event,\r
a88c3163 862 IN ESL_PORT * pPort\r
d7ce7006 863 )\r
864{\r
865 EFI_HANDLE ChildHandle;\r
a88c3163 866 struct sockaddr_in LocalAddress;\r
d7ce7006 867 EFI_TCP4_CONFIG_DATA * pConfigData;\r
a88c3163 868 ESL_LAYER * pLayer;\r
869 ESL_PORT * pNewPort;\r
870 ESL_SOCKET * pNewSocket;\r
871 ESL_SOCKET * pSocket;\r
872 ESL_TCP4_CONTEXT * pTcp4;\r
d7ce7006 873 EFI_TCP4_PROTOCOL * pTcp4Protocol;\r
874 EFI_STATUS Status;\r
875 EFI_HANDLE TcpPortHandle;\r
876 EFI_STATUS TempStatus;\r
877\r
878 DBG_ENTER ( );\r
a88c3163 879 VERIFY_AT_TPL ( TPL_SOCKETS );\r
d7ce7006 880\r
881 //\r
882 // Assume success\r
883 //\r
884 Status = EFI_SUCCESS;\r
885\r
886 //\r
887 // Determine if this connection fits into the connection FIFO\r
888 //\r
889 pSocket = pPort->pSocket;\r
890 TcpPortHandle = pPort->Context.Tcp4.ListenToken.NewChildHandle;\r
891 if (( SOCKET_STATE_LISTENING == pSocket->State )\r
892 && ( pSocket->MaxFifoDepth > pSocket->FifoDepth )) {\r
893 //\r
894 // Allocate a socket for this connection\r
895 //\r
896 ChildHandle = NULL;\r
897 pLayer = &mEslLayer;\r
898 Status = EslSocketAllocate ( &ChildHandle,\r
899 DEBUG_CONNECTION,\r
900 &pNewSocket );\r
901 if ( !EFI_ERROR ( Status )) {\r
902 //\r
903 // Clone the socket parameters\r
904 //\r
a88c3163 905 pNewSocket->pApi = pSocket->pApi;\r
d7ce7006 906 pNewSocket->Domain = pSocket->Domain;\r
907 pNewSocket->Protocol = pSocket->Protocol;\r
908 pNewSocket->Type = pSocket->Type;\r
909\r
910 //\r
a88c3163 911 // Build the local address\r
d7ce7006 912 //\r
913 pTcp4 = &pPort->Context.Tcp4;\r
a88c3163 914 LocalAddress.sin_len = (uint8_t)pNewSocket->pApi->MinimumAddressLength;\r
915 LocalAddress.sin_family = AF_INET;\r
916 LocalAddress.sin_port = 0;\r
917 LocalAddress.sin_addr.s_addr = *(UINT32 *)&pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0];\r
918\r
919 //\r
920 // Allocate a port for this connection\r
921 // Note in this instance Configure may not be called with NULL!\r
922 //\r
923 Status = EslSocketPortAllocate ( pNewSocket,\r
924 pPort->pService,\r
925 TcpPortHandle,\r
926 (struct sockaddr *)&LocalAddress,\r
927 FALSE,\r
928 DEBUG_CONNECTION,\r
929 &pNewPort );\r
d7ce7006 930 if ( !EFI_ERROR ( Status )) {\r
931 //\r
932 // Restart the listen operation on the port\r
933 //\r
a88c3163 934 pTcp4Protocol = pPort->pProtocol.TCPv4;\r
d7ce7006 935 Status = pTcp4Protocol->Accept ( pTcp4Protocol,\r
936 &pTcp4->ListenToken );\r
937\r
938 //\r
939 // Close the TCP port using SocketClose\r
940 //\r
941 TcpPortHandle = NULL;\r
942 pTcp4 = &pNewPort->Context.Tcp4;\r
d7ce7006 943\r
944 //\r
945 // Check for an accept call error\r
946 //\r
947 if ( !EFI_ERROR ( Status )) {\r
948 //\r
949 // Get the port configuration\r
950 //\r
a88c3163 951 pNewPort->bConfigured = TRUE;\r
d7ce7006 952 pConfigData = &pTcp4->ConfigData;\r
953 pConfigData->ControlOption = &pTcp4->Option;\r
a88c3163 954 pTcp4Protocol = pNewPort->pProtocol.TCPv4;\r
d7ce7006 955 Status = pTcp4Protocol->GetModeData ( pTcp4Protocol,\r
956 NULL,\r
957 pConfigData,\r
958 NULL,\r
959 NULL,\r
960 NULL );\r
961 if ( !EFI_ERROR ( Status )) {\r
962 //\r
963 // Add the new socket to the connection FIFO\r
964 //\r
965 if ( NULL == pSocket->pFifoTail ) {\r
966 //\r
967 // First connection\r
968 //\r
969 pSocket->pFifoHead = pNewSocket;\r
970 }\r
971 else {\r
972 //\r
973 // Add to end of list.\r
974 //\r
975 pSocket->pFifoTail->pNextConnection = pNewSocket;\r
976 }\r
977 pSocket->pFifoTail = pNewSocket;\r
978 pSocket->FifoDepth += 1;\r
979\r
980 //\r
981 // Update the socket state\r
982 //\r
983 pNewSocket->State = SOCKET_STATE_IN_FIFO;\r
984\r
985 //\r
986 // Log the connection\r
987 //\r
988 DEBUG (( DEBUG_CONNECTION | DEBUG_INFO,\r
989 "0x%08x: Socket on port %d.%d.%d.%d:%d connected to %d.%d.%d.%d:%d\r\n",\r
990 pNewSocket,\r
991 pConfigData->AccessPoint.StationAddress.Addr[0],\r
992 pConfigData->AccessPoint.StationAddress.Addr[1],\r
993 pConfigData->AccessPoint.StationAddress.Addr[2],\r
994 pConfigData->AccessPoint.StationAddress.Addr[3],\r
995 pConfigData->AccessPoint.StationPort,\r
996 pConfigData->AccessPoint.RemoteAddress.Addr[0],\r
997 pConfigData->AccessPoint.RemoteAddress.Addr[1],\r
998 pConfigData->AccessPoint.RemoteAddress.Addr[2],\r
999 pConfigData->AccessPoint.RemoteAddress.Addr[3],\r
1000 pConfigData->AccessPoint.RemotePort ));\r
1001 DEBUG (( DEBUG_CONNECTION | DEBUG_INFO,\r
1002 "0x%08x: Listen socket adding socket 0x%08x to FIFO, depth: %d\r\n",\r
1003 pSocket,\r
1004 pNewSocket,\r
1005 pSocket->FifoDepth ));\r
1006\r
1007 //\r
1008 // Start the receive operation\r
1009 //\r
a88c3163 1010 EslSocketRxStart ( pNewPort );\r
d7ce7006 1011 }\r
1012 else {\r
1013 DEBUG (( DEBUG_ERROR | DEBUG_CONNECTION | DEBUG_INFO,\r
1014 "ERROR - GetModeData failed on port 0x%08x, Status: %r\r\n",\r
1015 pNewPort,\r
1016 Status ));\r
1017 }\r
1018 }\r
1019 else {\r
1020 //\r
1021 // The listen failed on this port\r
1022 //\r
1023 DEBUG (( DEBUG_LISTEN | DEBUG_INFO,\r
1024 "ERROR - Listen failed on port 0x%08x, Status: %r\r\n",\r
1025 pPort,\r
1026 Status ));\r
1027\r
1028 //\r
1029 // Close the listening port\r
1030 //\r
a88c3163 1031 EslSocketPortCloseStart ( pPort, TRUE, DEBUG_LISTEN );\r
d7ce7006 1032 }\r
1033 }\r
1034\r
1035 //\r
1036 // Done with the socket if necessary\r
1037 //\r
1038 if ( EFI_ERROR ( Status )) {\r
1039 TempStatus = EslSocketCloseStart ( &pNewSocket->SocketProtocol,\r
1040 TRUE,\r
1041 &pSocket->errno );\r
1042 ASSERT ( EFI_SUCCESS == TempStatus );\r
1043 }\r
1044 }\r
1045 }\r
1046 else {\r
1047 DEBUG (( DEBUG_CONNECTION,\r
1048 "0x%08x: Socket FIFO full, connection refused\r\n",\r
1049 pSocket ));\r
1050\r
1051 //\r
1052 // The FIFO is full or the socket is in the wrong state\r
1053 //\r
1054 Status = EFI_BUFFER_TOO_SMALL;\r
1055 }\r
1056\r
1057 //\r
1058 // Close the connection if necessary\r
1059 //\r
1060 if (( EFI_ERROR ( Status ))\r
1061 && ( NULL == TcpPortHandle )) {\r
1062 //\r
1063 // TODO: Finish this code path\r
1064 // The new connection does not fit into the connection FIFO\r
1065 //\r
1066 // Process:\r
1067 // Call close\r
1068 // Release the resources\r
1069 \r
1070 }\r
1071\r
1072 DBG_EXIT ( );\r
1073}\r
1074\r
1075\r
1076/**\r
a88c3163 1077 Get the local socket address.\r
d7ce7006 1078\r
a88c3163 1079 This routine returns the IPv4 address and TCP port number associated\r
1080 with the local socket.\r
d7ce7006 1081\r
a88c3163 1082 This routine is called by ::EslSocketGetLocalAddress to determine the\r
1083 network address for the SOCK_STREAM or SOCK_SEQPACKET socket.\r
1084\r
1085 @param [in] pPort Address of an ::ESL_PORT structure.\r
1086\r
1087 @param [out] pSockAddr Network address to receive the local system address\r
1088\r
1089**/\r
1090VOID\r
1091EslTcp4LocalAddressGet (\r
1092 IN ESL_PORT * pPort,\r
1093 OUT struct sockaddr * pSockAddr\r
1094 )\r
1095{\r
1096 struct sockaddr_in * pLocalAddress;\r
1097 ESL_TCP4_CONTEXT * pTcp4;\r
1098\r
1099 DBG_ENTER ( );\r
1100\r
1101 //\r
1102 // Return the local address\r
1103 //\r
1104 pTcp4 = &pPort->Context.Tcp4;\r
1105 pLocalAddress = (struct sockaddr_in *)pSockAddr;\r
1106 pLocalAddress->sin_family = AF_INET;\r
1107 pLocalAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.StationPort );\r
1108 CopyMem ( &pLocalAddress->sin_addr,\r
1109 &pTcp4->ConfigData.AccessPoint.StationAddress.Addr[0],\r
1110 sizeof ( pLocalAddress->sin_addr ));\r
1111\r
1112 DBG_EXIT ( );\r
1113}\r
1114\r
1115\r
1116/**\r
1117 Set the local port address.\r
1118\r
1119 This routine sets the local port address.\r
1120\r
1121 This support routine is called by ::EslSocketPortAllocate.\r
1122\r
1123 @param [in] pPort Address of an ESL_PORT structure\r
1124 @param [in] pSockAddr Address of a sockaddr structure that contains the\r
1125 connection point on the local machine. An IPv4 address\r
1126 of INADDR_ANY specifies that the connection is made to\r
1127 all of the network stacks on the platform. Specifying a\r
1128 specific IPv4 address restricts the connection to the\r
1129 network stack supporting that address. Specifying zero\r
1130 for the port causes the network layer to assign a port\r
1131 number from the dynamic range. Specifying a specific\r
1132 port number causes the network layer to use that port.\r
1133\r
1134 @param [in] bBindTest TRUE = run bind testing\r
1135\r
1136 @retval EFI_SUCCESS The operation was successful\r
d7ce7006 1137\r
1138 **/\r
1139EFI_STATUS\r
a88c3163 1140EslTcp4LocalAddressSet (\r
1141 IN ESL_PORT * pPort,\r
1142 IN CONST struct sockaddr * pSockAddr,\r
1143 IN BOOLEAN bBindTest\r
d7ce7006 1144 )\r
1145{\r
d7ce7006 1146 EFI_TCP4_ACCESS_POINT * pAccessPoint;\r
a88c3163 1147 CONST struct sockaddr_in * pIpAddress;\r
1148 CONST UINT8 * pIpv4Address;\r
d7ce7006 1149 EFI_STATUS Status;\r
1150\r
1151 DBG_ENTER ( );\r
1152\r
1153 //\r
a88c3163 1154 // Validate the address\r
1155 //\r
1156 pIpAddress = (struct sockaddr_in *)pSockAddr;\r
1157 if ( INADDR_BROADCAST == pIpAddress->sin_addr.s_addr ) {\r
d7ce7006 1158 //\r
a88c3163 1159 // The local address must not be the broadcast address\r
d7ce7006 1160 //\r
a88c3163 1161 Status = EFI_INVALID_PARAMETER;\r
1162 pPort->pSocket->errno = EADDRNOTAVAIL;\r
1163 }\r
1164 else {\r
d7ce7006 1165 //\r
a88c3163 1166 // Set the local address\r
d7ce7006 1167 //\r
a88c3163 1168 pIpv4Address = (UINT8 *)&pIpAddress->sin_addr.s_addr;\r
1169 pAccessPoint = &pPort->Context.Tcp4.ConfigData.AccessPoint;\r
1170 pAccessPoint->StationAddress.Addr[0] = pIpv4Address[0];\r
1171 pAccessPoint->StationAddress.Addr[1] = pIpv4Address[1];\r
1172 pAccessPoint->StationAddress.Addr[2] = pIpv4Address[2];\r
1173 pAccessPoint->StationAddress.Addr[3] = pIpv4Address[3];\r
d7ce7006 1174\r
1175 //\r
a88c3163 1176 // Determine if the default address is used\r
d7ce7006 1177 //\r
a88c3163 1178 pAccessPoint->UseDefaultAddress = (BOOLEAN)( 0 == pIpAddress->sin_addr.s_addr );\r
1179 \r
d7ce7006 1180 //\r
a88c3163 1181 // Set the subnet mask\r
d7ce7006 1182 //\r
a88c3163 1183 if ( pAccessPoint->UseDefaultAddress ) {\r
1184 pAccessPoint->SubnetMask.Addr[0] = 0;\r
1185 pAccessPoint->SubnetMask.Addr[1] = 0;\r
1186 pAccessPoint->SubnetMask.Addr[2] = 0;\r
1187 pAccessPoint->SubnetMask.Addr[3] = 0;\r
1188 }\r
1189 else {\r
1190 pAccessPoint->SubnetMask.Addr[0] = 0xff;\r
1191 pAccessPoint->SubnetMask.Addr[1] = 0xff;\r
1192 pAccessPoint->SubnetMask.Addr[2] = 0xff;\r
1193 pAccessPoint->SubnetMask.Addr[3] = 0xff;\r
d7ce7006 1194 }\r
d7ce7006 1195\r
1196 //\r
a88c3163 1197 // Validate the IP address\r
d7ce7006 1198 //\r
a88c3163 1199 pAccessPoint->StationPort = 0;\r
1200 Status = bBindTest ? EslSocketBindTest ( pPort, EADDRNOTAVAIL )\r
1201 : EFI_SUCCESS;\r
1202 if ( !EFI_ERROR ( Status )) {\r
1203 //\r
1204 // Set the port number\r
1205 //\r
1206 pAccessPoint->StationPort = SwapBytes16 ( pIpAddress->sin_port );\r
f74dc4bb 1207 pPort->pSocket->bAddressSet = TRUE;\r
a88c3163 1208\r
1209 //\r
1210 // Display the local address\r
1211 //\r
1212 DEBUG (( DEBUG_BIND,\r
1213 "0x%08x: Port, Local TCP4 Address: %d.%d.%d.%d:%d\r\n",\r
1214 pPort,\r
1215 pAccessPoint->StationAddress.Addr[0],\r
1216 pAccessPoint->StationAddress.Addr[1],\r
1217 pAccessPoint->StationAddress.Addr[2],\r
1218 pAccessPoint->StationAddress.Addr[3],\r
1219 pAccessPoint->StationPort ));\r
d7ce7006 1220 }\r
a88c3163 1221 }\r
1222\r
1223 //\r
1224 // Return the operation status\r
1225 //\r
1226 DBG_EXIT_STATUS ( Status );\r
1227 return Status;\r
1228}\r
1229\r
1230\r
1231/**\r
1232 Free a receive packet\r
1233\r
1234 This routine performs the network specific operations necessary\r
1235 to free a receive packet.\r
1236\r
1237 This routine is called by ::EslSocketPortCloseTxDone to free a\r
1238 receive packet.\r
1239\r
1240 @param [in] pPacket Address of an ::ESL_PACKET structure.\r
1241 @param [in, out] pRxBytes Address of the count of RX bytes\r
1242\r
1243**/\r
1244VOID\r
1245EslTcp4PacketFree (\r
1246 IN ESL_PACKET * pPacket,\r
1247 IN OUT size_t * pRxBytes\r
1248 )\r
1249{\r
1250 DBG_ENTER ( );\r
1251\r
1252 //\r
1253 // Account for the receive bytes\r
1254 //\r
1255 *pRxBytes -= pPacket->Op.Tcp4Rx.RxData.DataLength;\r
1256 DBG_EXIT ( );\r
1257}\r
1258\r
d7ce7006 1259\r
a88c3163 1260/**\r
1261 Initialize the network specific portions of an ::ESL_PORT structure.\r
1262\r
1263 This routine initializes the network specific portions of an\r
1264 ::ESL_PORT structure for use by the socket.\r
1265\r
1266 This support routine is called by ::EslSocketPortAllocate\r
1267 to connect the socket with the underlying network adapter\r
1268 running the TCPv4 protocol.\r
1269\r
1270 @param [in] pPort Address of an ESL_PORT structure\r
1271 @param [in] DebugFlags Flags for debug messages\r
1272\r
1273 @retval EFI_SUCCESS - Socket successfully created\r
1274\r
1275 **/\r
1276EFI_STATUS\r
1277EslTcp4PortAllocate (\r
1278 IN ESL_PORT * pPort,\r
1279 IN UINTN DebugFlags\r
1280 )\r
1281{\r
1282 EFI_TCP4_ACCESS_POINT * pAccessPoint;\r
1283 ESL_SOCKET * pSocket;\r
1284 ESL_TCP4_CONTEXT * pTcp4;\r
1285 EFI_STATUS Status;\r
1286\r
1287 DBG_ENTER ( );\r
1288\r
1289 //\r
1290 // Use for/break instead of goto\r
1291 for ( ; ; ) {\r
d7ce7006 1292 //\r
1293 // Allocate the close event\r
1294 //\r
a88c3163 1295 pSocket = pPort->pSocket;\r
1296 pTcp4 = &pPort->Context.Tcp4;\r
d7ce7006 1297 Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,\r
1298 TPL_SOCKETS,\r
a88c3163 1299 (EFI_EVENT_NOTIFY)EslSocketPortCloseComplete,\r
d7ce7006 1300 pPort,\r
1301 &pTcp4->CloseToken.CompletionToken.Event);\r
1302 if ( EFI_ERROR ( Status )) {\r
1303 DEBUG (( DEBUG_ERROR | DebugFlags,\r
1304 "ERROR - Failed to create the close event, Status: %r\r\n",\r
1305 Status ));\r
1306 pSocket->errno = ENOMEM;\r
1307 break;\r
1308 }\r
1309 DEBUG (( DEBUG_CLOSE | DEBUG_POOL,\r
1310 "0x%08x: Created close event\r\n",\r
1311 pTcp4->CloseToken.CompletionToken.Event ));\r
1312\r
1313 //\r
1314 // Allocate the connection event\r
1315 //\r
1316 Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL,\r
1317 TPL_SOCKETS,\r
a88c3163 1318 (EFI_EVENT_NOTIFY)EslTcp4ConnectComplete,\r
d7ce7006 1319 pPort,\r
1320 &pTcp4->ConnectToken.CompletionToken.Event);\r
1321 if ( EFI_ERROR ( Status )) {\r
1322 DEBUG (( DEBUG_ERROR | DebugFlags,\r
1323 "ERROR - Failed to create the connect event, Status: %r\r\n",\r
1324 Status ));\r
1325 pSocket->errno = ENOMEM;\r
1326 break;\r
1327 }\r
1328 DEBUG (( DEBUG_CLOSE | DEBUG_POOL,\r
1329 "0x%08x: Created connect event\r\n",\r
1330 pTcp4->ConnectToken.CompletionToken.Event ));\r
1331\r
1332 //\r
a88c3163 1333 // Initialize the port\r
d7ce7006 1334 //\r
a88c3163 1335 pSocket->TxPacketOffset = OFFSET_OF ( ESL_PACKET, Op.Tcp4Tx.TxData );\r
1336 pSocket->TxTokenEventOffset = OFFSET_OF ( ESL_IO_MGMT, Token.Tcp4Tx.CompletionToken.Event );\r
1337 pSocket->TxTokenOffset = OFFSET_OF ( EFI_TCP4_IO_TOKEN, Packet.TxData );\r
d7ce7006 1338\r
1339 //\r
a88c3163 1340 // Save the cancel, receive and transmit addresses\r
1341 // pPort->pfnRxCancel = NULL; since the UEFI implementation returns EFI_UNSUPPORTED\r
d7ce7006 1342 //\r
a88c3163 1343 pPort->pfnConfigure = (PFN_NET_CONFIGURE)pPort->pProtocol.TCPv4->Configure;\r
3bdf9aae 1344 pPort->pfnRxPoll = (PFN_NET_POLL)pPort->pProtocol.TCPv4->Poll;\r
a88c3163 1345 pPort->pfnRxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv4->Receive;\r
1346 pPort->pfnTxStart = (PFN_NET_IO_START)pPort->pProtocol.TCPv4->Transmit;\r
d7ce7006 1347\r
1348 //\r
a88c3163 1349 // Set the configuration flags\r
d7ce7006 1350 //\r
a88c3163 1351 pAccessPoint = &pPort->Context.Tcp4.ConfigData.AccessPoint;\r
1352 pAccessPoint->ActiveFlag = FALSE;\r
1353 pTcp4->ConfigData.TimeToLive = 255;\r
d7ce7006 1354 break;\r
1355 }\r
1356\r
d7ce7006 1357 //\r
1358 // Return the operation status\r
1359 //\r
1360 DBG_EXIT_STATUS ( Status );\r
1361 return Status;\r
1362}\r
1363\r
1364\r
1365/**\r
1366 Close a TCP4 port.\r
1367\r
a88c3163 1368 This routine releases the network specific resources allocated by\r
1369 ::EslTcp4PortAllocate.\r
1370\r
1371 This routine is called by ::EslSocketPortClose.\r
1372 See the \ref PortCloseStateMachine section.\r
d7ce7006 1373 \r
a88c3163 1374 @param [in] pPort Address of an ::ESL_PORT structure.\r
d7ce7006 1375\r
1376 @retval EFI_SUCCESS The port is closed\r
1377 @retval other Port close error\r
1378\r
1379**/\r
1380EFI_STATUS\r
a88c3163 1381EslTcp4PortClose (\r
1382 IN ESL_PORT * pPort\r
d7ce7006 1383 )\r
1384{\r
1385 UINTN DebugFlags;\r
a88c3163 1386 ESL_TCP4_CONTEXT * pTcp4;\r
d7ce7006 1387 EFI_STATUS Status;\r
1388 \r
1389 DBG_ENTER ( );\r
1390\r
d7ce7006 1391 //\r
1392 // Locate the port in the socket list\r
1393 //\r
1394 Status = EFI_SUCCESS;\r
d7ce7006 1395 DebugFlags = pPort->DebugFlags;\r
d7ce7006 1396 pTcp4 = &pPort->Context.Tcp4;\r
d7ce7006 1397\r
1398 //\r
1399 // Done with the connect event\r
1400 //\r
1401 if ( NULL != pTcp4->ConnectToken.CompletionToken.Event ) {\r
1402 Status = gBS->CloseEvent ( pTcp4->ConnectToken.CompletionToken.Event );\r
1403 if ( !EFI_ERROR ( Status )) {\r
1404 DEBUG (( DebugFlags | DEBUG_POOL,\r
1405 "0x%08x: Closed connect event\r\n",\r
1406 pTcp4->ConnectToken.CompletionToken.Event ));\r
1407 }\r
1408 else {\r
1409 DEBUG (( DEBUG_ERROR | DebugFlags,\r
1410 "ERROR - Failed to close the connect event, Status: %r\r\n",\r
1411 Status ));\r
1412 ASSERT ( EFI_SUCCESS == Status );\r
1413 }\r
1414 }\r
1415\r
1416 //\r
1417 // Done with the close event\r
1418 //\r
1419 if ( NULL != pTcp4->CloseToken.CompletionToken.Event ) {\r
1420 Status = gBS->CloseEvent ( pTcp4->CloseToken.CompletionToken.Event );\r
1421 if ( !EFI_ERROR ( Status )) {\r
1422 DEBUG (( DebugFlags | DEBUG_POOL,\r
1423 "0x%08x: Closed close event\r\n",\r
1424 pTcp4->CloseToken.CompletionToken.Event ));\r
1425 }\r
1426 else {\r
1427 DEBUG (( DEBUG_ERROR | DebugFlags,\r
1428 "ERROR - Failed to close the close event, Status: %r\r\n",\r
1429 Status ));\r
1430 ASSERT ( EFI_SUCCESS == Status );\r
1431 }\r
1432 }\r
1433\r
1434 //\r
1435 // Done with the listen completion event\r
1436 //\r
1437 if ( NULL != pTcp4->ListenToken.CompletionToken.Event ) {\r
1438 Status = gBS->CloseEvent ( pTcp4->ListenToken.CompletionToken.Event );\r
1439 if ( !EFI_ERROR ( Status )) {\r
1440 DEBUG (( DebugFlags | DEBUG_POOL,\r
1441 "0x%08x: Closed listen completion event\r\n",\r
1442 pTcp4->ListenToken.CompletionToken.Event ));\r
1443 }\r
1444 else {\r
1445 DEBUG (( DEBUG_ERROR | DebugFlags,\r
1446 "ERROR - Failed to close the listen completion event, Status: %r\r\n",\r
1447 Status ));\r
1448 ASSERT ( EFI_SUCCESS == Status );\r
1449 }\r
1450 }\r
1451\r
d7ce7006 1452 //\r
1453 // Return the operation status\r
1454 //\r
1455 DBG_EXIT_STATUS ( Status );\r
1456 return Status;\r
1457}\r
1458\r
1459\r
1460/**\r
a88c3163 1461 Perform the network specific close operation on the port.\r
d7ce7006 1462\r
a88c3163 1463 This routine performs a cancel operations on the TCPv4 port to\r
1464 shutdown the receive operations on the port.\r
d7ce7006 1465\r
a88c3163 1466 This routine is called by the ::EslSocketPortCloseTxDone\r
1467 routine after the port completes all of the transmission.\r
1468\r
1469 @param [in] pPort Address of an ::ESL_PORT structure.\r
1470\r
1471 @retval EFI_SUCCESS The port is closed, not normally returned\r
1472 @retval EFI_NOT_READY The port is still closing\r
1473 @retval EFI_ALREADY_STARTED Error, the port is in the wrong state,\r
1474 most likely the routine was called already.\r
d7ce7006 1475\r
1476**/\r
a88c3163 1477EFI_STATUS\r
1478EslTcp4PortCloseOp (\r
1479 IN ESL_PORT * pPort\r
d7ce7006 1480 )\r
1481{\r
a88c3163 1482 ESL_TCP4_CONTEXT * pTcp4;\r
1483 EFI_TCP4_PROTOCOL * pTcp4Protocol;\r
d7ce7006 1484 EFI_STATUS Status;\r
1485\r
1486 DBG_ENTER ( );\r
1487\r
1488 //\r
a88c3163 1489 // Close the configured port\r
d7ce7006 1490 //\r
a88c3163 1491 Status = EFI_SUCCESS;\r
1492 pTcp4 = &pPort->Context.Tcp4;\r
1493 pTcp4Protocol = pPort->pProtocol.TCPv4;\r
1494 pTcp4->CloseToken.AbortOnClose = pPort->bCloseNow;\r
1495 Status = pTcp4Protocol->Close ( pTcp4Protocol,\r
1496 &pTcp4->CloseToken );\r
1497 if ( !EFI_ERROR ( Status )) {\r
1498 DEBUG (( pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO,\r
1499 "0x%08x: Port close started\r\n",\r
1500 pPort ));\r
1501 }\r
1502 else {\r
1503 DEBUG (( DEBUG_ERROR | pPort->DebugFlags | DEBUG_CLOSE | DEBUG_INFO,\r
1504 "ERROR - Close failed on port 0x%08x, Status: %r\r\n",\r
1505 pPort,\r
1506 Status ));\r
1507 }\r
d7ce7006 1508\r
1509 //\r
a88c3163 1510 // Return the operation status\r
d7ce7006 1511 //\r
d7ce7006 1512 DBG_EXIT_STATUS ( Status );\r
a88c3163 1513 return Status;\r
d7ce7006 1514}\r
1515\r
1516\r
1517/**\r
a88c3163 1518 Receive data from a network connection.\r
d7ce7006 1519\r
a88c3163 1520 This routine attempts to return buffered data to the caller. The\r
1521 data is removed from the urgent queue if the message flag MSG_OOB\r
1522 is specified, otherwise data is removed from the normal queue.\r
1523 See the \ref ReceiveEngine section.\r
d7ce7006 1524\r
a88c3163 1525 This routine is called by ::EslSocketReceive to handle the network\r
1526 specific receive operation to support SOCK_STREAM and SOCK_SEQPACKET\r
1527 sockets.\r
d7ce7006 1528\r
a88c3163 1529 @param [in] pPort Address of an ::ESL_PORT structure.\r
d7ce7006 1530\r
a88c3163 1531 @param [in] pPacket Address of an ::ESL_PACKET structure.\r
d7ce7006 1532 \r
a88c3163 1533 @param [in] pbConsumePacket Address of a BOOLEAN indicating if the packet is to be consumed\r
d7ce7006 1534 \r
1535 @param [in] BufferLength Length of the the buffer\r
1536 \r
1537 @param [in] pBuffer Address of a buffer to receive the data.\r
1538 \r
1539 @param [in] pDataLength Number of received data bytes in the buffer.\r
1540\r
1541 @param [out] pAddress Network address to receive the remote system address\r
1542\r
a88c3163 1543 @param [out] pSkipBytes Address to receive the number of bytes skipped\r
d7ce7006 1544\r
a88c3163 1545 @return Returns the address of the next free byte in the buffer.\r
d7ce7006 1546\r
1547 **/\r
a88c3163 1548UINT8 *\r
1549EslTcp4Receive (\r
1550 IN ESL_PORT * pPort,\r
1551 IN ESL_PACKET * pPacket,\r
1552 IN BOOLEAN * pbConsumePacket,\r
d7ce7006 1553 IN size_t BufferLength,\r
1554 IN UINT8 * pBuffer,\r
1555 OUT size_t * pDataLength,\r
1556 OUT struct sockaddr * pAddress,\r
a88c3163 1557 OUT size_t * pSkipBytes\r
d7ce7006 1558 )\r
1559{\r
a88c3163 1560 size_t DataLength;\r
d7ce7006 1561 struct sockaddr_in * pRemoteAddress;\r
a88c3163 1562 ESL_TCP4_CONTEXT * pTcp4;\r
d7ce7006 1563\r
1564 DBG_ENTER ( );\r
1565\r
1566 //\r
a88c3163 1567 // Return the remote system address if requested\r
d7ce7006 1568 //\r
a88c3163 1569 if ( NULL != pAddress ) {\r
d7ce7006 1570 //\r
a88c3163 1571 // Build the remote address\r
d7ce7006 1572 //\r
1573 pTcp4 = &pPort->Context.Tcp4;\r
a88c3163 1574 DEBUG (( DEBUG_RX,\r
1575 "Getting packet remote address: %d.%d.%d.%d:%d\r\n",\r
1576 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0],\r
1577 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1],\r
1578 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2],\r
1579 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3],\r
1580 pTcp4->ConfigData.AccessPoint.RemotePort ));\r
1581 pRemoteAddress = (struct sockaddr_in *)pAddress;\r
1582 CopyMem ( &pRemoteAddress->sin_addr,\r
1583 &pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0],\r
1584 sizeof ( pRemoteAddress->sin_addr ));\r
1585 pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort );\r
d7ce7006 1586 }\r
1587\r
1588 //\r
a88c3163 1589 // Determine the amount of received data\r
d7ce7006 1590 //\r
a88c3163 1591 DataLength = pPacket->ValidBytes;\r
1592 if ( BufferLength < DataLength ) {\r
1593 DataLength = BufferLength;\r
1594 }\r
d7ce7006 1595\r
1596 //\r
a88c3163 1597 // Move the data into the buffer\r
1598 //\r
1599 DEBUG (( DEBUG_RX,\r
1600 "0x%08x: Port copy packet 0x%08x data into 0x%08x, 0x%08x bytes\r\n",\r
1601 pPort,\r
1602 pPacket,\r
1603 pBuffer,\r
1604 DataLength ));\r
1605 CopyMem ( pBuffer, pPacket->pBuffer, DataLength );\r
d7ce7006 1606\r
fcb6f89d 1607 //\r
1608 // Set the next buffer address\r
1609 //\r
1610 pBuffer += DataLength;\r
1611\r
a88c3163 1612 //\r
1613 // Determine if the data is being read\r
1614 //\r
1615 if ( *pbConsumePacket ) {\r
d7ce7006 1616 //\r
a88c3163 1617 // Account for the bytes consumed\r
d7ce7006 1618 //\r
a88c3163 1619 pPacket->pBuffer += DataLength;\r
1620 pPacket->ValidBytes -= DataLength;\r
1621 DEBUG (( DEBUG_RX,\r
1622 "0x%08x: Port account for 0x%08x bytes\r\n",\r
d7ce7006 1623 pPort,\r
a88c3163 1624 DataLength ));\r
d7ce7006 1625\r
1626 //\r
a88c3163 1627 // Determine if the entire packet was consumed\r
d7ce7006 1628 //\r
a88c3163 1629 if (( 0 == pPacket->ValidBytes )\r
1630 || ( SOCK_STREAM != pPort->pSocket->Type )) {\r
1631 //\r
1632 // All done with this packet\r
1633 // Account for any discarded data\r
1634 //\r
1635 *pSkipBytes = pPacket->ValidBytes;\r
d7ce7006 1636 }\r
a88c3163 1637 else\r
1638 {\r
1639 //\r
1640 // More data to consume later\r
1641 //\r
1642 *pbConsumePacket = FALSE;\r
d7ce7006 1643 }\r
1644 }\r
d7ce7006 1645\r
a88c3163 1646 //\r
1647 // Return the data length and the buffer address\r
1648 //\r
1649 *pDataLength = DataLength;\r
1650 DBG_EXIT_HEX ( pBuffer );\r
1651 return pBuffer;\r
1652}\r
d7ce7006 1653\r
a88c3163 1654\r
1655/**\r
1656 Get the remote socket address.\r
1657\r
1658 This routine returns the address of the remote connection point\r
1659 associated with the SOCK_STREAM or SOCK_SEQPACKET socket.\r
1660\r
1661 This routine is called by ::EslSocketGetPeerAddress to detemine\r
1662 the TCPv4 address and por number associated with the network adapter.\r
1663\r
1664 @param [in] pPort Address of an ::ESL_PORT structure.\r
1665\r
1666 @param [out] pAddress Network address to receive the remote system address\r
1667\r
1668**/\r
1669VOID\r
1670EslTcp4RemoteAddressGet (\r
1671 IN ESL_PORT * pPort,\r
1672 OUT struct sockaddr * pAddress\r
1673 )\r
1674{\r
1675 struct sockaddr_in * pRemoteAddress;\r
1676 ESL_TCP4_CONTEXT * pTcp4;\r
1677\r
1678 DBG_ENTER ( );\r
1679\r
1680 //\r
1681 // Return the remote address\r
1682 //\r
1683 pTcp4 = &pPort->Context.Tcp4;\r
1684 pRemoteAddress = (struct sockaddr_in *)pAddress;\r
1685 pRemoteAddress->sin_family = AF_INET;\r
1686 pRemoteAddress->sin_port = SwapBytes16 ( pTcp4->ConfigData.AccessPoint.RemotePort );\r
1687 CopyMem ( &pRemoteAddress->sin_addr,\r
1688 &pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0],\r
1689 sizeof ( pRemoteAddress->sin_addr ));\r
d7ce7006 1690\r
1691 DBG_EXIT ( );\r
1692}\r
1693\r
1694\r
1695/**\r
a88c3163 1696 Set the remote address\r
1697\r
1698 This routine sets the remote address in the port.\r
1699\r
1700 This routine is called by ::EslSocketConnect to specify the\r
1701 remote network address.\r
1702\r
1703 @param [in] pPort Address of an ::ESL_PORT structure.\r
1704\r
1705 @param [in] pSockAddr Network address of the remote system.\r
1706\r
1707 @param [in] SockAddrLength Length in bytes of the network address.\r
d7ce7006 1708\r
a88c3163 1709 @retval EFI_SUCCESS The operation was successful\r
d7ce7006 1710\r
1711 **/\r
a88c3163 1712EFI_STATUS\r
1713EslTcp4RemoteAddressSet (\r
1714 IN ESL_PORT * pPort,\r
1715 IN CONST struct sockaddr * pSockAddr,\r
1716 IN socklen_t SockAddrLength\r
d7ce7006 1717 )\r
1718{\r
a88c3163 1719 CONST struct sockaddr_in * pRemoteAddress;\r
1720 ESL_TCP4_CONTEXT * pTcp4;\r
d7ce7006 1721 EFI_STATUS Status;\r
1722\r
1723 DBG_ENTER ( );\r
1724\r
1725 //\r
a88c3163 1726 // Set the remote address\r
d7ce7006 1727 //\r
d7ce7006 1728 pTcp4 = &pPort->Context.Tcp4;\r
a88c3163 1729 pRemoteAddress = (struct sockaddr_in *)pSockAddr;\r
1730 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[0] = (UINT8)( pRemoteAddress->sin_addr.s_addr );\r
1731 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[1] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 8 );\r
1732 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[2] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 16 );\r
1733 pTcp4->ConfigData.AccessPoint.RemoteAddress.Addr[3] = (UINT8)( pRemoteAddress->sin_addr.s_addr >> 24 );\r
1734 pTcp4->ConfigData.AccessPoint.RemotePort = SwapBytes16 ( pRemoteAddress->sin_port );\r
1735 Status = EFI_SUCCESS;\r
1736 if ( INADDR_BROADCAST == pRemoteAddress->sin_addr.s_addr ) {\r
1737 DEBUG (( DEBUG_CONNECT,\r
1738 "ERROR - Invalid remote address\r\n" ));\r
1739 Status = EFI_INVALID_PARAMETER;\r
1740 pPort->pSocket->errno = EAFNOSUPPORT;\r
d7ce7006 1741 }\r
1742\r
a88c3163 1743 //\r
1744 // Return the operation status\r
1745 //\r
1746 DBG_EXIT_STATUS ( Status );\r
1747 return Status;\r
d7ce7006 1748}\r
1749\r
1750\r
1751/**\r
a88c3163 1752 Process the receive completion\r
1753\r
1754 This routine queues the data in FIFO order in either the urgent\r
1755 or normal data queues depending upon the type of data received.\r
1756 See the \ref ReceiveEngine section.\r
1757\r
1758 This routine is called by the TCPv4 driver when some data is\r
1759 received.\r
d7ce7006 1760\r
a88c3163 1761 Buffer the data that was just received.\r
1762\r
1763 @param [in] Event The receive completion event\r
d7ce7006 1764\r
a88c3163 1765 @param [in] pIo Address of an ::ESL_IO_MGMT structure\r
d7ce7006 1766\r
1767**/\r
1768VOID\r
a88c3163 1769EslTcp4RxComplete (\r
1770 IN EFI_EVENT Event,\r
1771 IN ESL_IO_MGMT * pIo\r
d7ce7006 1772 )\r
1773{\r
a88c3163 1774 BOOLEAN bUrgent;\r
1775 size_t LengthInBytes;\r
1776 ESL_PACKET * pPacket;\r
1777 EFI_STATUS Status;\r
d7ce7006 1778\r
1779 DBG_ENTER ( );\r
1780\r
1781 //\r
a88c3163 1782 // Get the operation status.\r
d7ce7006 1783 //\r
a88c3163 1784 Status = pIo->Token.Tcp4Rx.CompletionToken.Status;\r
d7ce7006 1785\r
1786 //\r
a88c3163 1787 // +--------------------+ +---------------------------+\r
1788 // | ESL_IO_MGMT | | ESL_PACKET |\r
1789 // | | | |\r
1790 // | +---------------+ +-----------------------+ |\r
1791 // | | Token | | EFI_TCP4_RECEIVE_DATA | |\r
1792 // | | RxData --> | | |\r
1793 // | | | +-----------------------+---+\r
1794 // | | Event | | Data Buffer |\r
1795 // +----+---------------+ | |\r
1796 // | |\r
1797 // +---------------------------+\r
d7ce7006 1798 //\r
a88c3163 1799 //\r
1800 // Duplicate the buffer address and length for use by the\r
1801 // buffer handling code in EslTcp4Receive. These fields are\r
1802 // used when a partial read is done of the data from the\r
1803 // packet.\r
1804 //\r
1805 pPacket = pIo->pPacket;\r
1806 pPacket->pBuffer = pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentBuffer;\r
1807 LengthInBytes = pPacket->Op.Tcp4Rx.RxData.DataLength;\r
1808 pPacket->ValidBytes = LengthInBytes;\r
d7ce7006 1809\r
a88c3163 1810 //\r
1811 // Get the data type so that it may be linked to the\r
1812 // correct receive buffer list on the ESL_SOCKET structure\r
1813 //\r
1814 bUrgent = pPacket->Op.Tcp4Rx.RxData.UrgentFlag;\r
d7ce7006 1815\r
1816 //\r
a88c3163 1817 // Complete this request\r
d7ce7006 1818 //\r
a88c3163 1819 EslSocketRxComplete ( pIo, Status, LengthInBytes, bUrgent );\r
1820 DBG_EXIT ( );\r
1821}\r
1822\r
1823\r
1824/**\r
1825 Start a receive operation\r
1826\r
1827 This routine posts a receive buffer to the TCPv4 driver.\r
1828 See the \ref ReceiveEngine section.\r
1829\r
1830 This support routine is called by EslSocketRxStart.\r
1831\r
1832 @param [in] pPort Address of an ::ESL_PORT structure.\r
1833 @param [in] pIo Address of an ::ESL_IO_MGMT structure.\r
1834\r
1835 **/\r
1836VOID\r
1837EslTcp4RxStart (\r
1838 IN ESL_PORT * pPort,\r
1839 IN ESL_IO_MGMT * pIo\r
1840 )\r
1841{\r
1842 ESL_PACKET * pPacket;\r
1843\r
1844 DBG_ENTER ( );\r
1845\r
1846 //\r
1847 // Initialize the buffer for receive\r
1848 //\r
1849 pPacket = pIo->pPacket;\r
1850 pIo->Token.Tcp4Rx.Packet.RxData = &pPacket->Op.Tcp4Rx.RxData;\r
1851 pPacket->Op.Tcp4Rx.RxData.DataLength = sizeof ( pPacket->Op.Tcp4Rx.Buffer );\r
1852 pPacket->Op.Tcp4Rx.RxData.FragmentCount = 1;\r
1853 pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentLength = pPacket->Op.Tcp4Rx.RxData.DataLength;\r
1854 pPacket->Op.Tcp4Rx.RxData.FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp4Rx.Buffer[0];\r
d7ce7006 1855\r
1856 DBG_EXIT ( );\r
1857}\r
1858\r
1859\r
1860/**\r
1861 Determine if the socket is configured.\r
1862\r
a88c3163 1863 This routine uses the flag ESL_SOCKET::bConfigured to determine\r
1864 if the network layer's configuration routine has been called.\r
1865\r
1866 This routine is called by EslSocketIsConfigured to verify\r
1867 that the socket has been configured.\r
1868\r
1869 @param [in] pSocket Address of an ::ESL_SOCKET structure.\r
d7ce7006 1870\r
d7ce7006 1871 @retval EFI_SUCCESS - The port is connected\r
1872 @retval EFI_NOT_STARTED - The port is not connected\r
1873\r
1874 **/\r
1875 EFI_STATUS\r
a88c3163 1876 EslTcp4SocketIsConfigured (\r
1877 IN ESL_SOCKET * pSocket\r
d7ce7006 1878 )\r
1879{\r
1880 EFI_STATUS Status;\r
1881\r
1882 DBG_ENTER ( );\r
1883\r
1884 //\r
1885 // Determine the socket configuration status\r
1886 //\r
1887 Status = pSocket->bConfigured ? EFI_SUCCESS : EFI_NOT_STARTED;\r
1888\r
1889 //\r
1890 // Return the port connected state.\r
1891 //\r
1892 DBG_EXIT_STATUS ( Status );\r
1893 return Status;\r
1894}\r
1895\r
1896\r
1897/**\r
1898 Buffer data for transmission over a network connection.\r
1899\r
a88c3163 1900 This routine buffers data for the transmit engine in one of two\r
1901 queues, one for urgent (out-of-band) data and the other for normal\r
1902 data. The urgent data is provided to TCP as soon as it is available,\r
1903 allowing the TCP layer to schedule transmission of the urgent data\r
1904 between packets of normal data.\r
d7ce7006 1905\r
a88c3163 1906 This routine is called by ::EslSocketTransmit to buffer\r
1907 data for transmission. When the \ref TransmitEngine has resources,\r
1908 this routine will start the transmission of the next buffer on\r
1909 the network connection.\r
d7ce7006 1910\r
1911 Transmission errors are returned during the next transmission or\r
1912 during the close operation. Only buffering errors are returned\r
1913 during the current transmission attempt.\r
1914\r
a88c3163 1915 @param [in] pSocket Address of an ::ESL_SOCKET structure\r
d7ce7006 1916 \r
1917 @param [in] Flags Message control flags\r
1918 \r
1919 @param [in] BufferLength Length of the the buffer\r
1920 \r
1921 @param [in] pBuffer Address of a buffer to receive the data.\r
1922 \r
1923 @param [in] pDataLength Number of received data bytes in the buffer.\r
1924\r
a88c3163 1925 @param [in] pAddress Network address of the remote system address\r
1926\r
1927 @param [in] AddressLength Length of the remote network address structure\r
1928\r
d7ce7006 1929 @retval EFI_SUCCESS - Socket data successfully buffered\r
1930\r
1931 **/\r
1932EFI_STATUS\r
a88c3163 1933EslTcp4TxBuffer (\r
1934 IN ESL_SOCKET * pSocket,\r
d7ce7006 1935 IN int Flags,\r
1936 IN size_t BufferLength,\r
1937 IN CONST UINT8 * pBuffer,\r
a88c3163 1938 OUT size_t * pDataLength,\r
1939 IN const struct sockaddr * pAddress,\r
1940 IN socklen_t AddressLength\r
d7ce7006 1941 )\r
1942{\r
1943 BOOLEAN bUrgent;\r
a88c3163 1944 BOOLEAN bUrgentQueue;\r
1945 ESL_PACKET * pPacket;\r
1946 ESL_IO_MGMT ** ppActive;\r
1947 ESL_IO_MGMT ** ppFree;\r
1948 ESL_PORT * pPort;\r
1949 ESL_PACKET ** ppQueueHead;\r
1950 ESL_PACKET ** ppQueueTail;\r
1951 ESL_PACKET * pPreviousPacket;\r
1952 ESL_TCP4_CONTEXT * pTcp4;\r
d7ce7006 1953 size_t * pTxBytes;\r
1954 EFI_TCP4_TRANSMIT_DATA * pTxData;\r
1955 EFI_STATUS Status;\r
1956 EFI_TPL TplPrevious;\r
1957\r
1958 DBG_ENTER ( );\r
1959\r
1960 //\r
1961 // Assume failure\r
1962 //\r
1963 Status = EFI_UNSUPPORTED;\r
1964 pSocket->errno = ENOTCONN;\r
a88c3163 1965 *pDataLength = 0;\r
d7ce7006 1966\r
1967 //\r
1968 // Verify that the socket is connected\r
1969 //\r
1970 if ( SOCKET_STATE_CONNECTED == pSocket->State ) {\r
1971 //\r
1972 // Locate the port\r
1973 //\r
1974 pPort = pSocket->pPortList;\r
1975 if ( NULL != pPort ) {\r
1976 //\r
1977 // Determine the queue head\r
1978 //\r
1979 pTcp4 = &pPort->Context.Tcp4;\r
1980 bUrgent = (BOOLEAN)( 0 != ( Flags & MSG_OOB ));\r
a88c3163 1981 bUrgentQueue = bUrgent\r
1982 && ( !pSocket->bOobInLine )\r
1983 && pSocket->pApi->bOobSupported;\r
1984 if ( bUrgentQueue ) {\r
d7ce7006 1985 ppQueueHead = &pSocket->pTxOobPacketListHead;\r
1986 ppQueueTail = &pSocket->pTxOobPacketListTail;\r
a88c3163 1987 ppActive = &pPort->pTxOobActive;\r
1988 ppFree = &pPort->pTxOobFree;\r
d7ce7006 1989 pTxBytes = &pSocket->TxOobBytes;\r
1990 }\r
1991 else {\r
1992 ppQueueHead = &pSocket->pTxPacketListHead;\r
1993 ppQueueTail = &pSocket->pTxPacketListTail;\r
a88c3163 1994 ppActive = &pPort->pTxActive;\r
1995 ppFree = &pPort->pTxFree;\r
d7ce7006 1996 pTxBytes = &pSocket->TxBytes;\r
1997 }\r
1998\r
1999 //\r
2000 // Verify that there is enough room to buffer another\r
2001 // transmit operation\r
2002 //\r
2003 if ( pSocket->MaxTxBuf > *pTxBytes ) {\r
a88c3163 2004 if ( pPort->bTxFlowControl ) {\r
2005 DEBUG (( DEBUG_TX,\r
2006 "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n0x%08x: pPort, TX flow control released, Max bytes: %d > %d bufferred bytes\r\n",\r
2007 pPort,\r
2008 pSocket->MaxTxBuf,\r
2009 *pTxBytes ));\r
2010 pPort->bTxFlowControl = FALSE;\r
2011 }\r
2012\r
d7ce7006 2013 //\r
2014 // Attempt to allocate the packet\r
2015 //\r
2016 Status = EslSocketPacketAllocate ( &pPacket,\r
2017 sizeof ( pPacket->Op.Tcp4Tx )\r
2018 - sizeof ( pPacket->Op.Tcp4Tx.Buffer )\r
2019 + BufferLength,\r
a88c3163 2020 0,\r
d7ce7006 2021 DEBUG_TX );\r
2022 if ( !EFI_ERROR ( Status )) {\r
2023 //\r
2024 // Initialize the transmit operation\r
2025 //\r
2026 pTxData = &pPacket->Op.Tcp4Tx.TxData;\r
a88c3163 2027 pTxData->Push = TRUE || bUrgent;\r
d7ce7006 2028 pTxData->Urgent = bUrgent;\r
2029 pTxData->DataLength = (UINT32) BufferLength;\r
2030 pTxData->FragmentCount = 1;\r
2031 pTxData->FragmentTable[0].FragmentLength = (UINT32) BufferLength;\r
2032 pTxData->FragmentTable[0].FragmentBuffer = &pPacket->Op.Tcp4Tx.Buffer[0];\r
2033\r
2034 //\r
2035 // Copy the data into the buffer\r
2036 //\r
2037 CopyMem ( &pPacket->Op.Tcp4Tx.Buffer[0],\r
2038 pBuffer,\r
2039 BufferLength );\r
2040\r
2041 //\r
2042 // Synchronize with the socket layer\r
2043 //\r
2044 RAISE_TPL ( TplPrevious, TPL_SOCKETS );\r
2045\r
2046 //\r
2047 // Stop transmission after an error\r
2048 //\r
2049 if ( !EFI_ERROR ( pSocket->TxError )) {\r
2050 //\r
2051 // Display the request\r
2052 //\r
2053 DEBUG (( DEBUG_TX,\r
2054 "Send %d %s bytes from 0x%08x\r\n",\r
2055 BufferLength,\r
2056 bUrgent ? L"urgent" : L"normal",\r
2057 pBuffer ));\r
2058\r
2059 //\r
2060 // Queue the data for transmission\r
2061 //\r
2062 pPacket->pNext = NULL;\r
2063 pPreviousPacket = *ppQueueTail;\r
2064 if ( NULL == pPreviousPacket ) {\r
2065 *ppQueueHead = pPacket;\r
2066 }\r
2067 else {\r
2068 pPreviousPacket->pNext = pPacket;\r
2069 }\r
2070 *ppQueueTail = pPacket;\r
2071 DEBUG (( DEBUG_TX,\r
2072 "0x%08x: Packet on %s transmit list\r\n",\r
2073 pPacket,\r
a88c3163 2074 bUrgentQueue ? L"urgent" : L"normal" ));\r
d7ce7006 2075\r
2076 //\r
2077 // Account for the buffered data\r
2078 //\r
2079 *pTxBytes += BufferLength;\r
2080 *pDataLength = BufferLength;\r
2081\r
2082 //\r
2083 // Start the transmit engine if it is idle\r
2084 //\r
a88c3163 2085 if ( NULL != *ppFree ) {\r
2086 EslSocketTxStart ( pPort,\r
2087 ppQueueHead,\r
2088 ppQueueTail,\r
2089 ppActive,\r
2090 ppFree );\r
d7ce7006 2091 }\r
2092 }\r
2093 else {\r
2094 //\r
2095 // Previous transmit error\r
2096 // Stop transmission\r
2097 //\r
2098 Status = pSocket->TxError;\r
2099 pSocket->errno = EIO;\r
2100\r
2101 //\r
2102 // Free the packet\r
2103 //\r
2104 EslSocketPacketFree ( pPacket, DEBUG_TX );\r
2105 }\r
2106\r
2107 //\r
2108 // Release the socket layer synchronization\r
2109 //\r
2110 RESTORE_TPL ( TplPrevious );\r
2111 }\r
2112 else {\r
2113 //\r
2114 // Packet allocation failed\r
2115 //\r
2116 pSocket->errno = ENOMEM;\r
2117 }\r
2118 }\r
2119 else {\r
a88c3163 2120 if ( !pPort->bTxFlowControl ) {\r
2121 DEBUG (( DEBUG_TX,\r
2122 "0x%08x: pPort, TX flow control applied, Max bytes %d <= %d bufferred bytes\r\nTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT\r\n",\r
2123 pPort,\r
2124 pSocket->MaxTxBuf,\r
2125 *pTxBytes ));\r
2126 pPort->bTxFlowControl = TRUE;\r
2127 }\r
d7ce7006 2128 //\r
2129 // Not enough buffer space available\r
2130 //\r
2131 pSocket->errno = EAGAIN;\r
2132 Status = EFI_NOT_READY;\r
2133 }\r
2134 }\r
2135 }\r
2136\r
2137 //\r
2138 // Return the operation status\r
2139 //\r
2140 DBG_EXIT_STATUS ( Status );\r
2141 return Status;\r
2142}\r
2143\r
2144\r
2145/**\r
2146 Process the normal data transmit completion\r
2147\r
a88c3163 2148 This routine use ::EslSocketTxComplete to perform the transmit\r
2149 completion processing for normal data.\r
2150\r
2151 This routine is called by the TCPv4 network layer when a\r
2152 normal data transmit request completes.\r
2153\r
2154 @param [in] Event The normal transmit completion event\r
d7ce7006 2155\r
a88c3163 2156 @param [in] pIo The ESL_IO_MGMT structure address\r
d7ce7006 2157\r
2158**/\r
2159VOID\r
a88c3163 2160EslTcp4TxComplete (\r
d7ce7006 2161 IN EFI_EVENT Event,\r
a88c3163 2162 IN ESL_IO_MGMT * pIo\r
d7ce7006 2163 )\r
2164{\r
2165 UINT32 LengthInBytes;\r
a88c3163 2166 ESL_PACKET * pPacket;\r
2167 ESL_PORT * pPort;\r
2168 ESL_SOCKET * pSocket;\r
d7ce7006 2169 EFI_STATUS Status;\r
2170 \r
2171 DBG_ENTER ( );\r
a88c3163 2172\r
d7ce7006 2173 //\r
2174 // Locate the active transmit packet\r
2175 //\r
a88c3163 2176 pPacket = pIo->pPacket;\r
2177 pPort = pIo->pPort;\r
d7ce7006 2178 pSocket = pPort->pSocket;\r
a88c3163 2179\r
d7ce7006 2180 //\r
a88c3163 2181 // Get the transmit length and status\r
d7ce7006 2182 //\r
d7ce7006 2183 LengthInBytes = pPacket->Op.Tcp4Tx.TxData.DataLength;\r
2184 pSocket->TxBytes -= LengthInBytes;\r
a88c3163 2185 Status = pIo->Token.Tcp4Tx.CompletionToken.Status;\r
d7ce7006 2186\r
2187 //\r
a88c3163 2188 // Complete the transmit operation\r
d7ce7006 2189 //\r
a88c3163 2190 EslSocketTxComplete ( pIo,\r
2191 LengthInBytes,\r
2192 Status,\r
2193 "Normal ",\r
2194 &pSocket->pTxPacketListHead,\r
2195 &pSocket->pTxPacketListTail,\r
2196 &pPort->pTxActive,\r
2197 &pPort->pTxFree );\r
d7ce7006 2198 DBG_EXIT ( );\r
2199}\r
2200\r
2201\r
2202/**\r
2203 Process the urgent data transmit completion\r
2204\r
a88c3163 2205 This routine use ::EslSocketTxComplete to perform the transmit\r
2206 completion processing for urgent data.\r
d7ce7006 2207\r
a88c3163 2208 This routine is called by the TCPv4 network layer when a\r
2209 urgent data transmit request completes.\r
2210\r
2211 @param [in] Event The urgent transmit completion event\r
2212\r
2213 @param [in] pIo The ESL_IO_MGMT structure address\r
d7ce7006 2214\r
2215**/\r
2216VOID\r
a88c3163 2217EslTcp4TxOobComplete (\r
d7ce7006 2218 IN EFI_EVENT Event,\r
a88c3163 2219 IN ESL_IO_MGMT * pIo\r
d7ce7006 2220 )\r
2221{\r
2222 UINT32 LengthInBytes;\r
a88c3163 2223 ESL_PACKET * pPacket;\r
2224 ESL_PORT * pPort;\r
2225 ESL_SOCKET * pSocket;\r
d7ce7006 2226 EFI_STATUS Status;\r
2227\r
2228 DBG_ENTER ( );\r
2229\r
2230 //\r
2231 // Locate the active transmit packet\r
2232 //\r
a88c3163 2233 pPacket = pIo->pPacket;\r
2234 pPort = pIo->pPort;\r
d7ce7006 2235 pSocket = pPort->pSocket;\r
d7ce7006 2236\r
2237 //\r
a88c3163 2238 // Get the transmit length and status\r
d7ce7006 2239 //\r
d7ce7006 2240 LengthInBytes = pPacket->Op.Tcp4Tx.TxData.DataLength;\r
2241 pSocket->TxOobBytes -= LengthInBytes;\r
a88c3163 2242 Status = pIo->Token.Tcp4Tx.CompletionToken.Status;\r
d7ce7006 2243\r
2244 //\r
a88c3163 2245 // Complete the transmit operation\r
d7ce7006 2246 //\r
a88c3163 2247 EslSocketTxComplete ( pIo,\r
2248 LengthInBytes,\r
2249 Status,\r
2250 "Urgent ",\r
2251 &pSocket->pTxOobPacketListHead,\r
2252 &pSocket->pTxOobPacketListTail,\r
2253 &pPort->pTxOobActive,\r
2254 &pPort->pTxOobFree );\r
d7ce7006 2255 DBG_EXIT ( );\r
2256}\r
2257\r
2258\r
2259/**\r
a88c3163 2260 Interface between the socket layer and the network specific\r
2261 code that supports SOCK_STREAM and SOCK_SEQPACKET sockets\r
2262 over TCPv4.\r
2263**/\r
2264CONST ESL_PROTOCOL_API cEslTcp4Api = {\r
2265 "TCPv4",\r
2266 IPPROTO_TCP,\r
2267 OFFSET_OF ( ESL_PORT, Context.Tcp4.ConfigData ),\r
2268 OFFSET_OF ( ESL_LAYER, pTcp4List ),\r
2269 OFFSET_OF ( struct sockaddr_in, sin_zero ),\r
2270 sizeof ( struct sockaddr_in ),\r
2271 AF_INET,\r
2272 sizeof (((ESL_PACKET *)0 )->Op.Tcp4Rx ),\r
2273 OFFSET_OF ( ESL_PACKET, Op.Tcp4Rx.Buffer ) - OFFSET_OF ( ESL_PACKET, Op ),\r
2274 OFFSET_OF ( ESL_IO_MGMT, Token.Tcp4Rx.Packet.RxData ),\r
2275 TRUE,\r
2276 EADDRINUSE,\r
2277 EslTcp4Accept,\r
2278 EslTcp4ConnectPoll,\r
2279 EslTcp4ConnectStart,\r
2280 EslTcp4SocketIsConfigured,\r
2281 EslTcp4LocalAddressGet,\r
2282 EslTcp4LocalAddressSet,\r
2283 EslTcp4Listen,\r
2284 NULL, // OptionGet\r
2285 NULL, // OptionSet\r
2286 EslTcp4PacketFree,\r
2287 EslTcp4PortAllocate,\r
2288 EslTcp4PortClose,\r
2289 EslTcp4PortCloseOp,\r
2290 FALSE,\r
2291 EslTcp4Receive,\r
2292 EslTcp4RemoteAddressGet,\r
2293 EslTcp4RemoteAddressSet,\r
2294 EslTcp4RxComplete,\r
2295 EslTcp4RxStart,\r
2296 EslTcp4TxBuffer,\r
2297 EslTcp4TxComplete,\r
2298 EslTcp4TxOobComplete\r
2299};\r