1 /** @file
2   The wrap of TCP/IP Socket interface.
3 
4 Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "IScsiImpl.h"
16 
17 /**
18   The common notify function associated with various Tcp4Io events.
19 
20   @param[in]  Event   The event signaled.
21   @param[in]  Context The context.
22 **/
23 VOID
24 EFIAPI
Tcp4IoCommonNotify(IN EFI_EVENT Event,IN VOID * Context)25 Tcp4IoCommonNotify (
26   IN EFI_EVENT  Event,
27   IN VOID       *Context
28   )
29 {
30   *((BOOLEAN *) Context) = TRUE;
31 }
32 
33 /**
34   Create a TCP socket with the specified configuration data.
35 
36   @param[in]  Image      The handle of the driver image.
37   @param[in]  Controller The handle of the controller.
38   @param[in]  ConfigData The Tcp4 configuration data.
39   @param[in]  Tcp4Io     The Tcp4Io.
40 
41   @retval EFI_SUCCESS    The TCP socket is created and configured.
42   @retval Others         Failed to create the TCP socket or configure it.
43 **/
44 EFI_STATUS
Tcp4IoCreateSocket(IN EFI_HANDLE Image,IN EFI_HANDLE Controller,IN TCP4_IO_CONFIG_DATA * ConfigData,IN TCP4_IO * Tcp4Io)45 Tcp4IoCreateSocket (
46   IN EFI_HANDLE           Image,
47   IN EFI_HANDLE           Controller,
48   IN TCP4_IO_CONFIG_DATA  *ConfigData,
49   IN TCP4_IO              *Tcp4Io
50   )
51 {
52   EFI_STATUS            Status;
53   EFI_TCP4_PROTOCOL     *Tcp4;
54   EFI_TCP4_CONFIG_DATA  Tcp4ConfigData;
55   EFI_TCP4_OPTION       ControlOption;
56   EFI_TCP4_ACCESS_POINT *AccessPoint;
57 
58   Tcp4Io->Handle = NULL;
59   Tcp4Io->ConnToken.CompletionToken.Event = NULL;
60   Tcp4Io->TxToken.CompletionToken.Event = NULL;
61   Tcp4Io->RxToken.CompletionToken.Event = NULL;
62   Tcp4Io->CloseToken.CompletionToken.Event = NULL;
63   Tcp4 = NULL;
64 
65   //
66   // Create the TCP4 child instance and get the TCP4 protocol.
67   //
68   Status = NetLibCreateServiceChild (
69             Controller,
70             Image,
71             &gEfiTcp4ServiceBindingProtocolGuid,
72             &Tcp4Io->Handle
73             );
74   if (EFI_ERROR (Status)) {
75     return Status;
76   }
77 
78   Status = gBS->OpenProtocol (
79                   Tcp4Io->Handle,
80                   &gEfiTcp4ProtocolGuid,
81                   (VOID **)&Tcp4Io->Tcp4,
82                   Image,
83                   Controller,
84                   EFI_OPEN_PROTOCOL_BY_DRIVER
85                   );
86   if (EFI_ERROR (Status)) {
87     goto ON_ERROR;
88   }
89 
90   Tcp4Io->Image       = Image;
91   Tcp4Io->Controller  = Controller;
92   Tcp4                = Tcp4Io->Tcp4;
93 
94   //
95   // Set the configuration parameters.
96   //
97   ControlOption.ReceiveBufferSize       = 0x200000;
98   ControlOption.SendBufferSize          = 0x200000;
99   ControlOption.MaxSynBackLog           = 0;
100   ControlOption.ConnectionTimeout       = 0;
101   ControlOption.DataRetries             = 6;
102   ControlOption.FinTimeout              = 0;
103   ControlOption.TimeWaitTimeout         = 0;
104   ControlOption.KeepAliveProbes         = 4;
105   ControlOption.KeepAliveTime           = 0;
106   ControlOption.KeepAliveInterval       = 0;
107   ControlOption.EnableNagle             = FALSE;
108   ControlOption.EnableTimeStamp         = FALSE;
109   ControlOption.EnableWindowScaling     = TRUE;
110   ControlOption.EnableSelectiveAck      = FALSE;
111   ControlOption.EnablePathMtuDiscovery  = FALSE;
112 
113   Tcp4ConfigData.TypeOfService          = 8;
114   Tcp4ConfigData.TimeToLive             = 255;
115   Tcp4ConfigData.ControlOption          = &ControlOption;
116 
117   AccessPoint = &Tcp4ConfigData.AccessPoint;
118 
119   AccessPoint->UseDefaultAddress = FALSE;
120   AccessPoint->StationPort = 0;
121   AccessPoint->RemotePort = ConfigData->RemotePort;
122   AccessPoint->ActiveFlag = TRUE;
123 
124   CopyMem (&AccessPoint->StationAddress, &ConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS));
125   CopyMem (&AccessPoint->SubnetMask, &ConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
126   CopyMem (&AccessPoint->RemoteAddress, &ConfigData->RemoteIp, sizeof (EFI_IPv4_ADDRESS));
127 
128   //
129   // Configure the TCP4 protocol.
130   //
131   Status = Tcp4->Configure (Tcp4, &Tcp4ConfigData);
132   if (EFI_ERROR (Status)) {
133     goto ON_ERROR;
134   }
135 
136   if (!EFI_IP4_EQUAL (&ConfigData->Gateway, &mZeroIp4Addr)) {
137     //
138     // the gateway is not zero, add the default route by hand
139     //
140     Status = Tcp4->Routes (Tcp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, &ConfigData->Gateway);
141     if (EFI_ERROR (Status)) {
142       goto ON_ERROR;
143     }
144   }
145   //
146   // Create events for variuos asynchronous operations.
147   //
148   Status = gBS->CreateEvent (
149                   EVT_NOTIFY_SIGNAL,
150                   TPL_NOTIFY,
151                   Tcp4IoCommonNotify,
152                   &Tcp4Io->IsConnDone,
153                   &Tcp4Io->ConnToken.CompletionToken.Event
154                   );
155   if (EFI_ERROR (Status)) {
156     goto ON_ERROR;
157   }
158 
159   Status = gBS->CreateEvent (
160                   EVT_NOTIFY_SIGNAL,
161                   TPL_NOTIFY,
162                   Tcp4IoCommonNotify,
163                   &Tcp4Io->IsTxDone,
164                   &Tcp4Io->TxToken.CompletionToken.Event
165                   );
166   if (EFI_ERROR (Status)) {
167     goto ON_ERROR;
168   }
169 
170   Status = gBS->CreateEvent (
171                   EVT_NOTIFY_SIGNAL,
172                   TPL_NOTIFY,
173                   Tcp4IoCommonNotify,
174                   &Tcp4Io->IsRxDone,
175                   &Tcp4Io->RxToken.CompletionToken.Event
176                   );
177   if (EFI_ERROR (Status)) {
178     goto ON_ERROR;
179   }
180 
181   Status = gBS->CreateEvent (
182                   EVT_NOTIFY_SIGNAL,
183                   TPL_NOTIFY,
184                   Tcp4IoCommonNotify,
185                   &Tcp4Io->IsCloseDone,
186                   &Tcp4Io->CloseToken.CompletionToken.Event
187                   );
188   if (EFI_ERROR (Status)) {
189     goto ON_ERROR;
190   }
191 
192   Tcp4Io->IsTxDone  = FALSE;
193   Tcp4Io->IsRxDone  = FALSE;
194 
195   return EFI_SUCCESS;
196 
197 ON_ERROR:
198 
199   if (Tcp4Io->RxToken.CompletionToken.Event != NULL) {
200     gBS->CloseEvent (Tcp4Io->RxToken.CompletionToken.Event);
201   }
202 
203   if (Tcp4Io->TxToken.CompletionToken.Event != NULL) {
204     gBS->CloseEvent (Tcp4Io->TxToken.CompletionToken.Event);
205   }
206 
207   if (Tcp4Io->ConnToken.CompletionToken.Event != NULL) {
208     gBS->CloseEvent (Tcp4Io->ConnToken.CompletionToken.Event);
209   }
210 
211   if (Tcp4 != NULL) {
212     Tcp4->Configure (Tcp4, NULL);
213 
214     gBS->CloseProtocol (
215           Tcp4Io->Handle,
216           &gEfiTcp4ProtocolGuid,
217           Image,
218           Controller
219           );
220   }
221 
222   NetLibDestroyServiceChild (
223     Controller,
224     Image,
225     &gEfiTcp4ServiceBindingProtocolGuid,
226     Tcp4Io->Handle
227     );
228 
229   return Status;
230 }
231 
232 /**
233   Destroy the socket.
234 
235   @param[in]  Tcp4Io The Tcp4Io which wraps the socket to be destroyeds.
236 **/
237 VOID
Tcp4IoDestroySocket(IN TCP4_IO * Tcp4Io)238 Tcp4IoDestroySocket (
239   IN TCP4_IO  *Tcp4Io
240   )
241 {
242   EFI_TCP4_PROTOCOL *Tcp4;
243 
244   Tcp4 = Tcp4Io->Tcp4;
245 
246   Tcp4->Configure (Tcp4, NULL);
247 
248   gBS->CloseEvent (Tcp4Io->TxToken.CompletionToken.Event);
249   gBS->CloseEvent (Tcp4Io->RxToken.CompletionToken.Event);
250   gBS->CloseEvent (Tcp4Io->ConnToken.CompletionToken.Event);
251 
252   gBS->CloseProtocol (
253         Tcp4Io->Handle,
254         &gEfiTcp4ProtocolGuid,
255         Tcp4Io->Image,
256         Tcp4Io->Controller
257         );
258 
259   NetLibDestroyServiceChild (
260     Tcp4Io->Controller,
261     Tcp4Io->Image,
262     &gEfiTcp4ServiceBindingProtocolGuid,
263     Tcp4Io->Handle
264     );
265 }
266 
267 /**
268   Connect to the other endpoint of the TCP socket.
269 
270   @param[in, out]  Tcp4Io    The Tcp4Io wrapping the TCP socket.
271   @param[in]       Timeout   The time to wait for connection done.
272 
273   @retval EFI_SUCCESS          Connect to the other endpoint of the TCP socket successfully.
274   @retval EFI_TIMEOUT          Failed to connect to the other endpoint of the TCP socket in the                               specified time period.
275   @retval Others               Other errors as indicated.
276 **/
277 EFI_STATUS
Tcp4IoConnect(IN OUT TCP4_IO * Tcp4Io,IN EFI_EVENT Timeout)278 Tcp4IoConnect (
279   IN OUT TCP4_IO    *Tcp4Io,
280   IN EFI_EVENT      Timeout
281   )
282 {
283   EFI_TCP4_PROTOCOL *Tcp4;
284   EFI_STATUS        Status;
285 
286   Tcp4Io->IsConnDone  = FALSE;
287   Tcp4                = Tcp4Io->Tcp4;
288   Status              = Tcp4->Connect (Tcp4, &Tcp4Io->ConnToken);
289   if (EFI_ERROR (Status)) {
290     return Status;
291   }
292 
293   while (!Tcp4Io->IsConnDone && EFI_ERROR (gBS->CheckEvent (Timeout))) {
294     Tcp4->Poll (Tcp4);
295   }
296 
297   if (!Tcp4Io->IsConnDone) {
298     Status = EFI_TIMEOUT;
299   } else {
300     Status = Tcp4Io->ConnToken.CompletionToken.Status;
301   }
302 
303   return Status;
304 }
305 
306 /**
307   Reset the socket.
308 
309   @param[in, out]  Tcp4Io The Tcp4Io wrapping the TCP socket.
310 **/
311 VOID
Tcp4IoReset(IN OUT TCP4_IO * Tcp4Io)312 Tcp4IoReset (
313   IN OUT TCP4_IO  *Tcp4Io
314   )
315 {
316   EFI_STATUS        Status;
317   EFI_TCP4_PROTOCOL *Tcp4;
318 
319   Tcp4Io->CloseToken.AbortOnClose = TRUE;
320   Tcp4Io->IsCloseDone             = FALSE;
321 
322   Tcp4 = Tcp4Io->Tcp4;
323   Status = Tcp4->Close (Tcp4, &Tcp4Io->CloseToken);
324   if (EFI_ERROR (Status)) {
325     return ;
326   }
327 
328   while (!Tcp4Io->IsCloseDone) {
329     Tcp4->Poll (Tcp4);
330   }
331 }
332 
333 /**
334   Transmit the Packet to the other endpoint of the socket.
335 
336   @param[in]   Tcp4Io          The Tcp4Io wrapping the TCP socket.
337   @param[in]   Packet          The packet to transmit.
338 
339   @retval EFI_SUCCESS          The packet is trasmitted.
340   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
341   @retval Others               Other errors as indicated.
342 **/
343 EFI_STATUS
Tcp4IoTransmit(IN TCP4_IO * Tcp4Io,IN NET_BUF * Packet)344 Tcp4IoTransmit (
345   IN TCP4_IO  *Tcp4Io,
346   IN NET_BUF  *Packet
347   )
348 {
349   EFI_TCP4_TRANSMIT_DATA  *TxData;
350   EFI_TCP4_PROTOCOL       *Tcp4;
351   EFI_STATUS              Status;
352 
353   TxData = AllocatePool (sizeof (EFI_TCP4_TRANSMIT_DATA) + (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA));
354   if (TxData == NULL) {
355     return EFI_OUT_OF_RESOURCES;
356   }
357 
358   TxData->Push        = TRUE;
359   TxData->Urgent      = FALSE;
360   TxData->DataLength  = Packet->TotalSize;
361 
362   //
363   // Build the fragment table.
364   //
365   TxData->FragmentCount = Packet->BlockOpNum;
366   NetbufBuildExt (Packet, (NET_FRAGMENT *) &TxData->FragmentTable[0], &TxData->FragmentCount);
367 
368   Tcp4Io->TxToken.Packet.TxData = TxData;
369 
370   //
371   // Trasnmit the packet.
372   //
373   Tcp4    = Tcp4Io->Tcp4;
374   Status  = Tcp4->Transmit (Tcp4, &Tcp4Io->TxToken);
375   if (EFI_ERROR (Status)) {
376     goto ON_EXIT;
377   }
378 
379   while (!Tcp4Io->IsTxDone) {
380     Tcp4->Poll (Tcp4);
381   }
382 
383   Tcp4Io->IsTxDone  = FALSE;
384 
385   Status            = Tcp4Io->TxToken.CompletionToken.Status;
386 
387 ON_EXIT:
388 
389   FreePool (TxData);
390 
391   return Status;
392 }
393 
394 /**
395   Receive data from the socket.
396 
397   @param[in]  Tcp4Io           The Tcp4Io which wraps the socket to be destroyed.
398   @param[in]  Packet           The buffer to hold the data copy from the soket rx buffer.
399   @param[in]  AsyncMode        Is this receive asyncronous or not.
400   @param[in]  Timeout          The time to wait for receiving the amount of data the Packet
401                                can hold.
402 
403   @retval EFI_SUCCESS          The required amount of data is received from the socket.
404   @retval EFI_OUT_OF_RESOURCES Failed to allocate momery.
405   @retval EFI_TIMEOUT          Failed to receive the required amount of data in the
406                                specified time period.
407   @retval Others               Other errors as indicated.
408 **/
409 EFI_STATUS
Tcp4IoReceive(IN TCP4_IO * Tcp4Io,IN NET_BUF * Packet,IN BOOLEAN AsyncMode,IN EFI_EVENT Timeout)410 Tcp4IoReceive (
411   IN TCP4_IO    *Tcp4Io,
412   IN NET_BUF    *Packet,
413   IN BOOLEAN    AsyncMode,
414   IN EFI_EVENT  Timeout
415   )
416 {
417   EFI_TCP4_PROTOCOL     *Tcp4;
418   EFI_TCP4_RECEIVE_DATA RxData;
419   EFI_STATUS            Status;
420   NET_FRAGMENT          *Fragment;
421   UINT32                FragmentCount;
422   UINT32                CurrentFragment;
423 
424   FragmentCount = Packet->BlockOpNum;
425   Fragment      = AllocatePool (FragmentCount * sizeof (NET_FRAGMENT));
426   if (Fragment == NULL) {
427     return EFI_OUT_OF_RESOURCES;
428   }
429   //
430   // Build the fragment table.
431   //
432   NetbufBuildExt (Packet, Fragment, &FragmentCount);
433 
434   RxData.FragmentCount          = 1;
435   Tcp4Io->RxToken.Packet.RxData = &RxData;
436   CurrentFragment               = 0;
437   Tcp4                          = Tcp4Io->Tcp4;
438   Status                        = EFI_SUCCESS;
439 
440   while (CurrentFragment < FragmentCount) {
441     RxData.DataLength                       = Fragment[CurrentFragment].Len;
442     RxData.FragmentTable[0].FragmentLength  = Fragment[CurrentFragment].Len;
443     RxData.FragmentTable[0].FragmentBuffer  = Fragment[CurrentFragment].Bulk;
444 
445     Status = Tcp4->Receive (Tcp4, &Tcp4Io->RxToken);
446     if (EFI_ERROR (Status)) {
447       goto ON_EXIT;
448     }
449 
450     while (!Tcp4Io->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {
451       //
452       // Poll until some data is received or something error happens.
453       //
454       Tcp4->Poll (Tcp4);
455     }
456 
457     if (!Tcp4Io->IsRxDone) {
458       //
459       // Timeout occurs, cancel the receive request.
460       //
461       Tcp4->Cancel (Tcp4, &Tcp4Io->RxToken.CompletionToken);
462 
463       Status = EFI_TIMEOUT;
464       goto ON_EXIT;
465     } else {
466       Tcp4Io->IsRxDone = FALSE;
467     }
468 
469     if (EFI_ERROR (Tcp4Io->RxToken.CompletionToken.Status)) {
470       Status = Tcp4Io->RxToken.CompletionToken.Status;
471       goto ON_EXIT;
472     }
473 
474     Fragment[CurrentFragment].Len -= RxData.FragmentTable[0].FragmentLength;
475     if (Fragment[CurrentFragment].Len == 0) {
476       CurrentFragment++;
477     } else {
478       Fragment[CurrentFragment].Bulk += RxData.FragmentTable[0].FragmentLength;
479     }
480   }
481 
482 ON_EXIT:
483   Tcp4Io->RxToken.Packet.RxData = NULL;
484   FreePool (Fragment);
485 
486   return Status;
487 }
488