Fix a bug for vlan ping failure.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / MnpDxe / MnpVlan.c
1 /** @file\r
2   VLAN Config Protocol implementation and VLAN packet process routine.\r
3 \r
4 Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions\r
7 of the BSD License which accompanies this distribution.  The full\r
8 text of the license may be found at<BR>\r
9 http://opensource.org/licenses/bsd-license.php\r
10 \r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 \r
14 **/\r
15 \r
16 #include "MnpImpl.h"\r
17 #include "MnpVlan.h"\r
18 \r
19 VLAN_DEVICE_PATH          mVlanDevicePathTemplate = {\r
20   {\r
21     MESSAGING_DEVICE_PATH,\r
22     MSG_VLAN_DP,\r
23     {\r
24       (UINT8) (sizeof (VLAN_DEVICE_PATH)),\r
25       (UINT8) ((sizeof (VLAN_DEVICE_PATH)) >> 8)\r
26     }\r
27   },\r
28   0\r
29 };\r
30 \r
31 EFI_VLAN_CONFIG_PROTOCOL  mVlanConfigProtocolTemplate = {\r
32   VlanConfigSet,\r
33   VlanConfigFind,\r
34   VlanConfigRemove\r
35 };\r
36 \r
37 \r
38 /**\r
39   Create a child handle for the VLAN ID.\r
40 \r
41   @param[in]       ImageHandle        The driver image handle.\r
42   @param[in]       ControllerHandle   Handle of device to bind driver to.\r
43   @param[in]       VlanId             The VLAN ID.\r
44   @param[out]      Devicepath         Pointer to returned device path for child handle.\r
45 \r
46   @return The handle of VLAN child or NULL if failed to create VLAN child.\r
47 \r
48 **/\r
49 EFI_HANDLE\r
50 MnpCreateVlanChild (\r
51   IN     EFI_HANDLE                  ImageHandle,\r
52   IN     EFI_HANDLE                  ControllerHandle,\r
53   IN     UINT16                      VlanId,\r
54      OUT EFI_DEVICE_PATH_PROTOCOL    **Devicepath OPTIONAL\r
55   )\r
56 {\r
57   EFI_HANDLE                ChildHandle;\r
58   VLAN_DEVICE_PATH          VlanNode;\r
59   EFI_DEVICE_PATH_PROTOCOL  *ParentDevicePath;\r
60   EFI_DEVICE_PATH_PROTOCOL  *VlanDevicePath;\r
61   EFI_STATUS                Status;\r
62 \r
63   //\r
64   // Try to get parent device path\r
65   //\r
66   Status = gBS->OpenProtocol (\r
67                   ControllerHandle,\r
68                   &gEfiDevicePathProtocolGuid,\r
69                   (VOID **) &ParentDevicePath,\r
70                   ImageHandle,\r
71                   ControllerHandle,\r
72                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
73                   );\r
74   if (EFI_ERROR (Status)) {\r
75     return NULL;\r
76   }\r
77 \r
78   //\r
79   // Construct device path for child handle: MAC + VLAN\r
80   //\r
81   CopyMem (&VlanNode, &mVlanDevicePathTemplate, sizeof (VLAN_DEVICE_PATH));\r
82   VlanNode.VlanId = VlanId;\r
83   VlanDevicePath = AppendDevicePathNode (\r
84                      ParentDevicePath,\r
85                      (EFI_DEVICE_PATH_PROTOCOL *) &VlanNode\r
86                      );\r
87   if (VlanDevicePath == NULL) {\r
88     return NULL;\r
89   }\r
90 \r
91   //\r
92   // Create child VLAN handle by installing DevicePath protocol\r
93   //\r
94   ChildHandle = NULL;\r
95   Status = gBS->InstallMultipleProtocolInterfaces (\r
96                   &ChildHandle,\r
97                   &gEfiDevicePathProtocolGuid,\r
98                   VlanDevicePath,\r
99                   NULL\r
100                   );\r
101   if (EFI_ERROR (Status)) {\r
102     FreePool (VlanDevicePath);\r
103     return NULL;\r
104   }\r
105 \r
106   if (Devicepath != NULL) {\r
107     *Devicepath = VlanDevicePath;\r
108   }\r
109 \r
110   return ChildHandle;\r
111 }\r
112 \r
113 /**\r
114   Remove VLAN tag from a packet.\r
115 \r
116   @param[in, out]  MnpDeviceData      Pointer to the mnp device context data.\r
117   @param[in, out]  Nbuf               Pointer to the NET_BUF to remove VLAN tag.\r
118   @param[out]      VlanId             Pointer to the returned VLAN ID.\r
119 \r
120   @retval TRUE             VLAN tag is removed from this packet.\r
121   @retval FALSE            There is no VLAN tag in this packet.\r
122 \r
123 **/\r
124 BOOLEAN\r
125 MnpRemoveVlanTag (\r
126   IN OUT MNP_DEVICE_DATA   *MnpDeviceData,\r
127   IN OUT NET_BUF           *Nbuf,\r
128      OUT UINT16            *VlanId\r
129   )\r
130 {\r
131   UINT8     *Packet;\r
132   UINTN     ProtocolOffset;\r
133   UINT16    ProtocolType;\r
134   VLAN_TCI  VlanTag;\r
135 \r
136   ProtocolOffset = MnpDeviceData->Snp->Mode->HwAddressSize * 2;\r
137 \r
138   //\r
139   // Get the packet buffer.\r
140   //\r
141   Packet = NetbufGetByte (Nbuf, 0, NULL);\r
142   ASSERT (Packet != NULL);\r
143 \r
144   //\r
145   // Check whether this is VLAN tagged frame by Ether Type\r
146   //\r
147   *VlanId      = 0;\r
148   ProtocolType = NTOHS (*(UINT16 *) (Packet + ProtocolOffset));\r
149   if (ProtocolType != ETHER_TYPE_VLAN) {\r
150     //\r
151     // Not a VLAN tagged frame\r
152     //\r
153     return FALSE;\r
154   }\r
155 \r
156   VlanTag.Uint16 = NTOHS (*(UINT16 *) (Packet + ProtocolOffset + sizeof (ProtocolType)));\r
157   *VlanId = VlanTag.Bits.Vid;\r
158 \r
159   //\r
160   // Move hardware address (DA + SA) 4 bytes right to override VLAN tag\r
161   //\r
162   CopyMem (Packet + NET_VLAN_TAG_LEN, Packet, ProtocolOffset);\r
163 \r
164   //\r
165   // Remove VLAN tag from the Nbuf\r
166   //\r
167   NetbufTrim (Nbuf, NET_VLAN_TAG_LEN, NET_BUF_HEAD);\r
168 \r
169   return TRUE;\r
170 }\r
171 \r
172 \r
173 /**\r
174   Build the vlan packet to transmit from the TxData passed in.\r
175 \r
176   @param  MnpServiceData         Pointer to the mnp service context data.\r
177   @param  TxData                 Pointer to the transmit data containing the\r
178                                  information to build the packet.\r
179   @param  ProtocolType           Pointer to the Ethernet protocol type.\r
180   @param  Packet                 Pointer to record the address of the packet.\r
181   @param  Length                 Pointer to a UINT32 variable used to record the\r
182                                  packet's length.\r
183 \r
184 **/\r
185 VOID\r
186 MnpInsertVlanTag (\r
187   IN     MNP_SERVICE_DATA                    *MnpServiceData,\r
188   IN     EFI_MANAGED_NETWORK_TRANSMIT_DATA   *TxData,\r
189      OUT UINT16                              *ProtocolType,\r
190   IN OUT UINT8                               **Packet,\r
191   IN OUT UINT32                              *Length\r
192   )\r
193 {\r
194   VLAN_TCI                *VlanTci;\r
195   UINT16                  *Tpid;\r
196   UINT16                  *EtherType;\r
197   MNP_DEVICE_DATA         *MnpDeviceData;\r
198   EFI_SIMPLE_NETWORK_MODE *SnpMode;\r
199 \r
200   MnpDeviceData = MnpServiceData->MnpDeviceData;\r
201   SnpMode       = MnpDeviceData->Snp->Mode;\r
202 \r
203   *ProtocolType = ETHER_TYPE_VLAN;\r
204   *Length = *Length + NET_VLAN_TAG_LEN;\r
205   *Packet = *Packet - NET_VLAN_TAG_LEN;\r
206 \r
207   Tpid    = (UINT16 *) (*Packet + SnpMode->MediaHeaderSize - sizeof (*ProtocolType));\r
208   VlanTci = (VLAN_TCI *) (UINTN) (Tpid + 1);\r
209   if (TxData->HeaderLength != 0) {\r
210     //\r
211     // Media header is in packet, move DA+SA 4 bytes left\r
212     //\r
213     CopyMem (\r
214       *Packet,\r
215       *Packet + NET_VLAN_TAG_LEN,\r
216       SnpMode->MediaHeaderSize - sizeof (*ProtocolType)\r
217       );\r
218     *Tpid = HTONS (ETHER_TYPE_VLAN);\r
219   } else {\r
220     //\r
221     // Media header not in packet, VLAN TCI and original protocol type becomes payload\r
222     //\r
223     EtherType  = (UINT16 *) (UINTN) (VlanTci + 1);\r
224     *EtherType = HTONS (TxData->ProtocolType);\r
225   }\r
226 \r
227   VlanTci->Bits.Vid      = MnpServiceData->VlanId;\r
228   VlanTci->Bits.Cfi      = VLAN_TCI_CFI_CANONICAL_MAC;\r
229   VlanTci->Bits.Priority = MnpServiceData->Priority;\r
230   VlanTci->Uint16        = HTONS (VlanTci->Uint16);\r
231 }\r
232 \r
233 \r
234 /**\r
235   Get VLAN configuration variable.\r
236 \r
237   @param[in]       MnpDeviceData      Pointer to the MNP device context data.\r
238   @param[out]      NumberOfVlan       Pointer to number of VLAN to be returned.\r
239   @param[out]      VlanVariable       Pointer to the buffer to return requested\r
240                                       array of VLAN_TCI.\r
241 \r
242   @retval EFI_SUCCESS            The array of VLAN_TCI was returned in VlanVariable\r
243                                  and number of VLAN was returned in NumberOfVlan.\r
244   @retval EFI_NOT_FOUND          VLAN configuration variable not found.\r
245   @retval EFI_OUT_OF_RESOURCES   There is not enough pool memory to store the configuration.\r
246 \r
247 **/\r
248 EFI_STATUS\r
249 MnpGetVlanVariable (\r
250   IN     MNP_DEVICE_DATA   *MnpDeviceData,\r
251      OUT UINTN             *NumberOfVlan,\r
252      OUT VLAN_TCI          **VlanVariable\r
253   )\r
254 {\r
255   UINTN       BufferSize;\r
256   EFI_STATUS  Status;\r
257   VLAN_TCI    *Buffer;\r
258 \r
259   //\r
260   // Get VLAN configuration from EFI Variable\r
261   //\r
262   Buffer = NULL;\r
263   BufferSize = 0;\r
264   Status = gRT->GetVariable (\r
265                   MnpDeviceData->MacString,\r
266                   &gEfiVlanConfigProtocolGuid,\r
267                   NULL,\r
268                   &BufferSize,\r
269                   NULL\r
270                   );\r
271   if (Status != EFI_BUFFER_TOO_SMALL) {\r
272     return EFI_NOT_FOUND;\r
273   }\r
274 \r
275   //\r
276   // Allocate buffer to read the variable\r
277   //\r
278   Buffer = AllocateZeroPool (BufferSize);\r
279   if (Buffer == NULL) {\r
280     return EFI_OUT_OF_RESOURCES;\r
281   }\r
282 \r
283   Status = gRT->GetVariable (\r
284                   MnpDeviceData->MacString,\r
285                   &gEfiVlanConfigProtocolGuid,\r
286                   NULL,\r
287                   &BufferSize,\r
288                   Buffer\r
289                   );\r
290   if (EFI_ERROR (Status)) {\r
291     FreePool (Buffer);\r
292     return Status;\r
293   }\r
294 \r
295   *NumberOfVlan = BufferSize / sizeof (VLAN_TCI);\r
296   *VlanVariable = Buffer;\r
297 \r
298   return Status;\r
299 }\r
300 \r
301 \r
302 /**\r
303   Set VLAN configuration variable.\r
304 \r
305   @param[in] MnpDeviceData       Pointer to the MNP device context data.\r
306   @param[in] NumberOfVlan        Number of VLAN in array VlanVariable.\r
307   @param[in] VlanVariable        Pointer to array of VLAN_TCI.\r
308 \r
309   @retval EFI_SUCCESS            The VLAN variable is successfully set.\r
310   @retval EFI_OUT_OF_RESOURCES   There is not enough resource to set the configuration.\r
311 \r
312 **/\r
313 EFI_STATUS\r
314 MnpSetVlanVariable (\r
315   IN MNP_DEVICE_DATA             *MnpDeviceData,\r
316   IN UINTN                       NumberOfVlan,\r
317   IN VLAN_TCI                    *VlanVariable\r
318   )\r
319 {\r
320   return gRT->SetVariable (\r
321                 MnpDeviceData->MacString,\r
322                 &gEfiVlanConfigProtocolGuid,\r
323                 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
324                 NumberOfVlan * sizeof (VLAN_TCI),\r
325                 VlanVariable\r
326                 );\r
327 }\r
328 \r
329 \r
330 /**\r
331   Create a VLAN device or modify the configuration parameter of an\r
332   already-configured VLAN.\r
333 \r
334   The Set() function is used to create a new VLAN device or change the VLAN\r
335   configuration parameters. If the VlanId hasn't been configured in the\r
336   physical Ethernet device, a new VLAN device will be created. If a VLAN with\r
337   this VlanId is already configured, then related configuration will be updated\r
338   as the input parameters.\r
339 \r
340   If VlanId is zero, the VLAN device will send and receive untagged frames.\r
341   Otherwise, the VLAN device will send and receive VLAN-tagged frames containing the VlanId.\r
342   If VlanId is out of scope of (0-4094), EFI_INVALID_PARAMETER is returned.\r
343   If Priority is out of the scope of (0-7), then EFI_INVALID_PARAMETER is returned.\r
344   If there is not enough system memory to perform the registration, then\r
345   EFI_OUT_OF_RESOURCES is returned.\r
346 \r
347   @param[in] This                Points to the EFI_VLAN_CONFIG_PROTOCOL.\r
348   @param[in] VlanId              A unique identifier (1-4094) of the VLAN which is being created\r
349                                  or modified, or zero (0).\r
350   @param[in] Priority            3 bit priority in VLAN header. Priority 0 is default value. If\r
351                                  VlanId is zero (0), Priority is ignored.\r
352 \r
353   @retval EFI_SUCCESS            The VLAN is successfully configured.\r
354   @retval EFI_INVALID_PARAMETER  One or more of following conditions is TRUE:\r
355                                  - This is NULL.\r
356                                  - VlanId is an invalid VLAN Identifier.\r
357                                  - Priority is invalid.\r
358   @retval EFI_OUT_OF_RESOURCES   There is not enough system memory to perform the registration.\r
359 \r
360 **/\r
361 EFI_STATUS\r
362 EFIAPI\r
363 VlanConfigSet (\r
364   IN EFI_VLAN_CONFIG_PROTOCOL    *This,\r
365   IN UINT16                      VlanId,\r
366   IN UINT8                       Priority\r
367   )\r
368 {\r
369   EFI_STATUS        Status;\r
370   MNP_DEVICE_DATA   *MnpDeviceData;\r
371   MNP_SERVICE_DATA  *MnpServiceData;\r
372   VLAN_TCI          *OldVariable;\r
373   VLAN_TCI          *NewVariable;\r
374   UINTN             NumberOfVlan;\r
375   UINTN             Index;\r
376   BOOLEAN           IsAdd;\r
377   LIST_ENTRY        *Entry;\r
378 \r
379   if ((This == NULL) || (VlanId > 4094) || (Priority > 7)) {\r
380     return EFI_INVALID_PARAMETER;\r
381   }\r
382 \r
383   IsAdd = FALSE;\r
384   MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);\r
385   if (MnpDeviceData->NumberOfVlan == 0) {\r
386     //\r
387     // No existing VLAN, this is the first VLAN to add\r
388     //\r
389     IsAdd = TRUE;\r
390     Entry = GetFirstNode (&MnpDeviceData->ServiceList);\r
391     MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);\r
392 \r
393     if (VlanId != 0) {\r
394       //\r
395       // VlanId is not 0, need destroy the default MNP service data\r
396       //\r
397       Status = MnpDestroyServiceChild (MnpServiceData);\r
398       if (EFI_ERROR (Status)) {\r
399         return Status;\r
400       }\r
401 \r
402       Status = MnpDestroyServiceData (MnpServiceData);\r
403       if (EFI_ERROR (Status)) {\r
404         return Status;\r
405       }\r
406 \r
407       //\r
408       // Create a new MNP service data for this VLAN\r
409       //\r
410       MnpServiceData = MnpCreateServiceData (MnpDeviceData, VlanId, Priority);\r
411       if (MnpServiceData == NULL) {\r
412         return EFI_OUT_OF_RESOURCES;\r
413       }\r
414     }\r
415   } else {\r
416     //\r
417     // Try to find VlanId in existing VLAN list\r
418     //\r
419     MnpServiceData = MnpFindServiceData (MnpDeviceData, VlanId);\r
420     if (MnpServiceData == NULL) {\r
421       //\r
422       // VlanId not found, create a new MNP service data\r
423       //\r
424       IsAdd = TRUE;\r
425       MnpServiceData = MnpCreateServiceData (MnpDeviceData, VlanId, Priority);\r
426       if (MnpServiceData == NULL) {\r
427         return EFI_OUT_OF_RESOURCES;\r
428       }\r
429     }\r
430   }\r
431 \r
432   MnpServiceData->VlanId = VlanId;\r
433   MnpServiceData->Priority = Priority;\r
434   if (IsAdd) {\r
435     MnpDeviceData->NumberOfVlan++;\r
436   }\r
437 \r
438   //\r
439   // Update VLAN configuration variable\r
440   //\r
441   OldVariable  = NULL;\r
442   NewVariable  = NULL;\r
443   NumberOfVlan = 0;\r
444   MnpGetVlanVariable (MnpDeviceData, &NumberOfVlan, &OldVariable);\r
445 \r
446   if (IsAdd) {\r
447     //\r
448     // VLAN not exist - add\r
449     //\r
450     NewVariable = AllocateZeroPool ((NumberOfVlan + 1) * sizeof (VLAN_TCI));\r
451     if (NewVariable == NULL) {\r
452       Status = EFI_OUT_OF_RESOURCES;\r
453       goto Exit;\r
454     }\r
455 \r
456     if (OldVariable != NULL) {\r
457       CopyMem (NewVariable, OldVariable, NumberOfVlan * sizeof (VLAN_TCI));\r
458     }\r
459 \r
460     Index = NumberOfVlan++;\r
461   } else {\r
462     //\r
463     // VLAN already exist - update\r
464     //\r
465     for (Index = 0; Index < NumberOfVlan; Index++) {\r
466       if (OldVariable[Index].Bits.Vid == VlanId) {\r
467         break;\r
468       }\r
469     }\r
470     ASSERT (Index < NumberOfVlan);\r
471 \r
472     NewVariable = OldVariable;\r
473     OldVariable = NULL;\r
474   }\r
475 \r
476   NewVariable[Index].Bits.Vid      = VlanId;\r
477   NewVariable[Index].Bits.Priority = Priority;\r
478 \r
479   Status = MnpSetVlanVariable (MnpDeviceData, NumberOfVlan, NewVariable);\r
480   FreePool (NewVariable);\r
481 \r
482 Exit:\r
483   if (OldVariable != NULL) {\r
484     FreePool (OldVariable);\r
485   }\r
486 \r
487   return Status;\r
488 }\r
489 \r
490 \r
491 /**\r
492   Find configuration information for specified VLAN or all configured VLANs.\r
493 \r
494   The Find() function is used to find the configuration information for matching\r
495   VLAN and allocate a buffer into which those entries are copied.\r
496 \r
497   @param[in]  This               Points to the EFI_VLAN_CONFIG_PROTOCOL.\r
498   @param[in]  VlanId             Pointer to VLAN identifier. Set to NULL to find all\r
499                                  configured VLANs.\r
500   @param[out] NumberOfVlan       The number of VLANs which is found by the specified criteria.\r
501   @param[out] Entries            The buffer which receive the VLAN configuration.\r
502 \r
503   @retval EFI_SUCCESS            The VLAN is successfully found.\r
504   @retval EFI_INVALID_PARAMETER  One or more of following conditions is TRUE:\r
505                                  - This is NULL.\r
506                                  - Specified VlanId is invalid.\r
507   @retval EFI_NOT_FOUND          No matching VLAN is found.\r
508 \r
509 **/\r
510 EFI_STATUS\r
511 EFIAPI\r
512 VlanConfigFind (\r
513   IN     EFI_VLAN_CONFIG_PROTOCOL    *This,\r
514   IN     UINT16                      *VlanId OPTIONAL,\r
515      OUT UINT16                      *NumberOfVlan,\r
516      OUT EFI_VLAN_FIND_DATA          **Entries\r
517   )\r
518 {\r
519   MNP_DEVICE_DATA     *MnpDeviceData;\r
520   MNP_SERVICE_DATA    *MnpServiceData;\r
521   LIST_ENTRY          *Entry;\r
522   EFI_VLAN_FIND_DATA  *VlanData;\r
523 \r
524   if ((This == NULL) || (VlanId != NULL && *VlanId > 4094) || (NumberOfVlan == NULL) || (Entries == NULL)) {\r
525     return EFI_INVALID_PARAMETER;\r
526   }\r
527 \r
528   *NumberOfVlan = 0;\r
529   *Entries      = NULL;\r
530 \r
531   MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);\r
532   if (MnpDeviceData->NumberOfVlan == 0) {\r
533     return EFI_NOT_FOUND;\r
534   }\r
535 \r
536   if (VlanId == NULL) {\r
537     //\r
538     // Return all current VLAN configuration\r
539     //\r
540     *NumberOfVlan = (UINT16) MnpDeviceData->NumberOfVlan;\r
541     VlanData = AllocateZeroPool (*NumberOfVlan * sizeof (EFI_VLAN_FIND_DATA));\r
542     if (VlanData == NULL) {\r
543       return EFI_OUT_OF_RESOURCES;\r
544     }\r
545 \r
546     *Entries = VlanData;\r
547     NET_LIST_FOR_EACH (Entry, &MnpDeviceData->ServiceList) {\r
548       MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);\r
549 \r
550       VlanData->VlanId = MnpServiceData->VlanId;\r
551       VlanData->Priority = MnpServiceData->Priority;\r
552       VlanData++;\r
553     }\r
554 \r
555     return EFI_SUCCESS;\r
556   }\r
557 \r
558   //\r
559   // VlanId is specified, try to find it in current VLAN list\r
560   //\r
561   MnpServiceData = MnpFindServiceData (MnpDeviceData, *VlanId);\r
562   if (MnpServiceData == NULL) {\r
563     return EFI_NOT_FOUND;\r
564   }\r
565 \r
566   VlanData = AllocateZeroPool (sizeof (EFI_VLAN_FIND_DATA));\r
567   if (VlanData == NULL) {\r
568     return EFI_OUT_OF_RESOURCES;\r
569   }\r
570   VlanData->VlanId = MnpServiceData->VlanId;\r
571   VlanData->Priority = MnpServiceData->Priority;\r
572 \r
573   *NumberOfVlan = 1;\r
574   *Entries = VlanData;\r
575 \r
576   return EFI_SUCCESS;\r
577 }\r
578 \r
579 \r
580 /**\r
581   Remove the configured VLAN device.\r
582 \r
583   The Remove() function is used to remove the specified VLAN device.\r
584   If the VlanId is out of the scope of (0-4094), EFI_INVALID_PARAMETER is returned.\r
585   If specified VLAN hasn't been previously configured, EFI_NOT_FOUND is returned.\r
586 \r
587   @param[in] This                Points to the EFI_VLAN_CONFIG_PROTOCOL.\r
588   @param[in] VlanId              Identifier (0-4094) of the VLAN to be removed.\r
589 \r
590   @retval EFI_SUCCESS            The VLAN is successfully removed.\r
591   @retval EFI_INVALID_PARAMETER  One or more of following conditions is TRUE:\r
592                                  - This is NULL.\r
593                                  - VlanId  is an invalid parameter.\r
594   @retval EFI_NOT_FOUND          The to-be-removed VLAN does not exist.\r
595 \r
596 **/\r
597 EFI_STATUS\r
598 EFIAPI\r
599 VlanConfigRemove (\r
600   IN EFI_VLAN_CONFIG_PROTOCOL    *This,\r
601   IN UINT16                      VlanId\r
602   )\r
603 {\r
604   EFI_STATUS        Status;\r
605   MNP_DEVICE_DATA   *MnpDeviceData;\r
606   MNP_SERVICE_DATA  *MnpServiceData;\r
607   LIST_ENTRY        *Entry;\r
608   VLAN_TCI          *VlanVariable;\r
609   VLAN_TCI          *VlanData;\r
610 \r
611   if ((This == NULL) || (VlanId > 4094)) {\r
612     return EFI_INVALID_PARAMETER;\r
613   }\r
614 \r
615   MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);\r
616   if (MnpDeviceData->NumberOfVlan == 0) {\r
617     return EFI_NOT_FOUND;\r
618   }\r
619 \r
620   //\r
621   // Try to find the VlanId\r
622   //\r
623   MnpServiceData = MnpFindServiceData (MnpDeviceData, VlanId);\r
624   if (MnpServiceData == NULL) {\r
625     return EFI_NOT_FOUND;\r
626   }\r
627 \r
628   MnpDeviceData->NumberOfVlan--;\r
629 \r
630   if ((VlanId != 0) || (MnpDeviceData->NumberOfVlan != 0)) {\r
631     //\r
632     // If VlanId is not 0 or VlanId is 0 and it is not the last VLAN to remove,\r
633     // destroy its MNP service data\r
634     //\r
635     Status = MnpDestroyServiceChild (MnpServiceData);\r
636     if (EFI_ERROR (Status)) {\r
637       return Status;\r
638     }\r
639 \r
640     Status = MnpDestroyServiceData (MnpServiceData);\r
641     if (EFI_ERROR (Status)) {\r
642       return Status;\r
643     }\r
644   }\r
645 \r
646   if ((VlanId != 0) && (MnpDeviceData->NumberOfVlan == 0)) {\r
647     //\r
648     // This is the last VLAN to be removed, restore the default MNP service data\r
649     //\r
650     MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0);\r
651     if (MnpServiceData == NULL) {\r
652       return EFI_OUT_OF_RESOURCES;\r
653     }\r
654   }\r
655 \r
656   //\r
657   // Update VLAN configuration variable\r
658   //\r
659   VlanVariable = NULL;\r
660   if (MnpDeviceData->NumberOfVlan != 0) {\r
661     VlanVariable = AllocatePool (MnpDeviceData->NumberOfVlan * sizeof (VLAN_TCI));\r
662     if (VlanVariable == NULL) {\r
663       return EFI_OUT_OF_RESOURCES;\r
664     }\r
665 \r
666     VlanData = VlanVariable;\r
667     NET_LIST_FOR_EACH (Entry, &MnpDeviceData->ServiceList) {\r
668       MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);\r
669 \r
670       VlanData->Bits.Vid      = MnpServiceData->VlanId;\r
671       VlanData->Bits.Priority = MnpServiceData->Priority;\r
672       VlanData++;\r
673     }\r
674   }\r
675 \r
676   Status = MnpSetVlanVariable (MnpDeviceData, MnpDeviceData->NumberOfVlan, VlanVariable);\r
677 \r
678   if (VlanVariable != NULL) {\r
679     FreePool (VlanVariable);\r
680   }\r
681 \r
682   return Status;\r
683 }\r