5e121a5dcf5f62d6603ad070c867fb75fc3e2c24
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Ip4Dxe / Ip4If.c
1 /** @file\r
2 \r
3 Copyright (c) 2005 - 2007, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 \r
13 Module Name:\r
14 \r
15   Ip4If.c\r
16 \r
17 Abstract:\r
18 \r
19   Implement IP4 pesudo interface.\r
20 \r
21 \r
22 **/\r
23 \r
24 #include "Ip4Impl.h"\r
25 \r
26 //\r
27 // Mac address with all zero, used to determine whethter the ARP\r
28 // resolve succeeded. Failed ARP requests zero the MAC address buffer.\r
29 //\r
30 STATIC EFI_MAC_ADDRESS  mZeroMacAddress;\r
31 \r
32 STATIC\r
33 VOID\r
34 EFIAPI\r
35 Ip4OnFrameSentDpc (\r
36   IN VOID                   *Context\r
37   );\r
38 \r
39 STATIC\r
40 VOID\r
41 EFIAPI\r
42 Ip4OnFrameSent (\r
43   IN EFI_EVENT              Event,\r
44   IN VOID                   *Context\r
45   );\r
46 \r
47 STATIC\r
48 VOID\r
49 EFIAPI\r
50 Ip4OnArpResolvedDpc (\r
51   IN VOID                   *Context\r
52   );\r
53 \r
54 STATIC\r
55 VOID\r
56 EFIAPI\r
57 Ip4OnArpResolved (\r
58   IN EFI_EVENT              Event,\r
59   IN VOID                   *Context\r
60   );\r
61 \r
62 STATIC\r
63 VOID\r
64 EFIAPI\r
65 Ip4OnFrameReceivedDpc (\r
66   IN VOID                   *Context\r
67   );\r
68 \r
69 STATIC\r
70 VOID\r
71 EFIAPI\r
72 Ip4OnFrameReceived (\r
73   IN EFI_EVENT              Event,\r
74   IN VOID                   *Context\r
75   );\r
76 \r
77 STATIC\r
78 VOID\r
79 Ip4CancelFrameArp (\r
80   IN IP4_ARP_QUE            *ArpQue,\r
81   IN EFI_STATUS             IoStatus,\r
82   IN IP4_FRAME_TO_CANCEL    FrameToCancel, OPTIONAL\r
83   IN VOID                   *Context\r
84   );\r
85 \r
86 \r
87 /**\r
88   Wrap a transmit request into a newly allocated IP4_LINK_TX_TOKEN.\r
89 \r
90   @param  Interface             The interface to send out from\r
91   @param  IpInstance            The IpInstance that transmit the packet.  NULL if\r
92                                 the packet is sent by the IP4 driver itself.\r
93   @param  Packet                The packet to transmit\r
94   @param  CallBack              Call back function to execute if transmission\r
95                                 finished.\r
96   @param  Context               Opaque parameter to the call back.\r
97 \r
98   @return The wrapped token if succeed or NULL\r
99 \r
100 **/\r
101 STATIC\r
102 IP4_LINK_TX_TOKEN *\r
103 Ip4WrapLinkTxToken (\r
104   IN IP4_INTERFACE          *Interface,\r
105   IN IP4_PROTOCOL           *IpInstance,    OPTIONAL\r
106   IN NET_BUF                *Packet,\r
107   IN IP4_FRAME_CALLBACK     CallBack,\r
108   IN VOID                   *Context\r
109   )\r
110 {\r
111   EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
112   EFI_MANAGED_NETWORK_TRANSMIT_DATA     *MnpTxData;\r
113   IP4_LINK_TX_TOKEN                     *Token;\r
114   EFI_STATUS                            Status;\r
115   UINT32                                Count;\r
116 \r
117   Token = AllocatePool (sizeof (IP4_LINK_TX_TOKEN) + \\r
118             (Packet->BlockOpNum - 1) * sizeof (EFI_MANAGED_NETWORK_FRAGMENT_DATA));\r
119 \r
120   if (Token == NULL) {\r
121     return NULL;\r
122   }\r
123 \r
124   Token->Signature = IP4_FRAME_TX_SIGNATURE;\r
125   InitializeListHead (&Token->Link);\r
126 \r
127   Token->Interface  = Interface;\r
128   Token->IpInstance = IpInstance;\r
129   Token->CallBack   = CallBack;\r
130   Token->Packet     = Packet;\r
131   Token->Context    = Context;\r
132   CopyMem (&Token->DstMac, &mZeroMacAddress, sizeof (Token->DstMac));\r
133   CopyMem (&Token->SrcMac, &Interface->Mac, sizeof (Token->SrcMac));\r
134 \r
135   MnpToken          = &(Token->MnpToken);\r
136   MnpToken->Status  = EFI_NOT_READY;\r
137 \r
138   Status = gBS->CreateEvent (\r
139                   EVT_NOTIFY_SIGNAL,\r
140                   TPL_NOTIFY,\r
141                   Ip4OnFrameSent,\r
142                   Token,\r
143                   &MnpToken->Event\r
144                   );\r
145 \r
146   if (EFI_ERROR (Status)) {\r
147     gBS->FreePool (Token);\r
148     return NULL;\r
149   }\r
150 \r
151   MnpTxData                     = &Token->MnpTxData;\r
152   MnpToken->Packet.TxData       = MnpTxData;\r
153 \r
154   MnpTxData->DestinationAddress = &Token->DstMac;\r
155   MnpTxData->SourceAddress      = &Token->SrcMac;\r
156   MnpTxData->ProtocolType       = IP4_ETHER_PROTO;\r
157   MnpTxData->DataLength         = Packet->TotalSize;\r
158   MnpTxData->HeaderLength       = 0;\r
159 \r
160   Count                         = Packet->BlockOpNum;\r
161 \r
162   NetbufBuildExt (Packet, (NET_FRAGMENT *) MnpTxData->FragmentTable, &Count);\r
163   MnpTxData->FragmentCount      = (UINT16)Count;\r
164 \r
165   return Token;\r
166 }\r
167 \r
168 \r
169 /**\r
170   Free the link layer transmit token. It will close the event\r
171   then free the memory used.\r
172 \r
173   @param  Token                 Token to free\r
174 \r
175   @return NONE\r
176 \r
177 **/\r
178 STATIC\r
179 VOID\r
180 Ip4FreeLinkTxToken (\r
181   IN IP4_LINK_TX_TOKEN      *Token\r
182   )\r
183 {\r
184   NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);\r
185 \r
186   gBS->CloseEvent (Token->MnpToken.Event);\r
187   gBS->FreePool (Token);\r
188 }\r
189 \r
190 \r
191 /**\r
192   Create an IP_ARP_QUE structure to request ARP service.\r
193 \r
194   @param  Interface             The interface to send ARP from.\r
195   @param  DestIp                The destination IP (host byte order) to request MAC\r
196                                 for\r
197 \r
198   @return Point to newly created IP4_ARP_QUE if succeed, otherwise NULL.\r
199 \r
200 **/\r
201 STATIC\r
202 IP4_ARP_QUE *\r
203 Ip4CreateArpQue (\r
204   IN IP4_INTERFACE          *Interface,\r
205   IN IP4_ADDR               DestIp\r
206   )\r
207 {\r
208   IP4_ARP_QUE               *ArpQue;\r
209   EFI_STATUS                Status;\r
210 \r
211   ArpQue = AllocatePool (sizeof (IP4_ARP_QUE));\r
212 \r
213   if (ArpQue == NULL) {\r
214     return NULL;\r
215   }\r
216 \r
217   ArpQue->Signature = IP4_FRAME_ARP_SIGNATURE;\r
218   InitializeListHead (&ArpQue->Link);\r
219 \r
220   InitializeListHead (&ArpQue->Frames);\r
221   ArpQue->Interface = Interface;\r
222 \r
223   Status = gBS->CreateEvent (\r
224                   EVT_NOTIFY_SIGNAL,\r
225                   TPL_NOTIFY,\r
226                   Ip4OnArpResolved,\r
227                   ArpQue,\r
228                   &ArpQue->OnResolved\r
229                   );\r
230 \r
231   if (EFI_ERROR (Status)) {\r
232     gBS->FreePool (ArpQue);\r
233     return NULL;\r
234   }\r
235 \r
236   ArpQue->Ip  = DestIp;\r
237   CopyMem (&ArpQue->Mac, &mZeroMacAddress, sizeof (ArpQue->Mac));\r
238 \r
239   return ArpQue;\r
240 }\r
241 \r
242 \r
243 /**\r
244   Remove all the transmit requests queued on the ARP queue, then free it.\r
245 \r
246   @param  ArpQue                Arp queue to free\r
247   @param  IoStatus              The transmit status returned to transmit requests'\r
248                                 callback.\r
249 \r
250   @return NONE\r
251 \r
252 **/\r
253 STATIC\r
254 VOID\r
255 Ip4FreeArpQue (\r
256   IN IP4_ARP_QUE            *ArpQue,\r
257   IN EFI_STATUS             IoStatus\r
258   )\r
259 {\r
260   NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);\r
261 \r
262   //\r
263   // Remove all the frame waiting the ARP response\r
264   //\r
265   Ip4CancelFrameArp (ArpQue, IoStatus, NULL, NULL);\r
266 \r
267   gBS->CloseEvent (ArpQue->OnResolved);\r
268   gBS->FreePool (ArpQue);\r
269 }\r
270 \r
271 \r
272 /**\r
273   Create a link layer receive token to wrap the receive request\r
274 \r
275   @param  Interface             The interface to receive from\r
276   @param  IpInstance            The instance that request the receive (NULL for IP4\r
277                                 driver itself)\r
278   @param  CallBack              Call back function to execute when finished.\r
279   @param  Context               Opaque parameters to the callback\r
280 \r
281   @return Point to created IP4_LINK_RX_TOKEN if succeed, otherwise NULL.\r
282 \r
283 **/\r
284 STATIC\r
285 IP4_LINK_RX_TOKEN *\r
286 Ip4CreateLinkRxToken (\r
287   IN IP4_INTERFACE          *Interface,\r
288   IN IP4_PROTOCOL           *IpInstance,\r
289   IN IP4_FRAME_CALLBACK     CallBack,\r
290   IN VOID                   *Context\r
291   )\r
292 {\r
293   EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
294   IP4_LINK_RX_TOKEN                     *Token;\r
295   EFI_STATUS                            Status;\r
296 \r
297   Token = AllocatePool (sizeof (IP4_LINK_RX_TOKEN));\r
298   if (Token == NULL) {\r
299     return NULL;\r
300   }\r
301 \r
302   Token->Signature  = IP4_FRAME_RX_SIGNATURE;\r
303   Token->Interface  = Interface;\r
304   Token->IpInstance = IpInstance;\r
305   Token->CallBack   = CallBack;\r
306   Token->Context    = Context;\r
307 \r
308   MnpToken          = &Token->MnpToken;\r
309   MnpToken->Status  = EFI_NOT_READY;\r
310 \r
311   Status = gBS->CreateEvent (\r
312                   EVT_NOTIFY_SIGNAL,\r
313                   TPL_NOTIFY,\r
314                   Ip4OnFrameReceived,\r
315                   Token,\r
316                   &MnpToken->Event\r
317                   );\r
318 \r
319   if (EFI_ERROR (Status)) {\r
320     gBS->FreePool (Token);\r
321     return NULL;\r
322   }\r
323 \r
324   MnpToken->Packet.RxData = NULL;\r
325   return Token;\r
326 }\r
327 \r
328 \r
329 /**\r
330   Free the link layer request token. It will close the event\r
331   then free the memory used.\r
332 \r
333   @param  Token                 Request token to free\r
334 \r
335   @return NONE\r
336 \r
337 **/\r
338 STATIC\r
339 VOID\r
340 Ip4FreeFrameRxToken (\r
341   IN IP4_LINK_RX_TOKEN      *Token\r
342   )\r
343 {\r
344 \r
345   NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);\r
346 \r
347   gBS->CloseEvent (Token->MnpToken.Event);\r
348   gBS->FreePool (Token);\r
349 }\r
350 \r
351 \r
352 /**\r
353   Remove all the frames on the ARP queue that pass the FrameToCancel,\r
354   that is, either FrameToCancel is NULL or it returns true for the frame.\r
355 \r
356   @param  ArpQue                ARP frame to remove the frames from.\r
357   @param  IoStatus              The status returned to the cancelled frames'\r
358                                 callback function.\r
359   @param  FrameToCancel         Function to select which frame to cancel.\r
360   @param  Context               Opaque parameter to the FrameToCancel.\r
361 \r
362   @return NONE\r
363 \r
364 **/\r
365 STATIC\r
366 VOID\r
367 Ip4CancelFrameArp (\r
368   IN IP4_ARP_QUE            *ArpQue,\r
369   IN EFI_STATUS             IoStatus,\r
370   IN IP4_FRAME_TO_CANCEL    FrameToCancel, OPTIONAL\r
371   IN VOID                   *Context\r
372   )\r
373 {\r
374   LIST_ENTRY                *Entry;\r
375   LIST_ENTRY                *Next;\r
376   IP4_LINK_TX_TOKEN         *Token;\r
377 \r
378   NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {\r
379     Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);\r
380 \r
381     if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {\r
382       RemoveEntryList (Entry);\r
383 \r
384       Token->CallBack (Token->IpInstance, Token->Packet, IoStatus, 0, Token->Context);\r
385       Ip4FreeLinkTxToken (Token);\r
386     }\r
387   }\r
388 }\r
389 \r
390 \r
391 /**\r
392   Remove all the frames on the interface that pass the FrameToCancel,\r
393   either queued on ARP queues or that have already been delivered to\r
394   MNP and not yet recycled.\r
395 \r
396   @param  Interface             Interface to remove the frames from\r
397   @param  IoStatus              The transmit status returned to the frames'\r
398                                 callback\r
399   @param  FrameToCancel         Function to select the frame to cancel, NULL to\r
400                                 select all\r
401   @param  Context               Opaque parameters passed to FrameToCancel\r
402 \r
403   @return NONE\r
404 \r
405 **/\r
406 VOID\r
407 Ip4CancelFrames (\r
408   IN IP4_INTERFACE          *Interface,\r
409   IN EFI_STATUS             IoStatus,\r
410   IN IP4_FRAME_TO_CANCEL    FrameToCancel,   OPTIONAL\r
411   IN VOID                   *Context\r
412   )\r
413 {\r
414   LIST_ENTRY                *Entry;\r
415   LIST_ENTRY                *Next;\r
416   IP4_ARP_QUE               *ArpQue;\r
417   IP4_LINK_TX_TOKEN         *Token;\r
418 \r
419   //\r
420   // Cancel all the pending frames on ARP requests\r
421   //\r
422   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->ArpQues) {\r
423     ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);\r
424 \r
425     Ip4CancelFrameArp (ArpQue, IoStatus, FrameToCancel, Context);\r
426 \r
427     if (IsListEmpty (&ArpQue->Frames)) {\r
428       Interface->Arp->Cancel (Interface->Arp, &ArpQue->Ip, ArpQue->OnResolved);\r
429     }\r
430   }\r
431 \r
432   //\r
433   // Cancel all the frames that have been delivered to MNP\r
434   // but not yet recycled.\r
435   //\r
436   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Interface->SentFrames) {\r
437     Token = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);\r
438 \r
439     if ((FrameToCancel == NULL) || FrameToCancel (Token, Context)) {\r
440       Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);\r
441     }\r
442   }\r
443 }\r
444 \r
445 \r
446 /**\r
447   Create an IP4_INTERFACE. Delay the creation of ARP instance until\r
448   the interface is configured.\r
449 \r
450   @param  Mnp                   The shared MNP child of this IP4 service binding\r
451                                 instance\r
452   @param  Controller            The controller this IP4 service binding instance\r
453                                 is installed. Most like the UNDI handle.\r
454   @param  ImageHandle           This driver's image handle\r
455 \r
456   @return Point to the created IP4_INTERFACE, otherwise NULL.\r
457 \r
458 **/\r
459 IP4_INTERFACE *\r
460 Ip4CreateInterface (\r
461   IN  EFI_MANAGED_NETWORK_PROTOCOL  *Mnp,\r
462   IN  EFI_HANDLE                    Controller,\r
463   IN  EFI_HANDLE                    ImageHandle\r
464   )\r
465 {\r
466   IP4_INTERFACE             *Interface;\r
467   EFI_SIMPLE_NETWORK_MODE   SnpMode;\r
468 \r
469   Interface = AllocatePool (sizeof (IP4_INTERFACE));\r
470 \r
471   if ((Interface == NULL) || (Mnp == NULL)) {\r
472     return NULL;\r
473   }\r
474 \r
475   Interface->Signature = IP4_INTERFACE_SIGNATURE;\r
476   InitializeListHead (&Interface->Link);\r
477   Interface->RefCnt     = 1;\r
478 \r
479   Interface->Ip         = IP4_ALLZERO_ADDRESS;\r
480   Interface->SubnetMask = IP4_ALLZERO_ADDRESS;\r
481   Interface->Configured = FALSE;\r
482 \r
483   Interface->Controller = Controller;\r
484   Interface->Image      = ImageHandle;\r
485   Interface->Mnp        = Mnp;\r
486   Interface->Arp        = NULL;\r
487   Interface->ArpHandle  = NULL;\r
488 \r
489   InitializeListHead (&Interface->ArpQues);\r
490   InitializeListHead (&Interface->SentFrames);\r
491 \r
492   Interface->RecvRequest = NULL;\r
493 \r
494   //\r
495   // Get the interface's Mac address and broadcast mac address from SNP\r
496   //\r
497   if (EFI_ERROR (Mnp->GetModeData (Mnp, NULL, &SnpMode))) {\r
498     gBS->FreePool (Interface);\r
499     return NULL;\r
500   }\r
501 \r
502   CopyMem (&Interface->Mac, &SnpMode.CurrentAddress, sizeof (Interface->Mac));\r
503   CopyMem (&Interface->BroadcastMac, &SnpMode.BroadcastAddress, sizeof (Interface->BroadcastMac));\r
504   Interface->HwaddrLen    = SnpMode.HwAddressSize;\r
505 \r
506   InitializeListHead (&Interface->IpInstances);\r
507   Interface->PromiscRecv = FALSE;\r
508 \r
509   return Interface;\r
510 }\r
511 \r
512 \r
513 /**\r
514   Set the interface's address, create and configure\r
515   the ARP child if necessary.\r
516 \r
517   @param  Interface             The interface to set the address\r
518   @param  IpAddr                The interface's IP address\r
519   @param  SubnetMask            The interface's netmask\r
520 \r
521   @retval EFI_SUCCESS           The interface is configured with Ip/netmask pair,\r
522                                 and a ARP is created for it.\r
523   @retval Others                Failed to set the interface's address.\r
524 \r
525 **/\r
526 EFI_STATUS\r
527 Ip4SetAddress (\r
528   IN  IP4_INTERFACE         *Interface,\r
529   IN  IP4_ADDR              IpAddr,\r
530   IN  IP4_ADDR              SubnetMask\r
531   )\r
532 {\r
533   EFI_ARP_CONFIG_DATA       ArpConfig;\r
534   EFI_STATUS                Status;\r
535   INTN                      Type;\r
536   INTN                      Len;\r
537   IP4_ADDR                  Netmask;\r
538 \r
539   NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);\r
540 \r
541   ASSERT (!Interface->Configured);\r
542 \r
543   //\r
544   // Set the ip/netmask, then compute the subnet broadcast\r
545   // and network broadcast for easy access. When computing\r
546   // nework broadcast, the subnet mask is most like longer\r
547   // than the default netmask (not subneted) as defined in\r
548   // RFC793. If that isn't the case, we are aggregating the\r
549   // networks, use the subnet's mask instead.\r
550   //\r
551   Interface->Ip             = IpAddr;\r
552   Interface->SubnetMask     = SubnetMask;\r
553   Interface->SubnetBrdcast  = (IpAddr | ~SubnetMask);\r
554 \r
555   Type                      = NetGetIpClass (IpAddr);\r
556   Len                       = NetGetMaskLength (SubnetMask);\r
557   Netmask                   = gIp4AllMasks[MIN (Len, Type << 3)];\r
558   Interface->NetBrdcast     = (IpAddr | ~Netmask);\r
559 \r
560   //\r
561   // If the address is NOT all zero, create then configure an ARP child.\r
562   // Pay attention: DHCP configures its station address as 0.0.0.0/0\r
563   //\r
564   Interface->Arp            = NULL;\r
565   Interface->ArpHandle      = NULL;\r
566 \r
567   if (IpAddr != IP4_ALLZERO_ADDRESS) {\r
568     Status = NetLibCreateServiceChild (\r
569                Interface->Controller,\r
570                Interface->Image,\r
571                &gEfiArpServiceBindingProtocolGuid,\r
572                &Interface->ArpHandle\r
573                );\r
574 \r
575     if (EFI_ERROR (Status)) {\r
576       return Status;;\r
577     }\r
578 \r
579     Status = gBS->OpenProtocol (\r
580                     Interface->ArpHandle,\r
581                     &gEfiArpProtocolGuid,\r
582                     (VOID **) &Interface->Arp,\r
583                     Interface->Image,\r
584                     Interface->Controller,\r
585                     EFI_OPEN_PROTOCOL_BY_DRIVER\r
586                     );\r
587 \r
588     if (EFI_ERROR (Status)) {\r
589       goto ON_ERROR;\r
590     }\r
591 \r
592     IpAddr                    = HTONL (IpAddr);\r
593     ArpConfig.SwAddressType   = IP4_ETHER_PROTO;\r
594     ArpConfig.SwAddressLength = 4;\r
595     ArpConfig.StationAddress  = &IpAddr;\r
596     ArpConfig.EntryTimeOut    = 0;\r
597     ArpConfig.RetryCount      = 0;\r
598     ArpConfig.RetryTimeOut    = 0;\r
599 \r
600     Status = Interface->Arp->Configure (Interface->Arp, &ArpConfig);\r
601 \r
602     if (EFI_ERROR (Status)) {\r
603       gBS->CloseProtocol (\r
604             Interface->ArpHandle,\r
605             &gEfiArpProtocolGuid,\r
606             Interface->Image,\r
607             Interface->Controller\r
608             );\r
609 \r
610       goto ON_ERROR;\r
611     }\r
612   }\r
613 \r
614   Interface->Configured = TRUE;\r
615   return EFI_SUCCESS;\r
616 \r
617 ON_ERROR:\r
618   NetLibDestroyServiceChild (\r
619     Interface->Controller,\r
620     Interface->Image,\r
621     &gEfiArpServiceBindingProtocolGuid,\r
622     &Interface->ArpHandle\r
623     );\r
624 \r
625   return Status;\r
626 }\r
627 \r
628 \r
629 /**\r
630   Fileter function to cancel all the frame related to an IP instance.\r
631 \r
632   @param  Frame                 The transmit request to test whether to cancel\r
633   @param  Context               The context which is the Ip instance that issued\r
634                                 the transmit.\r
635 \r
636   @retval TRUE                  The frame belongs to this instance and is to be\r
637                                 removed\r
638   @retval FALSE                 The frame doesn't belong to this instance.\r
639 \r
640 **/\r
641 STATIC\r
642 BOOLEAN\r
643 Ip4CancelInstanceFrame (\r
644   IN IP4_LINK_TX_TOKEN *Frame,\r
645   IN VOID              *Context\r
646   )\r
647 {\r
648   if (Frame->IpInstance == (IP4_PROTOCOL *) Context) {\r
649     return TRUE;\r
650   }\r
651 \r
652   return FALSE;\r
653 }\r
654 \r
655 \r
656 \r
657 /**\r
658   If there is a pending receive request, cancel it. Don't call\r
659   the receive request's callback because this function can be only\r
660   called if the instance or driver is tearing itself down. It\r
661   doesn't make sense to call it back. But it is necessary to call\r
662   the transmit token's callback to give it a chance to free the\r
663   packet and update the upper layer's transmit request status, say\r
664   that from the UDP.\r
665 \r
666   @param  Interface             The interface used by the IpInstance\r
667 \r
668   @return None\r
669 \r
670 **/\r
671 VOID\r
672 Ip4CancelReceive (\r
673   IN IP4_INTERFACE          *Interface\r
674   )\r
675 {\r
676   EFI_TPL                   OldTpl;\r
677   IP4_LINK_RX_TOKEN         *Token;\r
678 \r
679   if ((Token = Interface->RecvRequest) != NULL) {\r
680     OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
681 \r
682     Interface->RecvRequest = NULL;\r
683     Interface->Mnp->Cancel (Interface->Mnp, &Token->MnpToken);\r
684 \r
685     gBS->RestoreTPL (OldTpl);\r
686   }\r
687 }\r
688 \r
689 \r
690 /**\r
691   Free the interface used by IpInstance. All the IP instance with\r
692   the same Ip/Netmask pair share the same interface. It is reference\r
693   counted. All the frames haven't been sent will be cancelled.\r
694   Because the IpInstance is optional, the caller must remove\r
695   IpInstance from the interface's instance list itself.\r
696 \r
697   @param  Interface             The interface used by the IpInstance\r
698   @param  IpInstance            The Ip instance that free the interface. NULL if\r
699                                 the  Ip driver is releasing the default interface.\r
700 \r
701   @retval EFI_SUCCESS           The interface use IpInstance is freed.\r
702 \r
703 **/\r
704 EFI_STATUS\r
705 Ip4FreeInterface (\r
706   IN  IP4_INTERFACE         *Interface,\r
707   IN  IP4_PROTOCOL          *IpInstance           OPTIONAL\r
708   )\r
709 {\r
710   NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);\r
711   ASSERT (Interface->RefCnt > 0);\r
712 \r
713   //\r
714   // Remove all the pending transmit token related to this IP instance.\r
715   //\r
716   Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, IpInstance);\r
717 \r
718   if (--Interface->RefCnt > 0) {\r
719     return EFI_SUCCESS;\r
720   }\r
721 \r
722   //\r
723   // Destory the interface if this is the last IP instance that\r
724   // has the address. Remove all the system transmitted packets\r
725   // from this interface, cancel the receive request if there is\r
726   // one, and destory the ARP requests.\r
727   //\r
728   Ip4CancelFrames (Interface, EFI_ABORTED, Ip4CancelInstanceFrame, NULL);\r
729   Ip4CancelReceive (Interface);\r
730 \r
731   ASSERT (IsListEmpty (&Interface->IpInstances));\r
732   ASSERT (IsListEmpty (&Interface->ArpQues));\r
733   ASSERT (IsListEmpty (&Interface->SentFrames));\r
734 \r
735   if (Interface->Arp != NULL) {\r
736     gBS->CloseProtocol (\r
737           Interface->ArpHandle,\r
738           &gEfiArpProtocolGuid,\r
739           Interface->Image,\r
740           Interface->Controller\r
741           );\r
742 \r
743     NetLibDestroyServiceChild (\r
744       Interface->Controller,\r
745       Interface->Image,\r
746       &gEfiArpServiceBindingProtocolGuid,\r
747       Interface->ArpHandle\r
748       );\r
749   }\r
750 \r
751   RemoveEntryList (&Interface->Link);\r
752   gBS->FreePool (Interface);\r
753 \r
754   return EFI_SUCCESS;\r
755 }\r
756 \r
757 \r
758 /**\r
759   Callback function when ARP request are finished. It will cancelled\r
760   all the queued frame if the ARP requests failed. Or transmit them\r
761   if the request succeed.\r
762 \r
763   @param  Context               The context of the callback, a point to the ARP\r
764                                 queue\r
765 \r
766   @return None\r
767 \r
768 **/\r
769 STATIC\r
770 VOID\r
771 EFIAPI\r
772 Ip4OnArpResolvedDpc (\r
773   IN VOID                   *Context\r
774   )\r
775 {\r
776   LIST_ENTRY                *Entry;\r
777   LIST_ENTRY                *Next;\r
778   IP4_ARP_QUE               *ArpQue;\r
779   IP4_INTERFACE             *Interface;\r
780   IP4_LINK_TX_TOKEN         *Token;\r
781   EFI_STATUS                Status;\r
782 \r
783   ArpQue = (IP4_ARP_QUE *) Context;\r
784   NET_CHECK_SIGNATURE (ArpQue, IP4_FRAME_ARP_SIGNATURE);\r
785 \r
786   RemoveEntryList (&ArpQue->Link);\r
787 \r
788   //\r
789   // ARP resolve failed for some reason. Release all the frame\r
790   // and ARP queue itself. Ip4FreeArpQue will call the frame's\r
791   // owner back.\r
792   //\r
793   if (NET_MAC_EQUAL (&ArpQue->Mac, &mZeroMacAddress, ArpQue->Interface->HwaddrLen)) {\r
794     Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);\r
795 \r
796     return ;\r
797   }\r
798 \r
799   //\r
800   // ARP resolve succeeded, Transmit all the frame. Release the ARP\r
801   // queue. It isn't necessary for us to cache the ARP binding because\r
802   // we always check the ARP cache first before transmit.\r
803   //\r
804   Interface = ArpQue->Interface;\r
805 \r
806   NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {\r
807     RemoveEntryList (Entry);\r
808 \r
809     Token         = NET_LIST_USER_STRUCT (Entry, IP4_LINK_TX_TOKEN, Link);\r
810     CopyMem (&Token->DstMac, &ArpQue->Mac, sizeof (Token->DstMac));\r
811 \r
812     //\r
813     // Insert the tx token before transmitting it via MNP as the FrameSentDpc\r
814     // may be called before Mnp->Transmit returns which will remove this tx\r
815     // token from the SentFrames list. Remove it from the list if the returned\r
816     // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the\r
817     // FrameSentDpc won't be queued.\r
818     //\r
819     InsertTailList (&Interface->SentFrames, &Token->Link);\r
820 \r
821     Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);\r
822     if (EFI_ERROR (Status)) {\r
823       RemoveEntryList (Entry);\r
824       Token->CallBack (Token->IpInstance, Token->Packet, Status, 0, Token->Context);\r
825 \r
826       Ip4FreeLinkTxToken (Token);\r
827       continue;\r
828     }\r
829   }\r
830 \r
831   Ip4FreeArpQue (ArpQue, EFI_SUCCESS);\r
832 }\r
833 \r
834 STATIC\r
835 VOID\r
836 EFIAPI\r
837 Ip4OnArpResolved (\r
838   IN EFI_EVENT              Event,\r
839   IN VOID                   *Context\r
840   )\r
841 /*++\r
842 \r
843 Routine Description:\r
844 \r
845   Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK\r
846 \r
847 Arguments:\r
848 \r
849   Event   - The Arp request event\r
850   Context - The context of the callback, a point to the ARP queue\r
851 \r
852 Returns:\r
853 \r
854   None\r
855 \r
856 --*/\r
857 {\r
858   //\r
859   // Request Ip4OnArpResolvedDpc as a DPC at TPL_CALLBACK\r
860   //\r
861   NetLibQueueDpc (TPL_CALLBACK, Ip4OnArpResolvedDpc, Context);\r
862 }\r
863 \r
864 \r
865 \r
866 /**\r
867   Callback funtion when frame transmission is finished. It will\r
868   call the frame owner's callback function to tell it the result.\r
869 \r
870   @param  Context               Context which is point to the token.\r
871 \r
872   @return None.\r
873 \r
874 **/\r
875 STATIC\r
876 VOID\r
877 EFIAPI\r
878 Ip4OnFrameSentDpc (\r
879   IN VOID                    *Context\r
880   )\r
881 {\r
882   IP4_LINK_TX_TOKEN         *Token;\r
883 \r
884   Token = (IP4_LINK_TX_TOKEN *) Context;\r
885   NET_CHECK_SIGNATURE (Token, IP4_FRAME_TX_SIGNATURE);\r
886 \r
887   RemoveEntryList (&Token->Link);\r
888 \r
889   Token->CallBack (\r
890           Token->IpInstance,\r
891           Token->Packet,\r
892           Token->MnpToken.Status,\r
893           0,\r
894           Token->Context\r
895           );\r
896 \r
897   Ip4FreeLinkTxToken (Token);\r
898 }\r
899 \r
900 STATIC\r
901 VOID\r
902 EFIAPI\r
903 Ip4OnFrameSent (\r
904   IN EFI_EVENT               Event,\r
905   IN VOID                    *Context\r
906   )\r
907 /*++\r
908 \r
909 Routine Description:\r
910 \r
911   Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK\r
912 \r
913 Arguments:\r
914 \r
915   Event   - The transmit token's event\r
916   Context - Context which is point to the token.\r
917 \r
918 Returns:\r
919 \r
920   None.\r
921 \r
922 --*/\r
923 {\r
924   //\r
925   // Request Ip4OnFrameSentDpc as a DPC at TPL_CALLBACK\r
926   //\r
927   NetLibQueueDpc (TPL_CALLBACK, Ip4OnFrameSentDpc, Context);\r
928 }\r
929 \r
930 \r
931 \r
932 /**\r
933   Send a frame from the interface. If the next hop is broadcast or\r
934   multicast address, it is transmitted immediately. If the next hop\r
935   is a unicast, it will consult ARP to resolve the NextHop's MAC.\r
936   If some error happened, the CallBack won't be called. So, the caller\r
937   must test the return value, and take action when there is an error.\r
938 \r
939   @param  Interface             The interface to send the frame from\r
940   @param  IpInstance            The IP child that request the transmission.  NULL\r
941                                 if it is the IP4 driver itself.\r
942   @param  Packet                The packet to transmit.\r
943   @param  NextHop               The immediate destination to transmit the packet\r
944                                 to.\r
945   @param  CallBack              Function to call back when transmit finished.\r
946   @param  Context               Opaque parameter to the call back.\r
947 \r
948   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to send the frame\r
949   @retval EFI_NO_MAPPING        Can't resolve the MAC for the nexthop\r
950   @retval EFI_SUCCESS           The packet is successfully transmitted.\r
951 \r
952 **/\r
953 EFI_STATUS\r
954 Ip4SendFrame (\r
955   IN  IP4_INTERFACE         *Interface,\r
956   IN  IP4_PROTOCOL          *IpInstance,      OPTIONAL\r
957   IN  NET_BUF               *Packet,\r
958   IN  IP4_ADDR              NextHop,\r
959   IN  IP4_FRAME_CALLBACK    CallBack,\r
960   IN  VOID                  *Context\r
961   )\r
962 {\r
963   IP4_LINK_TX_TOKEN         *Token;\r
964   LIST_ENTRY                *Entry;\r
965   IP4_ARP_QUE               *ArpQue;\r
966   EFI_ARP_PROTOCOL          *Arp;\r
967   EFI_STATUS                Status;\r
968 \r
969   ASSERT (Interface->Configured);\r
970 \r
971   Token = Ip4WrapLinkTxToken (Interface, IpInstance, Packet, CallBack, Context);\r
972 \r
973   if (Token == NULL) {\r
974     return EFI_OUT_OF_RESOURCES;\r
975   }\r
976 \r
977   //\r
978   // Get the destination MAC address for multicast and broadcasts.\r
979   // Don't depend on ARP to solve the address since there maybe no\r
980   // ARP at all. Ip4Output has set NextHop to 255.255.255.255 for\r
981   // all the broadcasts.\r
982   //\r
983   if (NextHop == IP4_ALLONE_ADDRESS) {\r
984     CopyMem (&Token->DstMac, &Interface->BroadcastMac, sizeof (Token->DstMac));\r
985     goto SEND_NOW;\r
986 \r
987   } else if (IP4_IS_MULTICAST (NextHop)) {\r
988 \r
989     Status = Ip4GetMulticastMac (Interface->Mnp, NextHop, &Token->DstMac);\r
990 \r
991     if (EFI_ERROR (Status)) {\r
992       goto ON_ERROR;\r
993     }\r
994 \r
995     goto SEND_NOW;\r
996   }\r
997 \r
998   //\r
999   // Can only send out multicast/broadcast if the IP address is zero\r
1000   //\r
1001   if ((Arp = Interface->Arp) == NULL) {\r
1002     Status = EFI_NO_MAPPING;\r
1003     goto ON_ERROR;\r
1004   }\r
1005 \r
1006   //\r
1007   // First check whether this binding is in the ARP cache.\r
1008   //\r
1009   NextHop = HTONL (NextHop);\r
1010   Status  = Arp->Request (Arp, &NextHop, NULL, &Token->DstMac);\r
1011 \r
1012   if (Status == EFI_SUCCESS) {\r
1013     goto SEND_NOW;\r
1014 \r
1015   } else if (Status != EFI_NOT_READY) {\r
1016     goto ON_ERROR;\r
1017   }\r
1018 \r
1019   //\r
1020   // Have to do asynchronous ARP resolution. First check\r
1021   // whether there is already a pending request.\r
1022   //\r
1023   ArpQue = NULL;\r
1024 \r
1025   NET_LIST_FOR_EACH (Entry, &Interface->ArpQues) {\r
1026     ArpQue = NET_LIST_USER_STRUCT (Entry, IP4_ARP_QUE, Link);\r
1027 \r
1028     if (ArpQue->Ip == NextHop) {\r
1029       break;\r
1030     }\r
1031   }\r
1032 \r
1033   //\r
1034   // Found a pending ARP request, enqueue the frame then return\r
1035   //\r
1036   if (Entry != &Interface->ArpQues) {\r
1037     InsertTailList (&ArpQue->Frames, &Token->Link);\r
1038     return EFI_SUCCESS;\r
1039   }\r
1040 \r
1041   //\r
1042   // First frame to NextHop, issue an asynchronous ARP requests\r
1043   //\r
1044   ArpQue = Ip4CreateArpQue (Interface, NextHop);\r
1045 \r
1046   if (ArpQue == NULL) {\r
1047     Status = EFI_OUT_OF_RESOURCES;\r
1048     goto ON_ERROR;\r
1049   }\r
1050 \r
1051   Status = Arp->Request (Arp, &ArpQue->Ip, ArpQue->OnResolved, ArpQue->Mac.Addr);\r
1052 \r
1053   if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {\r
1054     Ip4FreeArpQue (ArpQue, EFI_NO_MAPPING);\r
1055     goto ON_ERROR;\r
1056   }\r
1057 \r
1058   InsertHeadList (&ArpQue->Frames, &Token->Link);\r
1059   InsertHeadList (&Interface->ArpQues, &ArpQue->Link);\r
1060   return EFI_SUCCESS;\r
1061 \r
1062 SEND_NOW:\r
1063   //\r
1064   // Insert the tx token into the SentFrames list before calling Mnp->Transmit.\r
1065   // Remove it if the returned status is not EFI_SUCCESS.\r
1066   //\r
1067   InsertTailList (&Interface->SentFrames, &Token->Link);\r
1068   Status = Interface->Mnp->Transmit (Interface->Mnp, &Token->MnpToken);\r
1069   if (EFI_ERROR (Status)) {\r
1070     RemoveEntryList (&Interface->SentFrames);\r
1071     goto ON_ERROR;\r
1072   }\r
1073 \r
1074   return EFI_SUCCESS;\r
1075 \r
1076 ON_ERROR:\r
1077   Ip4FreeLinkTxToken (Token);\r
1078   return Status;\r
1079 }\r
1080 \r
1081 \r
1082 /**\r
1083   Call back function when the received packet is freed.\r
1084   Check Ip4OnFrameReceived for information.\r
1085 \r
1086   @param  Context               Context, which is the IP4_LINK_RX_TOKEN.\r
1087 \r
1088   @return None.\r
1089 \r
1090 **/\r
1091 STATIC\r
1092 VOID\r
1093 Ip4RecycleFrame (\r
1094   IN VOID                   *Context\r
1095   )\r
1096 {\r
1097   IP4_LINK_RX_TOKEN         *Frame;\r
1098 \r
1099   Frame = (IP4_LINK_RX_TOKEN *) Context;\r
1100   NET_CHECK_SIGNATURE (Frame, IP4_FRAME_RX_SIGNATURE);\r
1101 \r
1102   gBS->SignalEvent (Frame->MnpToken.Packet.RxData->RecycleEvent);\r
1103   Ip4FreeFrameRxToken (Frame);\r
1104 }\r
1105 \r
1106 \r
1107 /**\r
1108   Received a frame from MNP, wrap it in net buffer then deliver\r
1109   it to IP's input function. The ownship of the packet also\r
1110   transferred to IP. When Ip is finished with this packet, it\r
1111   will call NetbufFree to release the packet, NetbufFree will\r
1112   again call the Ip4RecycleFrame to signal MNP's event and free\r
1113   the token used.\r
1114 \r
1115   @param  Context               Context for the callback.\r
1116 \r
1117   @return None.\r
1118 \r
1119 **/\r
1120 STATIC\r
1121 VOID\r
1122 EFIAPI\r
1123 Ip4OnFrameReceivedDpc (\r
1124   IN VOID                     *Context\r
1125   )\r
1126 {\r
1127   EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *MnpToken;\r
1128   EFI_MANAGED_NETWORK_RECEIVE_DATA      *MnpRxData;\r
1129   IP4_LINK_RX_TOKEN                     *Token;\r
1130   NET_FRAGMENT                          Netfrag;\r
1131   NET_BUF                               *Packet;\r
1132   UINT32                                Flag;\r
1133 \r
1134   Token = (IP4_LINK_RX_TOKEN *) Context;\r
1135   NET_CHECK_SIGNATURE (Token, IP4_FRAME_RX_SIGNATURE);\r
1136 \r
1137   //\r
1138   // First clear the interface's receive request in case the\r
1139   // caller wants to call Ip4ReceiveFrame in the callback.\r
1140   //\r
1141   Token->Interface->RecvRequest = NULL;\r
1142 \r
1143   MnpToken  = &Token->MnpToken;\r
1144   MnpRxData = MnpToken->Packet.RxData;\r
1145 \r
1146   if (EFI_ERROR (MnpToken->Status) || (MnpRxData == NULL)) {\r
1147     Token->CallBack (Token->IpInstance, NULL, MnpToken->Status, 0, Token->Context);\r
1148     Ip4FreeFrameRxToken (Token);\r
1149 \r
1150     return ;\r
1151   }\r
1152 \r
1153   //\r
1154   // Wrap the frame in a net buffer then deliever it to IP input.\r
1155   // IP will reassemble the packet, and deliver it to upper layer\r
1156   //\r
1157   Netfrag.Len  = MnpRxData->DataLength;\r
1158   Netfrag.Bulk = MnpRxData->PacketData;\r
1159 \r
1160   Packet = NetbufFromExt (&Netfrag, 1, 0, IP4_MAX_HEADLEN, Ip4RecycleFrame, Token);\r
1161 \r
1162   if (Packet == NULL) {\r
1163     gBS->SignalEvent (MnpRxData->RecycleEvent);\r
1164 \r
1165     Token->CallBack (Token->IpInstance, NULL, EFI_OUT_OF_RESOURCES, 0, Token->Context);\r
1166     Ip4FreeFrameRxToken (Token);\r
1167 \r
1168     return ;\r
1169   }\r
1170 \r
1171   Flag  = (MnpRxData->BroadcastFlag ? IP4_LINK_BROADCAST : 0);\r
1172   Flag |= (MnpRxData->MulticastFlag ? IP4_LINK_MULTICAST : 0);\r
1173   Flag |= (MnpRxData->PromiscuousFlag ? IP4_LINK_PROMISC : 0);\r
1174 \r
1175   Token->CallBack (Token->IpInstance, Packet, EFI_SUCCESS, Flag, Token->Context);\r
1176 }\r
1177 \r
1178 STATIC\r
1179 VOID\r
1180 EFIAPI\r
1181 Ip4OnFrameReceived (\r
1182   IN EFI_EVENT                Event,\r
1183   IN VOID                     *Context\r
1184   )\r
1185 /*++\r
1186 \r
1187 Routine Description:\r
1188 \r
1189   Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK\r
1190 \r
1191 Arguments:\r
1192 \r
1193   Event   - The receive event delivered to MNP for receive.\r
1194   Context - Context for the callback.\r
1195 \r
1196 Returns:\r
1197 \r
1198   None.\r
1199 \r
1200 --*/\r
1201 {\r
1202   //\r
1203   // Request Ip4OnFrameReceivedDpc as a DPC at TPL_CALLBACK\r
1204   //\r
1205   NetLibQueueDpc (TPL_CALLBACK, Ip4OnFrameReceivedDpc, Context);\r
1206 }\r
1207 \r
1208 \r
1209 /**\r
1210   Request to receive the packet from the interface.\r
1211 \r
1212   @param  Interface             The interface to receive the frames from\r
1213   @param  IpInstance            The instance that requests the receive. NULL for\r
1214                                 the driver itself.\r
1215   @param  CallBack              Function to call when receive finished.\r
1216   @param  Context               Opaque parameter to the callback\r
1217 \r
1218   @retval EFI_ALREADY_STARTED   There is already a pending receive request.\r
1219   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resource to receive\r
1220   @retval EFI_SUCCESS           The recieve request has been started.\r
1221 \r
1222 **/\r
1223 EFI_STATUS\r
1224 Ip4ReceiveFrame (\r
1225   IN  IP4_INTERFACE         *Interface,\r
1226   IN  IP4_PROTOCOL          *IpInstance,      OPTIONAL\r
1227   IN  IP4_FRAME_CALLBACK    CallBack,\r
1228   IN  VOID                  *Context\r
1229   )\r
1230 {\r
1231   IP4_LINK_RX_TOKEN *Token;\r
1232   EFI_STATUS        Status;\r
1233 \r
1234   NET_CHECK_SIGNATURE (Interface, IP4_INTERFACE_SIGNATURE);\r
1235 \r
1236   if (Interface->RecvRequest != NULL) {\r
1237     return EFI_ALREADY_STARTED;\r
1238   }\r
1239 \r
1240   Token = Ip4CreateLinkRxToken (Interface, IpInstance, CallBack, Context);\r
1241 \r
1242   if (Token == NULL) {\r
1243     return EFI_OUT_OF_RESOURCES;\r
1244   }\r
1245 \r
1246   Interface->RecvRequest = Token;\r
1247   Status = Interface->Mnp->Receive (Interface->Mnp, &Token->MnpToken);\r
1248   if (EFI_ERROR (Status)) {\r
1249     Interface->RecvRequest = NULL;\r
1250     Ip4FreeFrameRxToken (Token);\r
1251     return Status;\r
1252   }\r
1253   return EFI_SUCCESS;\r
1254 }\r