8cc7db8ad27934e92f4646f8a2340d98d2eb7a6b
[mirror_edk2.git] / MdeModulePkg / Universal / Network / MnpDxe / MnpIo.c
1 /** @file\r
2   Implementation of Managed Network Protocol I/O functions.\r
3     \r
4 Copyright (c) 2005 - 2009, Intel Corporation. <BR>\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
13 **/\r
14 \r
15 #include "MnpImpl.h"\r
16 \r
17 /**\r
18   Validates the Mnp transmit token.\r
19 \r
20   @param[in]  Instance            Pointer to the Mnp instance context data.\r
21   @param[in]  Token               Pointer to the transmit token to check.\r
22 \r
23   @return The Token is valid or not.\r
24 \r
25 **/\r
26 BOOLEAN\r
27 MnpIsValidTxToken (\r
28   IN MNP_INSTANCE_DATA                     *Instance,\r
29   IN EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *Token\r
30   )\r
31 {\r
32   MNP_SERVICE_DATA                  *MnpServiceData;\r
33   EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;\r
34   UINT32                            Index;\r
35   UINT32                            TotalLength;\r
36   EFI_MANAGED_NETWORK_FRAGMENT_DATA *FragmentTable;\r
37 \r
38   MnpServiceData = Instance->MnpServiceData;\r
39   NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);\r
40 \r
41   TxData  = Token->Packet.TxData;\r
42 \r
43   if ((Token->Event == NULL) || (TxData == NULL) || (TxData->FragmentCount == 0)) {\r
44     //\r
45     // The token is invalid if the Event is NULL, or the TxData is NULL, or\r
46     // the fragment count is zero.\r
47     //\r
48     DEBUG ((EFI_D_WARN, "MnpIsValidTxToken: Invalid Token.\n"));\r
49     return FALSE;\r
50   }\r
51 \r
52   if ((TxData->DestinationAddress != NULL) && (TxData->HeaderLength != 0)) {\r
53     //\r
54     // The token is invalid if the HeaderLength isn't zero while the DestinationAddress\r
55     // is NULL (The destination address is already put into the packet).\r
56     //\r
57     DEBUG ((EFI_D_WARN, "MnpIsValidTxToken: DestinationAddress isn't NULL, HeaderLength must be 0.\n"));\r
58     return FALSE;\r
59   }\r
60 \r
61   TotalLength   = 0;\r
62   FragmentTable = TxData->FragmentTable;\r
63   for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
64 \r
65     if ((FragmentTable[Index].FragmentLength == 0) || (FragmentTable[Index].FragmentBuffer == NULL)) {\r
66       //\r
67       // The token is invalid if any FragmentLength is zero or any FragmentBuffer is NULL.\r
68       //\r
69       DEBUG ((EFI_D_WARN, "MnpIsValidTxToken: Invalid FragmentLength or FragmentBuffer.\n"));\r
70       return FALSE;\r
71     }\r
72 \r
73     TotalLength += FragmentTable[Index].FragmentLength;\r
74   }\r
75 \r
76   if ((TxData->DestinationAddress == NULL) && (FragmentTable[0].FragmentLength < TxData->HeaderLength)) {\r
77     //\r
78     // Media header is split between fragments.\r
79     //\r
80     return FALSE;\r
81   }\r
82 \r
83   if (TotalLength != (TxData->DataLength + TxData->HeaderLength)) {\r
84     //\r
85     // The length calculated from the fragment information doesn't equal to the\r
86     // sum of the DataLength and the HeaderLength.\r
87     //\r
88     DEBUG ((EFI_D_WARN, "MnpIsValidTxData: Invalid Datalength compared with the sum of fragment length.\n"));\r
89     return FALSE;\r
90   }\r
91 \r
92   if (TxData->DataLength > MnpServiceData->Mtu) {\r
93     //\r
94     // The total length is larger than the MTU.\r
95     //\r
96     DEBUG ((EFI_D_WARN, "MnpIsValidTxData: TxData->DataLength exceeds Mtu.\n"));\r
97     return FALSE;\r
98   }\r
99 \r
100   return TRUE;\r
101 }\r
102 \r
103 \r
104 /**\r
105   Build the packet to transmit from the TxData passed in.\r
106 \r
107   @param[in]   MnpServiceData      Pointer to the mnp service context data.\r
108   @param[in]   TxData              Pointer to the transmit data containing the information \r
109                                    to build the packet.\r
110   @param[out]  PktBuf              Pointer to record the address of the packet.\r
111   @param[out]  PktLen              Pointer to a UINT32 variable used to record the packet's \r
112                                    length.\r
113 \r
114 **/\r
115 VOID\r
116 MnpBuildTxPacket (\r
117   IN  MNP_SERVICE_DATA                   *MnpServiceData,\r
118   IN  EFI_MANAGED_NETWORK_TRANSMIT_DATA  *TxData,\r
119   OUT UINT8                              **PktBuf,\r
120   OUT UINT32                             *PktLen\r
121   )\r
122 {\r
123   EFI_SIMPLE_NETWORK_MODE *SnpMode;\r
124   UINT8                   *DstPos;\r
125   UINT16                  Index;\r
126 \r
127   if ((TxData->DestinationAddress == NULL) && (TxData->FragmentCount == 1)) {\r
128     //\r
129     // Media header is in FragmentTable and there is only one fragment,\r
130     // use fragment buffer directly.\r
131     //\r
132     *PktBuf = TxData->FragmentTable[0].FragmentBuffer;\r
133     *PktLen = TxData->FragmentTable[0].FragmentLength;\r
134   } else {\r
135     //\r
136     // Either media header isn't in FragmentTable or there is more than\r
137     // one fragment, copy the data into the packet buffer. Reserve the\r
138     // media header space if necessary.\r
139     //\r
140     SnpMode = MnpServiceData->Snp->Mode;\r
141     DstPos  = MnpServiceData->TxBuf;\r
142 \r
143     *PktLen = 0;\r
144     if (TxData->DestinationAddress != NULL) {\r
145       //\r
146       // If dest address is not NULL, move DstPos to reserve space for the\r
147       // media header. Add the media header length to buflen.\r
148       //\r
149       DstPos += SnpMode->MediaHeaderSize;\r
150       *PktLen += SnpMode->MediaHeaderSize;\r
151     }\r
152 \r
153     for (Index = 0; Index < TxData->FragmentCount; Index++) {\r
154       //\r
155       // Copy the data.\r
156       //\r
157       CopyMem (\r
158         DstPos,\r
159         TxData->FragmentTable[Index].FragmentBuffer,\r
160         TxData->FragmentTable[Index].FragmentLength\r
161         );\r
162       DstPos += TxData->FragmentTable[Index].FragmentLength;\r
163     }\r
164 \r
165     //\r
166     // Set the buffer pointer and the buffer length.\r
167     //\r
168     *PktBuf = MnpServiceData->TxBuf;\r
169     *PktLen += TxData->DataLength + TxData->HeaderLength;\r
170   }\r
171 }\r
172 \r
173 \r
174 /**\r
175   Synchronously send out the packet.\r
176 \r
177   @param[in]       MnpServiceData      Pointer to the mnp service context data.\r
178   @param[in]       Packet              Pointer to the pakcet buffer.\r
179   @param[in]       Length              The length of the packet.\r
180   @param[in, out]  Token               Pointer to the token the packet generated from.\r
181 \r
182   @retval EFI_SUCCESS                  The packet is sent out.\r
183   @retval EFI_TIMEOUT                  Time out occurs, the packet isn't sent.\r
184   @retval EFI_DEVICE_ERROR             An unexpected network error occurs.\r
185 \r
186 **/\r
187 EFI_STATUS\r
188 MnpSyncSendPacket (\r
189   IN MNP_SERVICE_DATA                          *MnpServiceData,\r
190   IN UINT8                                     *Packet,\r
191   IN UINT32                                    Length,\r
192   IN OUT EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *Token\r
193   )\r
194 {\r
195   EFI_STATUS                        Status;\r
196   EFI_SIMPLE_NETWORK_PROTOCOL       *Snp;\r
197   EFI_MANAGED_NETWORK_TRANSMIT_DATA *TxData;\r
198   UINT32                            HeaderSize;\r
199   UINT8                             *TxBuf;\r
200 \r
201   Snp         = MnpServiceData->Snp;\r
202   TxData      = Token->Packet.TxData;\r
203 \r
204   HeaderSize  = Snp->Mode->MediaHeaderSize - TxData->HeaderLength;\r
205 \r
206   //\r
207   // Start the timeout event.\r
208   //\r
209   Status = gBS->SetTimer (\r
210                   MnpServiceData->TxTimeoutEvent,\r
211                   TimerRelative,\r
212                   MNP_TX_TIMEOUT_TIME\r
213                   );\r
214   if (EFI_ERROR (Status)) {\r
215 \r
216     goto SIGNAL_TOKEN;\r
217   }\r
218 \r
219   for (;;) {\r
220     //\r
221     // Transmit the packet through SNP.\r
222     //\r
223     Status = Snp->Transmit (\r
224                     Snp,\r
225                     HeaderSize,\r
226                     Length,\r
227                     Packet,\r
228                     TxData->SourceAddress,\r
229                     TxData->DestinationAddress,\r
230                     &TxData->ProtocolType\r
231                     );\r
232     if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {\r
233 \r
234       Status = EFI_DEVICE_ERROR;\r
235       break;\r
236     }\r
237 \r
238     //\r
239     // If Status is EFI_SUCCESS, the packet is put in the transmit queue.\r
240     // if Status is EFI_NOT_READY, the transmit engine of the network interface is busy.\r
241     // Both need to sync SNP.\r
242     //\r
243     TxBuf = NULL;\r
244     do {\r
245       //\r
246       // Get the recycled transmit buffer status.\r
247       //\r
248       Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf);\r
249 \r
250       if (!EFI_ERROR (gBS->CheckEvent (MnpServiceData->TxTimeoutEvent))) {\r
251 \r
252         Status = EFI_TIMEOUT;\r
253         break;\r
254       }\r
255     } while (TxBuf == NULL);\r
256 \r
257     if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {\r
258 \r
259       break;\r
260     } else {\r
261       //\r
262       // Status is EFI_NOT_READY. Restart the timer event and call Snp->Transmit again.\r
263       //\r
264       gBS->SetTimer (\r
265             MnpServiceData->TxTimeoutEvent,\r
266             TimerRelative,\r
267             MNP_TX_TIMEOUT_TIME\r
268             );\r
269     }\r
270   }\r
271 \r
272   //\r
273   // Cancel the timer event.\r
274   //\r
275   gBS->SetTimer (MnpServiceData->TxTimeoutEvent, TimerCancel, 0);\r
276 \r
277 SIGNAL_TOKEN:\r
278 \r
279   Token->Status = Status;\r
280   gBS->SignalEvent (Token->Event);\r
281 \r
282   //\r
283   // Dispatch the DPC queued by the NotifyFunction of Token->Event.\r
284   //\r
285   DispatchDpc ();\r
286 \r
287   return EFI_SUCCESS;\r
288 }\r
289 \r
290 \r
291 /**\r
292   Try to deliver the received packet to the instance.\r
293 \r
294   @param[in, out]  Instance     Pointer to the mnp instance context data.\r
295 \r
296   @retval EFI_SUCCESS           The received packet is delivered, or there is no\r
297                                 packet to deliver, or there is no available receive\r
298                                 token.\r
299   @retval EFI_OUT_OF_RESOURCES  The deliver fails due to lack of memory resource.\r
300 \r
301 **/\r
302 EFI_STATUS\r
303 MnpInstanceDeliverPacket (\r
304   IN OUT MNP_INSTANCE_DATA  *Instance\r
305   )\r
306 {\r
307   MNP_SERVICE_DATA                      *MnpServiceData;\r
308   MNP_RXDATA_WRAP                       *RxDataWrap;\r
309   NET_BUF                               *DupNbuf;\r
310   EFI_MANAGED_NETWORK_RECEIVE_DATA      *RxData;\r
311   EFI_SIMPLE_NETWORK_MODE               *SnpMode;\r
312   EFI_MANAGED_NETWORK_COMPLETION_TOKEN  *RxToken;\r
313 \r
314   MnpServiceData = Instance->MnpServiceData;\r
315   NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);\r
316 \r
317   if (NetMapIsEmpty (&Instance->RxTokenMap) || IsListEmpty (&Instance->RcvdPacketQueue)) {\r
318     //\r
319     // No pending received data or no available receive token, return.\r
320     //\r
321     return EFI_SUCCESS;\r
322   }\r
323 \r
324   ASSERT (Instance->RcvdPacketQueueSize != 0);\r
325 \r
326   RxDataWrap = NET_LIST_HEAD (&Instance->RcvdPacketQueue, MNP_RXDATA_WRAP, WrapEntry);\r
327   if (RxDataWrap->Nbuf->RefCnt > 2) {\r
328     //\r
329     // There are other instances share this Nbuf, duplicate to get a\r
330     // copy to allow the instance to do R/W operations.\r
331     //\r
332     DupNbuf = MnpAllocNbuf (MnpServiceData);\r
333     if (DupNbuf == NULL) {\r
334       DEBUG ((EFI_D_WARN, "MnpDeliverPacket: Failed to allocate a free Nbuf.\n"));\r
335 \r
336       return EFI_OUT_OF_RESOURCES;\r
337     }\r
338 \r
339     //\r
340     // Duplicate the net buffer.\r
341     //\r
342     NetbufDuplicate (RxDataWrap->Nbuf, DupNbuf, 0);\r
343     MnpFreeNbuf (MnpServiceData, RxDataWrap->Nbuf);\r
344     RxDataWrap->Nbuf = DupNbuf;\r
345   }\r
346 \r
347   //\r
348   // All resources are OK, remove the packet from the queue.\r
349   //\r
350   NetListRemoveHead (&Instance->RcvdPacketQueue);\r
351   Instance->RcvdPacketQueueSize--;\r
352 \r
353   RxData  = &RxDataWrap->RxData;\r
354   SnpMode = MnpServiceData->Snp->Mode;\r
355 \r
356   //\r
357   // Set all the buffer pointers.\r
358   //\r
359   RxData->MediaHeader         = NetbufGetByte (RxDataWrap->Nbuf, 0, NULL);\r
360   RxData->DestinationAddress  = RxData->MediaHeader;\r
361   RxData->SourceAddress       = (UINT8 *) RxData->MediaHeader + SnpMode->HwAddressSize;\r
362   RxData->PacketData          = (UINT8 *) RxData->MediaHeader + SnpMode->MediaHeaderSize;\r
363 \r
364   //\r
365   // Insert this RxDataWrap into the delivered queue.\r
366   //\r
367   InsertTailList (&Instance->RxDeliveredPacketQueue, &RxDataWrap->WrapEntry);\r
368 \r
369   //\r
370   // Get the receive token from the RxTokenMap.\r
371   //\r
372   RxToken = NetMapRemoveHead (&Instance->RxTokenMap, NULL);\r
373 \r
374   //\r
375   // Signal this token's event.\r
376   //\r
377   RxToken->Packet.RxData  = &RxDataWrap->RxData;\r
378   RxToken->Status         = EFI_SUCCESS;\r
379   gBS->SignalEvent (RxToken->Event);\r
380 \r
381   return EFI_SUCCESS;\r
382 }\r
383 \r
384 \r
385 /**\r
386   Deliver the received packet for the instances belonging to the MnpServiceData.\r
387 \r
388   @param[in]  MnpServiceData        Pointer to the mnp service context data.\r
389 \r
390 **/\r
391 VOID\r
392 MnpDeliverPacket (\r
393   IN MNP_SERVICE_DATA  *MnpServiceData\r
394   )\r
395 {\r
396   LIST_ENTRY        *Entry;\r
397   MNP_INSTANCE_DATA *Instance;\r
398 \r
399   NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);\r
400 \r
401   NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {\r
402     Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);\r
403     NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);\r
404 \r
405     //\r
406     // Try to deliver packet for this instance.\r
407     //\r
408     MnpInstanceDeliverPacket (Instance);\r
409   }\r
410 }\r
411 \r
412 \r
413 /**\r
414   Recycle the RxData and other resources used to hold and deliver the received\r
415   packet.\r
416 \r
417   @param[in]  Event               The event this notify function registered to.\r
418   @param[in]  Context             Pointer to the context data registerd to the Event.\r
419 \r
420 **/\r
421 VOID\r
422 EFIAPI\r
423 MnpRecycleRxData (\r
424   IN EFI_EVENT  Event,\r
425   IN VOID       *Context\r
426   )\r
427 {\r
428   MNP_RXDATA_WRAP   *RxDataWrap;\r
429   MNP_SERVICE_DATA  *MnpServiceData;\r
430 \r
431   ASSERT (Context != NULL);\r
432 \r
433   RxDataWrap = (MNP_RXDATA_WRAP *) Context;\r
434   NET_CHECK_SIGNATURE (RxDataWrap->Instance, MNP_INSTANCE_DATA_SIGNATURE);\r
435 \r
436   ASSERT (RxDataWrap->Nbuf != NULL);\r
437 \r
438   MnpServiceData = RxDataWrap->Instance->MnpServiceData;\r
439   NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);\r
440 \r
441   //\r
442   // Free this Nbuf.\r
443   //\r
444   MnpFreeNbuf (MnpServiceData, RxDataWrap->Nbuf);\r
445   RxDataWrap->Nbuf = NULL;\r
446 \r
447   //\r
448   // Close the recycle event.\r
449   //\r
450   gBS->CloseEvent (RxDataWrap->RxData.RecycleEvent);\r
451 \r
452   //\r
453   // Remove this Wrap entry from the list.\r
454   //\r
455   RemoveEntryList (&RxDataWrap->WrapEntry);\r
456 \r
457   FreePool (RxDataWrap);\r
458 }\r
459 \r
460 \r
461 /**\r
462   Queue the received packet into instance's receive queue.\r
463 \r
464   @param[in, out]  Instance        Pointer to the mnp instance context data.\r
465   @param[in, out]  RxDataWrap      Pointer to the Wrap structure containing the\r
466                                    received data and other information.\r
467 **/\r
468 VOID\r
469 MnpQueueRcvdPacket (\r
470   IN OUT MNP_INSTANCE_DATA  *Instance,\r
471   IN OUT MNP_RXDATA_WRAP    *RxDataWrap\r
472   )\r
473 {\r
474   MNP_RXDATA_WRAP *OldRxDataWrap;\r
475 \r
476   NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);\r
477 \r
478   //\r
479   // Check the queue size. If it exceeds the limit, drop one packet\r
480   // from the head.\r
481   //\r
482   if (Instance->RcvdPacketQueueSize == MNP_MAX_RCVD_PACKET_QUE_SIZE) {\r
483 \r
484     DEBUG ((EFI_D_WARN, "MnpQueueRcvdPacket: Drop one packet bcz queue size limit reached.\n"));\r
485 \r
486     //\r
487     // Get the oldest packet.\r
488     //\r
489     OldRxDataWrap = NET_LIST_HEAD (\r
490                       &Instance->RcvdPacketQueue,\r
491                       MNP_RXDATA_WRAP,\r
492                       WrapEntry\r
493                       );\r
494 \r
495     //\r
496     // Recycle this OldRxDataWrap, this entry will be removed by the callee.\r
497     //\r
498     MnpRecycleRxData (NULL, (VOID *) OldRxDataWrap);\r
499     Instance->RcvdPacketQueueSize--;\r
500   }\r
501 \r
502   //\r
503   // Update the timeout tick using the configured parameter.\r
504   //\r
505   RxDataWrap->TimeoutTick = Instance->ConfigData.ReceivedQueueTimeoutValue;\r
506 \r
507   //\r
508   // Insert this Wrap into the instance queue.\r
509   //\r
510   InsertTailList (&Instance->RcvdPacketQueue, &RxDataWrap->WrapEntry);\r
511   Instance->RcvdPacketQueueSize++;\r
512 }\r
513 \r
514 \r
515 /**\r
516   Match the received packet with the instance receive filters.\r
517 \r
518   @param[in]  Instance          Pointer to the mnp instance context data.\r
519   @param[in]  RxData            Pointer to the EFI_MANAGED_NETWORK_RECEIVE_DATA.\r
520   @param[in]  GroupAddress      Pointer to the GroupAddress, the GroupAddress is\r
521                                 non-NULL and it contains the destination multicast\r
522                                 mac address of the received packet if the packet\r
523                                 destinated to a multicast mac address.\r
524   @param[in]  PktAttr           The received packets attribute.\r
525 \r
526   @return The received packet matches the instance's receive filters or not.\r
527 \r
528 **/\r
529 BOOLEAN\r
530 MnpMatchPacket (\r
531   IN MNP_INSTANCE_DATA                 *Instance,\r
532   IN EFI_MANAGED_NETWORK_RECEIVE_DATA  *RxData,\r
533   IN MNP_GROUP_ADDRESS                 *GroupAddress OPTIONAL,\r
534   IN UINT8                             PktAttr\r
535   )\r
536 {\r
537   EFI_MANAGED_NETWORK_CONFIG_DATA *ConfigData;\r
538   LIST_ENTRY                      *Entry;\r
539   MNP_GROUP_CONTROL_BLOCK         *GroupCtrlBlk;\r
540 \r
541   NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);\r
542 \r
543   ConfigData = &Instance->ConfigData;\r
544 \r
545   //\r
546   // Check the protocol type.\r
547   //\r
548   if ((ConfigData->ProtocolTypeFilter != 0) && (ConfigData->ProtocolTypeFilter != RxData->ProtocolType)) {\r
549     return FALSE;\r
550   }\r
551 \r
552   if (ConfigData->EnablePromiscuousReceive) {\r
553     //\r
554     // Always match if this instance is configured to be promiscuous.\r
555     //\r
556     return TRUE;\r
557   }\r
558 \r
559   //\r
560   // The protocol type is matched, check receive filter, include unicast and broadcast.\r
561   //\r
562   if ((Instance->ReceiveFilter & PktAttr) != 0) {\r
563     return TRUE;\r
564   }\r
565 \r
566   //\r
567   // Check multicast addresses.\r
568   //\r
569   if (ConfigData->EnableMulticastReceive && RxData->MulticastFlag) {\r
570 \r
571     ASSERT (GroupAddress != NULL);\r
572 \r
573     NET_LIST_FOR_EACH (Entry, &Instance->GroupCtrlBlkList) {\r
574 \r
575       GroupCtrlBlk = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_CONTROL_BLOCK, CtrlBlkEntry);\r
576       if (GroupCtrlBlk->GroupAddress == GroupAddress) {\r
577         //\r
578         // The instance is configured to receiveing packets destinated to this\r
579         // multicast address.\r
580         //\r
581         return TRUE;\r
582       }\r
583     }\r
584   }\r
585 \r
586   //\r
587   // No match.\r
588   //\r
589   return FALSE;\r
590 }\r
591 \r
592 \r
593 /**\r
594   Analyse the received packets.\r
595 \r
596   @param[in]       MnpServiceData    Pointer to the mnp service context data.\r
597   @param[in]       Nbuf              Pointer to the net buffer holding the received\r
598                                      packet.\r
599   @param[in, out]  RxData            Pointer to the buffer used to save the analysed\r
600                                      result in EFI_MANAGED_NETWORK_RECEIVE_DATA.\r
601   @param[out]      GroupAddress      Pointer to pointer to a MNP_GROUP_ADDRESS used to\r
602                                      pass out the address of the multicast address the\r
603                                      received packet destinated to.\r
604   @param[out]      PktAttr           Pointer to the buffer used to save the analysed\r
605                                      packet attribute.\r
606 \r
607 **/\r
608 VOID\r
609 MnpAnalysePacket (\r
610   IN  MNP_SERVICE_DATA                      *MnpServiceData,\r
611   IN  NET_BUF                               *Nbuf,\r
612   IN  OUT EFI_MANAGED_NETWORK_RECEIVE_DATA  *RxData,\r
613   OUT MNP_GROUP_ADDRESS                     **GroupAddress,\r
614   OUT UINT8                                 *PktAttr\r
615   )\r
616 {\r
617   EFI_SIMPLE_NETWORK_MODE *SnpMode;\r
618   UINT8                   *BufPtr;\r
619   LIST_ENTRY              *Entry;\r
620 \r
621   SnpMode = MnpServiceData->Snp->Mode;\r
622 \r
623   //\r
624   // Get the packet buffer.\r
625   //\r
626   BufPtr = NetbufGetByte (Nbuf, 0, NULL);\r
627   ASSERT (BufPtr != NULL);\r
628 \r
629   //\r
630   // Set the initial values.\r
631   //\r
632   RxData->BroadcastFlag   = FALSE;\r
633   RxData->MulticastFlag   = FALSE;\r
634   RxData->PromiscuousFlag = FALSE;\r
635   *PktAttr                = UNICAST_PACKET;\r
636 \r
637   if (!NET_MAC_EQUAL (&SnpMode->CurrentAddress, BufPtr, SnpMode->HwAddressSize)) {\r
638     //\r
639     // This packet isn't destinated to our current mac address, it't not unicast.\r
640     //\r
641     *PktAttr = 0;\r
642 \r
643     if (NET_MAC_EQUAL (&SnpMode->BroadcastAddress, BufPtr, SnpMode->HwAddressSize)) {\r
644       //\r
645       // It's broadcast.\r
646       //\r
647       RxData->BroadcastFlag = TRUE;\r
648       *PktAttr              = BROADCAST_PACKET;\r
649     } else if ((*BufPtr & 0x01) == 0x1) {\r
650       //\r
651       // It's multicast, try to match the multicast filters.\r
652       //\r
653       NET_LIST_FOR_EACH (Entry, &MnpServiceData->GroupAddressList) {\r
654 \r
655         *GroupAddress = NET_LIST_USER_STRUCT (Entry, MNP_GROUP_ADDRESS, AddrEntry);\r
656         if (NET_MAC_EQUAL (BufPtr, &((*GroupAddress)->Address), SnpMode->HwAddressSize)) {\r
657           RxData->MulticastFlag = TRUE;\r
658           break;\r
659         }\r
660       }\r
661 \r
662       if (!RxData->MulticastFlag) {\r
663         //\r
664         // No match, set GroupAddress to NULL. This multicast packet must\r
665         // be the result of PROMISUCOUS or PROMISUCOUS_MULTICAST flag is on.\r
666         //\r
667         *GroupAddress           = NULL;\r
668         RxData->PromiscuousFlag = TRUE;\r
669 \r
670         if (MnpServiceData->PromiscuousCount == 0) {\r
671           //\r
672           // Skip the below code, there is no receiver of this packet.\r
673           //\r
674           return ;\r
675         }\r
676       }\r
677     } else {\r
678       RxData->PromiscuousFlag = TRUE;\r
679     }\r
680   }\r
681 \r
682   ZeroMem (&RxData->Timestamp, sizeof (EFI_TIME));\r
683 \r
684   //\r
685   // Fill the common parts of RxData.\r
686   //\r
687   RxData->PacketLength  = Nbuf->TotalSize;\r
688   RxData->HeaderLength  = SnpMode->MediaHeaderSize;\r
689   RxData->AddressLength = SnpMode->HwAddressSize;\r
690   RxData->DataLength    = RxData->PacketLength - RxData->HeaderLength;\r
691   RxData->ProtocolType  = NTOHS (*(UINT16 *) (BufPtr + 2 * SnpMode->HwAddressSize));\r
692 }\r
693 \r
694 \r
695 /**\r
696   Wrap the RxData.\r
697 \r
698   @param[in]  Instance           Pointer to the mnp instance context data.\r
699   @param[in]  RxData             Pointer to the receive data to wrap.\r
700 \r
701   @return Pointer to a MNP_RXDATA_WRAP which wraps the RxData.\r
702 \r
703 **/\r
704 MNP_RXDATA_WRAP *\r
705 MnpWrapRxData (\r
706   IN MNP_INSTANCE_DATA                 *Instance,\r
707   IN EFI_MANAGED_NETWORK_RECEIVE_DATA  *RxData\r
708   )\r
709 {\r
710   EFI_STATUS      Status;\r
711   MNP_RXDATA_WRAP *RxDataWrap;\r
712 \r
713   //\r
714   // Allocate memory.\r
715   //\r
716   RxDataWrap = AllocatePool (sizeof (MNP_RXDATA_WRAP));\r
717   if (RxDataWrap == NULL) {\r
718     DEBUG ((EFI_D_ERROR, "MnpDispatchPacket: Failed to allocate a MNP_RXDATA_WRAP.\n"));\r
719     return NULL;\r
720   }\r
721 \r
722   RxDataWrap->Instance = Instance;\r
723 \r
724   //\r
725   // Fill the RxData in RxDataWrap,\r
726   //\r
727   CopyMem (&RxDataWrap->RxData, RxData, sizeof (RxDataWrap->RxData));\r
728 \r
729   //\r
730   // Create the recycle event.\r
731   //\r
732   Status = gBS->CreateEvent (\r
733                   EVT_NOTIFY_SIGNAL,\r
734                   TPL_NOTIFY,\r
735                   MnpRecycleRxData,\r
736                   RxDataWrap,\r
737                   &RxDataWrap->RxData.RecycleEvent\r
738                   );\r
739   if (EFI_ERROR (Status)) {\r
740 \r
741     DEBUG ((EFI_D_ERROR, "MnpDispatchPacket: gBS->CreateEvent failed, %r.\n", Status));\r
742     FreePool (RxDataWrap);\r
743     return NULL;\r
744   }\r
745 \r
746   return RxDataWrap;\r
747 }\r
748 \r
749 \r
750 /**\r
751   Enqueue the received the packets to the instances belonging to the\r
752   MnpServiceData.\r
753 \r
754   @param[in]  MnpServiceData    Pointer to the mnp service context data.\r
755   @param[in]  Nbuf              Pointer to the net buffer representing the received\r
756                                 packet.\r
757 \r
758 **/\r
759 VOID\r
760 MnpEnqueuePacket (\r
761   IN MNP_SERVICE_DATA   *MnpServiceData,\r
762   IN NET_BUF            *Nbuf\r
763   )\r
764 {\r
765   LIST_ENTRY                        *Entry;\r
766   MNP_INSTANCE_DATA                 *Instance;\r
767   EFI_MANAGED_NETWORK_RECEIVE_DATA  RxData;\r
768   UINT8                             PktAttr;\r
769   MNP_GROUP_ADDRESS                 *GroupAddress;\r
770   MNP_RXDATA_WRAP                   *RxDataWrap;\r
771 \r
772 \r
773   GroupAddress = NULL;\r
774   //\r
775   // First, analyse the packet header.\r
776   //\r
777   MnpAnalysePacket (MnpServiceData, Nbuf, &RxData, &GroupAddress, &PktAttr);\r
778 \r
779   if (RxData.PromiscuousFlag && (MnpServiceData->PromiscuousCount == 0)) {\r
780     //\r
781     // No receivers, no more action need.\r
782     //\r
783     return ;\r
784   }\r
785 \r
786   //\r
787   // Iterate the children to find match.\r
788   //\r
789   NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {\r
790 \r
791     Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);\r
792     NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);\r
793 \r
794     if (!Instance->Configured) {\r
795       continue;\r
796     }\r
797 \r
798     //\r
799     // Check the packet against the instance receive filters.\r
800     //\r
801     if (MnpMatchPacket (Instance, &RxData, GroupAddress, PktAttr)) {\r
802 \r
803       //\r
804       // Wrap the RxData.\r
805       //\r
806       RxDataWrap = MnpWrapRxData (Instance, &RxData);\r
807       if (RxDataWrap == NULL) {\r
808         continue;\r
809       }\r
810 \r
811       //\r
812       // Associate RxDataWrap with Nbuf and increase the RefCnt.\r
813       //\r
814       RxDataWrap->Nbuf = Nbuf;\r
815       NET_GET_REF (RxDataWrap->Nbuf);\r
816 \r
817       //\r
818       // Queue the packet into the instance queue.\r
819       //\r
820       MnpQueueRcvdPacket (Instance, RxDataWrap);\r
821     }\r
822   }\r
823 }\r
824 \r
825 \r
826 /**\r
827   Try to receive a packet and deliver it.\r
828 \r
829   @param[in, out]  MnpServiceData        Pointer to the mnp service context data.\r
830 \r
831   @retval EFI_SUCCESS           add return value to function comment\r
832   @retval EFI_NOT_STARTED       The simple network protocol is not started.\r
833   @retval EFI_NOT_READY         No packet received.\r
834   @retval EFI_DEVICE_ERROR      An unexpected error occurs.\r
835 \r
836 **/\r
837 EFI_STATUS\r
838 MnpReceivePacket (\r
839   IN OUT MNP_SERVICE_DATA  *MnpServiceData\r
840   )\r
841 {\r
842   EFI_STATUS                  Status;\r
843   EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
844   NET_BUF                     *Nbuf;\r
845   UINT8                       *BufPtr;\r
846   UINTN                       BufLen;\r
847   UINTN                       HeaderSize;\r
848   UINT32                      Trimmed;\r
849 \r
850   NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);\r
851 \r
852   Snp = MnpServiceData->Snp;\r
853   if (Snp->Mode->State != EfiSimpleNetworkInitialized) {\r
854     //\r
855     // The simple network protocol is not started.\r
856     //\r
857     return EFI_NOT_STARTED;\r
858   }\r
859 \r
860   if (IsListEmpty (&MnpServiceData->ChildrenList)) {\r
861     //\r
862     // There is no child, no need to receive packets.\r
863     //\r
864     return EFI_SUCCESS;\r
865   }\r
866 \r
867   if (MnpServiceData->RxNbufCache == NULL) {\r
868     //\r
869     // Try to get a new buffer as there may be buffers recycled.\r
870     //\r
871     MnpServiceData->RxNbufCache = MnpAllocNbuf (MnpServiceData);\r
872 \r
873     if (MnpServiceData->RxNbufCache == NULL) {\r
874       //\r
875       // No availabe buffer in the buffer pool.\r
876       //\r
877       return EFI_DEVICE_ERROR;\r
878     }\r
879 \r
880     NetbufAllocSpace (\r
881       MnpServiceData->RxNbufCache,\r
882       MnpServiceData->BufferLength,\r
883       NET_BUF_TAIL\r
884       );\r
885   }\r
886 \r
887   Nbuf    = MnpServiceData->RxNbufCache;\r
888   BufLen  = Nbuf->TotalSize;\r
889   BufPtr  = NetbufGetByte (Nbuf, 0, NULL);\r
890   ASSERT (BufPtr != NULL);\r
891 \r
892   //\r
893   // Receive packet through Snp.\r
894   //\r
895   Status = Snp->Receive (Snp, &HeaderSize, &BufLen, BufPtr, NULL, NULL, NULL);\r
896   if (EFI_ERROR (Status)) {\r
897 \r
898     DEBUG_CODE (\r
899       if (Status != EFI_NOT_READY) {\r
900         DEBUG ((EFI_D_WARN, "MnpReceivePacket: Snp->Receive() = %r.\n", Status));\r
901       }\r
902     );\r
903 \r
904     return Status;\r
905   }\r
906 \r
907   //\r
908   // Sanity check.\r
909   //\r
910   if ((HeaderSize != Snp->Mode->MediaHeaderSize) || (BufLen < HeaderSize)) {\r
911 \r
912     DEBUG (\r
913       (EFI_D_WARN,\r
914       "MnpReceivePacket: Size error, HL:TL = %d:%d.\n",\r
915       HeaderSize,\r
916       BufLen)\r
917       );\r
918     return EFI_DEVICE_ERROR;\r
919   }\r
920 \r
921   Trimmed = 0;\r
922   if (Nbuf->TotalSize != BufLen) {\r
923     //\r
924     // Trim the packet from tail.\r
925     //\r
926     Trimmed = NetbufTrim (Nbuf, Nbuf->TotalSize - (UINT32) BufLen, NET_BUF_TAIL);\r
927     ASSERT (Nbuf->TotalSize == BufLen);\r
928   }\r
929 \r
930   //\r
931   // Enqueue the packet to the matched instances.\r
932   //\r
933   MnpEnqueuePacket (MnpServiceData, Nbuf);\r
934 \r
935   if (Nbuf->RefCnt > 2) {\r
936     //\r
937     // RefCnt > 2 indicates there is at least one receiver of this packet.\r
938     // Free the current RxNbufCache and allocate a new one.\r
939     //\r
940     MnpFreeNbuf (MnpServiceData, Nbuf);\r
941 \r
942     Nbuf                        = MnpAllocNbuf (MnpServiceData);\r
943     MnpServiceData->RxNbufCache = Nbuf;\r
944     if (Nbuf == NULL) {\r
945       DEBUG ((EFI_D_ERROR, "MnpReceivePacket: Alloc packet for receiving cache failed.\n"));\r
946       return EFI_DEVICE_ERROR;\r
947     }\r
948 \r
949     NetbufAllocSpace (Nbuf, MnpServiceData->BufferLength, NET_BUF_TAIL);\r
950   } else {\r
951     //\r
952     // No receiver for this packet.\r
953     //\r
954     if (Trimmed > 0) {\r
955       NetbufAllocSpace (Nbuf, Trimmed, NET_BUF_TAIL);\r
956     }\r
957 \r
958     goto EXIT;\r
959   }\r
960   //\r
961   // Deliver the queued packets.\r
962   //\r
963   MnpDeliverPacket (MnpServiceData);\r
964 \r
965 EXIT:\r
966 \r
967   ASSERT (Nbuf->TotalSize == MnpServiceData->BufferLength);\r
968 \r
969   return Status;\r
970 }\r
971 \r
972 \r
973 /**\r
974   Remove the received packets if timeout occurs.\r
975 \r
976   @param[in]  Event             The event this notify function registered to.\r
977   @param[in]  Context           Pointer to the context data registered to the\r
978                                 event.\r
979    \r
980 **/\r
981 VOID\r
982 EFIAPI\r
983 MnpCheckPacketTimeout (\r
984   IN EFI_EVENT  Event,\r
985   IN VOID       *Context\r
986   )\r
987 {\r
988   MNP_SERVICE_DATA  *MnpServiceData;\r
989   LIST_ENTRY        *Entry;\r
990   LIST_ENTRY        *RxEntry;\r
991   LIST_ENTRY        *NextEntry;\r
992   MNP_INSTANCE_DATA *Instance;\r
993   MNP_RXDATA_WRAP   *RxDataWrap;\r
994   EFI_TPL           OldTpl;\r
995 \r
996   MnpServiceData = (MNP_SERVICE_DATA *) Context;\r
997   NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);\r
998 \r
999   NET_LIST_FOR_EACH (Entry, &MnpServiceData->ChildrenList) {\r
1000 \r
1001     Instance = NET_LIST_USER_STRUCT (Entry, MNP_INSTANCE_DATA, InstEntry);\r
1002     NET_CHECK_SIGNATURE (Instance, MNP_INSTANCE_DATA_SIGNATURE);\r
1003 \r
1004     if (!Instance->Configured || (Instance->ConfigData.ReceivedQueueTimeoutValue == 0)) {\r
1005       //\r
1006       // This instance is not configured or there is no receive time out,\r
1007       // just skip to the next instance.\r
1008       //\r
1009       continue;\r
1010     }\r
1011 \r
1012     OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
1013 \r
1014     NET_LIST_FOR_EACH_SAFE (RxEntry, NextEntry, &Instance->RcvdPacketQueue) {\r
1015 \r
1016       RxDataWrap = NET_LIST_USER_STRUCT (RxEntry, MNP_RXDATA_WRAP, WrapEntry);\r
1017 \r
1018       //\r
1019       // TimeoutTick unit is microsecond, MNP_TIMEOUT_CHECK_INTERVAL unit is 100ns.\r
1020       //\r
1021       if (RxDataWrap->TimeoutTick >= (MNP_TIMEOUT_CHECK_INTERVAL / 10)) {\r
1022 \r
1023         RxDataWrap->TimeoutTick -= (MNP_TIMEOUT_CHECK_INTERVAL / 10);\r
1024       } else {\r
1025         //\r
1026         // Drop the timeout packet.\r
1027         //\r
1028         DEBUG ((EFI_D_WARN, "MnpCheckPacketTimeout: Received packet timeout.\n"));\r
1029         MnpRecycleRxData (NULL, RxDataWrap);\r
1030         Instance->RcvdPacketQueueSize--;\r
1031       }\r
1032     }\r
1033 \r
1034     gBS->RestoreTPL (OldTpl);\r
1035   }\r
1036 }\r
1037 \r
1038 \r
1039 /**\r
1040   Poll to receive the packets from Snp. This function is either called by upperlayer\r
1041   protocols/applications or the system poll timer notify mechanism.\r
1042 \r
1043   @param[in]       Event        The event this notify function registered to.\r
1044   @param[in, out]  Context      Pointer to the context data registered to the event.\r
1045 \r
1046 **/\r
1047 VOID\r
1048 EFIAPI\r
1049 MnpSystemPoll (\r
1050   IN EFI_EVENT  Event,\r
1051   IN OUT VOID   *Context\r
1052   )\r
1053 {\r
1054   MNP_SERVICE_DATA  *MnpServiceData;\r
1055 \r
1056   MnpServiceData = (MNP_SERVICE_DATA *) Context;\r
1057   NET_CHECK_SIGNATURE (MnpServiceData, MNP_SERVICE_DATA_SIGNATURE);\r
1058 \r
1059   //\r
1060   // Try to receive packets from Snp.\r
1061   //\r
1062   MnpReceivePacket (MnpServiceData);\r
1063 \r
1064   //\r
1065   // Dispatch the DPC queued by the NotifyFunction of rx token's events.\r
1066   //\r
1067   DispatchDpc ();\r
1068 }\r