850ed51622f04b2f56357d6141e32cc12eebf7a5
[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, 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 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   if (MnpServiceData->VlanId == 0) {\r
201     *ProtocolType = TxData->ProtocolType;\r
202     return ;\r
203   }\r
204 \r
205   MnpDeviceData = MnpServiceData->MnpDeviceData;\r
206   SnpMode       = MnpDeviceData->Snp->Mode;\r
207 \r
208   *ProtocolType = ETHER_TYPE_VLAN;\r
209   *Length = *Length + NET_VLAN_TAG_LEN;\r
210   *Packet = *Packet - NET_VLAN_TAG_LEN;\r
211 \r
212   Tpid    = (UINT16 *) (*Packet + SnpMode->MediaHeaderSize - sizeof (*ProtocolType));\r
213   VlanTci = (VLAN_TCI *) (UINTN) (Tpid + 1);\r
214   if (TxData->HeaderLength != 0) {\r
215     //\r
216     // Media header is in packet, move DA+SA 4 bytes left\r
217     //\r
218     CopyMem (\r
219       *Packet,\r
220       *Packet + NET_VLAN_TAG_LEN,\r
221       SnpMode->MediaHeaderSize - sizeof (*ProtocolType)\r
222       );\r
223     *Tpid = HTONS (ETHER_TYPE_VLAN);\r
224   } else {\r
225     //\r
226     // Media header not in packet, VLAN TCI and original protocol type becomes payload\r
227     //\r
228     EtherType  = (UINT16 *) (UINTN) (VlanTci + 1);\r
229     *EtherType = HTONS (TxData->ProtocolType);\r
230   }\r
231 \r
232   VlanTci->Bits.Vid      = MnpServiceData->VlanId;\r
233   VlanTci->Bits.Cfi      = VLAN_TCI_CFI_CANONICAL_MAC;\r
234   VlanTci->Bits.Priority = MnpServiceData->Priority;\r
235   VlanTci->Uint16        = HTONS (VlanTci->Uint16);\r
236 }\r
237 \r
238 \r
239 /**\r
240   Get VLAN configuration variable.\r
241 \r
242   @param[in]       MnpDeviceData      Pointer to the MNP device context data.\r
243   @param[out]      NumberOfVlan       Pointer to number of VLAN to be returned.\r
244   @param[out]      VlanVariable       Pointer to the buffer to return requested\r
245                                       array of VLAN_TCI.\r
246 \r
247   @retval EFI_SUCCESS            The array of VLAN_TCI was returned in VlanVariable\r
248                                  and number of VLAN was returned in NumberOfVlan.\r
249   @retval EFI_NOT_FOUND          VLAN configuration variable not found.\r
250   @retval EFI_OUT_OF_RESOURCES   There is not enough pool memory to store the configuration.\r
251 \r
252 **/\r
253 EFI_STATUS\r
254 MnpGetVlanVariable (\r
255   IN     MNP_DEVICE_DATA   *MnpDeviceData,\r
256      OUT UINTN             *NumberOfVlan,\r
257      OUT VLAN_TCI          **VlanVariable\r
258   )\r
259 {\r
260   UINTN       BufferSize;\r
261   EFI_STATUS  Status;\r
262   VLAN_TCI    *Buffer;\r
263 \r
264   //\r
265   // Get VLAN configuration from EFI Variable\r
266   //\r
267   Buffer = NULL;\r
268   BufferSize = 0;\r
269   Status = gRT->GetVariable (\r
270                   MnpDeviceData->MacString,\r
271                   &gEfiVlanConfigProtocolGuid,\r
272                   NULL,\r
273                   &BufferSize,\r
274                   NULL\r
275                   );\r
276   if (Status != EFI_BUFFER_TOO_SMALL) {\r
277     return EFI_NOT_FOUND;\r
278   }\r
279 \r
280   //\r
281   // Allocate buffer to read the variable\r
282   //\r
283   Buffer = AllocateZeroPool (BufferSize);\r
284   if (Buffer == NULL) {\r
285     return EFI_OUT_OF_RESOURCES;\r
286   }\r
287 \r
288   Status = gRT->GetVariable (\r
289                   MnpDeviceData->MacString,\r
290                   &gEfiVlanConfigProtocolGuid,\r
291                   NULL,\r
292                   &BufferSize,\r
293                   Buffer\r
294                   );\r
295   if (EFI_ERROR (Status)) {\r
296     FreePool (Buffer);\r
297     return Status;\r
298   }\r
299 \r
300   *NumberOfVlan = BufferSize / sizeof (VLAN_TCI);\r
301   *VlanVariable = Buffer;\r
302 \r
303   return Status;\r
304 }\r
305 \r
306 \r
307 /**\r
308   Set VLAN configuration variable.\r
309 \r
310   @param[in] MnpDeviceData       Pointer to the MNP device context data.\r
311   @param[in] NumberOfVlan        Number of VLAN in array VlanVariable.\r
312   @param[in] VlanVariable        Pointer to array of VLAN_TCI.\r
313 \r
314   @retval EFI_SUCCESS            The VLAN variable is successfully set.\r
315   @retval EFI_OUT_OF_RESOURCES   There is not enough resource to set the configuration.\r
316 \r
317 **/\r
318 EFI_STATUS\r
319 MnpSetVlanVariable (\r
320   IN MNP_DEVICE_DATA             *MnpDeviceData,\r
321   IN UINTN                       NumberOfVlan,\r
322   IN VLAN_TCI                    *VlanVariable\r
323   )\r
324 {\r
325   return gRT->SetVariable (\r
326                 MnpDeviceData->MacString,\r
327                 &gEfiVlanConfigProtocolGuid,\r
328                 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
329                 NumberOfVlan * sizeof (VLAN_TCI),\r
330                 VlanVariable\r
331                 );\r
332 }\r
333 \r
334 \r
335 /**\r
336   Create a VLAN device or modify the configuration parameter of an\r
337   already-configured VLAN.\r
338 \r
339   The Set() function is used to create a new VLAN device or change the VLAN\r
340   configuration parameters. If the VlanId hasn't been configured in the\r
341   physical Ethernet device, a new VLAN device will be created. If a VLAN with\r
342   this VlanId is already configured, then related configuration will be updated\r
343   as the input parameters.\r
344 \r
345   If VlanId is zero, the VLAN device will send and receive untagged frames.\r
346   Otherwise, the VLAN device will send and receive VLAN-tagged frames containing the VlanId.\r
347   If VlanId is out of scope of (0-4094), EFI_INVALID_PARAMETER is returned.\r
348   If Priority is out of the scope of (0-7), then EFI_INVALID_PARAMETER is returned.\r
349   If there is not enough system memory to perform the registration, then\r
350   EFI_OUT_OF_RESOURCES is returned.\r
351 \r
352   @param[in] This                Points to the EFI_VLAN_CONFIG_PROTOCOL.\r
353   @param[in] VlanId              A unique identifier (1-4094) of the VLAN which is being created\r
354                                  or modified, or zero (0).\r
355   @param[in] Priority            3 bit priority in VLAN header. Priority 0 is default value. If\r
356                                  VlanId is zero (0), Priority is ignored.\r
357 \r
358   @retval EFI_SUCCESS            The VLAN is successfully configured.\r
359   @retval EFI_INVALID_PARAMETER  One or more of following conditions is TRUE:\r
360                                  - This is NULL.\r
361                                  - VlanId is an invalid VLAN Identifier.\r
362                                  - Priority is invalid.\r
363   @retval EFI_OUT_OF_RESOURCES   There is not enough system memory to perform the registration.\r
364 \r
365 **/\r
366 EFI_STATUS\r
367 EFIAPI\r
368 VlanConfigSet (\r
369   IN EFI_VLAN_CONFIG_PROTOCOL    *This,\r
370   IN UINT16                      VlanId,\r
371   IN UINT8                       Priority\r
372   )\r
373 {\r
374   EFI_STATUS        Status;\r
375   MNP_DEVICE_DATA   *MnpDeviceData;\r
376   MNP_SERVICE_DATA  *MnpServiceData;\r
377   VLAN_TCI          *OldVariable;\r
378   VLAN_TCI          *NewVariable;\r
379   UINTN             NumberOfVlan;\r
380   UINTN             Index;\r
381   BOOLEAN           IsAdd;\r
382   LIST_ENTRY        *Entry;\r
383 \r
384   if ((This == NULL) || (VlanId > 4094) || (Priority > 7)) {\r
385     return EFI_INVALID_PARAMETER;\r
386   }\r
387 \r
388   IsAdd = FALSE;\r
389   MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);\r
390   if (MnpDeviceData->NumberOfVlan == 0) {\r
391     //\r
392     // No existing VLAN, this is the first VLAN to add\r
393     //\r
394     IsAdd = TRUE;\r
395     Entry = GetFirstNode (&MnpDeviceData->ServiceList);\r
396     MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);\r
397 \r
398     if (VlanId != 0) {\r
399       //\r
400       // VlanId is not 0, need destroy the default MNP service data\r
401       //\r
402       Status = MnpDestroyServiceChild (MnpServiceData);\r
403       if (EFI_ERROR (Status)) {\r
404         return Status;\r
405       }\r
406 \r
407       Status = MnpDestroyServiceData (MnpServiceData);\r
408       if (EFI_ERROR (Status)) {\r
409         return Status;\r
410       }\r
411 \r
412       //\r
413       // Create a new MNP service data for this VLAN\r
414       //\r
415       MnpServiceData = MnpCreateServiceData (MnpDeviceData, VlanId, Priority);\r
416       if (MnpServiceData == NULL) {\r
417         return EFI_OUT_OF_RESOURCES;\r
418       }\r
419     }\r
420   } else {\r
421     //\r
422     // Try to find VlanId in existing VLAN list\r
423     //\r
424     MnpServiceData = MnpFindServiceData (MnpDeviceData, VlanId);\r
425     if (MnpServiceData == NULL) {\r
426       //\r
427       // VlanId not found, create a new MNP service data\r
428       //\r
429       IsAdd = TRUE;\r
430       MnpServiceData = MnpCreateServiceData (MnpDeviceData, VlanId, Priority);\r
431       if (MnpServiceData == NULL) {\r
432         return EFI_OUT_OF_RESOURCES;\r
433       }\r
434     }\r
435   }\r
436 \r
437   MnpServiceData->VlanId = VlanId;\r
438   MnpServiceData->Priority = Priority;\r
439   if (IsAdd) {\r
440     MnpDeviceData->NumberOfVlan++;\r
441   }\r
442 \r
443   //\r
444   // Update VLAN configuration variable\r
445   //\r
446   OldVariable  = NULL;\r
447   NewVariable  = NULL;\r
448   NumberOfVlan = 0;\r
449   MnpGetVlanVariable (MnpDeviceData, &NumberOfVlan, &OldVariable);\r
450 \r
451   if (IsAdd) {\r
452     //\r
453     // VLAN not exist - add\r
454     //\r
455     NewVariable = AllocateZeroPool ((NumberOfVlan + 1) * sizeof (VLAN_TCI));\r
456     if (NewVariable == NULL) {\r
457       Status = EFI_OUT_OF_RESOURCES;\r
458       goto Exit;\r
459     }\r
460 \r
461     if (OldVariable != NULL) {\r
462       CopyMem (NewVariable, OldVariable, NumberOfVlan * sizeof (VLAN_TCI));\r
463     }\r
464 \r
465     Index = NumberOfVlan++;\r
466   } else {\r
467     //\r
468     // VLAN already exist - update\r
469     //\r
470     for (Index = 0; Index < NumberOfVlan; Index++) {\r
471       if (OldVariable[Index].Bits.Vid == VlanId) {\r
472         break;\r
473       }\r
474     }\r
475     ASSERT (Index < NumberOfVlan);\r
476 \r
477     NewVariable = OldVariable;\r
478     OldVariable = NULL;\r
479   }\r
480 \r
481   NewVariable[Index].Bits.Vid      = VlanId;\r
482   NewVariable[Index].Bits.Priority = Priority;\r
483 \r
484   Status = MnpSetVlanVariable (MnpDeviceData, NumberOfVlan, NewVariable);\r
485   FreePool (NewVariable);\r
486 \r
487 Exit:\r
488   if (OldVariable != NULL) {\r
489     FreePool (OldVariable);\r
490   }\r
491 \r
492   return Status;\r
493 }\r
494 \r
495 \r
496 /**\r
497   Find configuration information for specified VLAN or all configured VLANs.\r
498 \r
499   The Find() function is used to find the configuration information for matching\r
500   VLAN and allocate a buffer into which those entries are copied.\r
501 \r
502   @param[in]  This               Points to the EFI_VLAN_CONFIG_PROTOCOL.\r
503   @param[in]  VlanId             Pointer to VLAN identifier. Set to NULL to find all\r
504                                  configured VLANs.\r
505   @param[out] NumberOfVlan       The number of VLANs which is found by the specified criteria.\r
506   @param[out] Entries            The buffer which receive the VLAN configuration.\r
507 \r
508   @retval EFI_SUCCESS            The VLAN is successfully found.\r
509   @retval EFI_INVALID_PARAMETER  One or more of following conditions is TRUE:\r
510                                  - This is NULL.\r
511                                  - Specified VlanId is invalid.\r
512   @retval EFI_NOT_FOUND          No matching VLAN is found.\r
513 \r
514 **/\r
515 EFI_STATUS\r
516 EFIAPI\r
517 VlanConfigFind (\r
518   IN     EFI_VLAN_CONFIG_PROTOCOL    *This,\r
519   IN     UINT16                      *VlanId OPTIONAL,\r
520      OUT UINT16                      *NumberOfVlan,\r
521      OUT EFI_VLAN_FIND_DATA          **Entries\r
522   )\r
523 {\r
524   MNP_DEVICE_DATA     *MnpDeviceData;\r
525   MNP_SERVICE_DATA    *MnpServiceData;\r
526   LIST_ENTRY          *Entry;\r
527   EFI_VLAN_FIND_DATA  *VlanData;\r
528 \r
529   if ((This == NULL) || (VlanId != NULL && *VlanId > 4094) || (NumberOfVlan == NULL) || (Entries == NULL)) {\r
530     return EFI_INVALID_PARAMETER;\r
531   }\r
532 \r
533   *NumberOfVlan = 0;\r
534   *Entries      = NULL;\r
535 \r
536   MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);\r
537   if (MnpDeviceData->NumberOfVlan == 0) {\r
538     return EFI_NOT_FOUND;\r
539   }\r
540 \r
541   if (VlanId == NULL) {\r
542     //\r
543     // Return all current VLAN configuration\r
544     //\r
545     *NumberOfVlan = (UINT16) MnpDeviceData->NumberOfVlan;\r
546     VlanData = AllocateZeroPool (*NumberOfVlan * sizeof (EFI_VLAN_FIND_DATA));\r
547     if (VlanData == NULL) {\r
548       return EFI_OUT_OF_RESOURCES;\r
549     }\r
550 \r
551     *Entries = VlanData;\r
552     NET_LIST_FOR_EACH (Entry, &MnpDeviceData->ServiceList) {\r
553       MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);\r
554 \r
555       VlanData->VlanId = MnpServiceData->VlanId;\r
556       VlanData->Priority = MnpServiceData->Priority;\r
557       VlanData++;\r
558     }\r
559 \r
560     return EFI_SUCCESS;\r
561   }\r
562 \r
563   //\r
564   // VlanId is specified, try to find it in current VLAN list\r
565   //\r
566   MnpServiceData = MnpFindServiceData (MnpDeviceData, *VlanId);\r
567   if (MnpServiceData == NULL) {\r
568     return EFI_NOT_FOUND;\r
569   }\r
570 \r
571   VlanData = AllocateZeroPool (sizeof (EFI_VLAN_FIND_DATA));\r
572   if (VlanData == NULL) {\r
573     return EFI_OUT_OF_RESOURCES;\r
574   }\r
575   VlanData->VlanId = MnpServiceData->VlanId;\r
576   VlanData->Priority = MnpServiceData->Priority;\r
577 \r
578   *NumberOfVlan = 1;\r
579   *Entries = VlanData;\r
580 \r
581   return EFI_SUCCESS;\r
582 }\r
583 \r
584 \r
585 /**\r
586   Remove the configured VLAN device.\r
587 \r
588   The Remove() function is used to remove the specified VLAN device.\r
589   If the VlanId is out of the scope of (0-4094), EFI_INVALID_PARAMETER is returned.\r
590   If specified VLAN hasn't been previously configured, EFI_NOT_FOUND is returned.\r
591 \r
592   @param[in] This                Points to the EFI_VLAN_CONFIG_PROTOCOL.\r
593   @param[in] VlanId              Identifier (0-4094) of the VLAN to be removed.\r
594 \r
595   @retval EFI_SUCCESS            The VLAN is successfully removed.\r
596   @retval EFI_INVALID_PARAMETER  One or more of following conditions is TRUE:\r
597                                  - This is NULL.\r
598                                  - VlanId  is an invalid parameter.\r
599   @retval EFI_NOT_FOUND          The to-be-removed VLAN does not exist.\r
600 \r
601 **/\r
602 EFI_STATUS\r
603 EFIAPI\r
604 VlanConfigRemove (\r
605   IN EFI_VLAN_CONFIG_PROTOCOL    *This,\r
606   IN UINT16                      VlanId\r
607   )\r
608 {\r
609   EFI_STATUS        Status;\r
610   MNP_DEVICE_DATA   *MnpDeviceData;\r
611   MNP_SERVICE_DATA  *MnpServiceData;\r
612   LIST_ENTRY        *Entry;\r
613   VLAN_TCI          *VlanVariable;\r
614   VLAN_TCI          *VlanData;\r
615 \r
616   if ((This == NULL) || (VlanId > 4094)) {\r
617     return EFI_INVALID_PARAMETER;\r
618   }\r
619 \r
620   MnpDeviceData = MNP_DEVICE_DATA_FROM_THIS (This);\r
621   if (MnpDeviceData->NumberOfVlan == 0) {\r
622     return EFI_NOT_FOUND;\r
623   }\r
624 \r
625   //\r
626   // Try to find the VlanId\r
627   //\r
628   MnpServiceData = MnpFindServiceData (MnpDeviceData, VlanId);\r
629   if (MnpServiceData == NULL) {\r
630     return EFI_NOT_FOUND;\r
631   }\r
632 \r
633   MnpDeviceData->NumberOfVlan--;\r
634 \r
635   if ((VlanId != 0) || (MnpDeviceData->NumberOfVlan != 0)) {\r
636     //\r
637     // If VlanId is not 0 or VlanId is 0 and it is not the last VLAN to remove,\r
638     // destroy its MNP service data\r
639     //\r
640     Status = MnpDestroyServiceChild (MnpServiceData);\r
641     if (EFI_ERROR (Status)) {\r
642       return Status;\r
643     }\r
644 \r
645     Status = MnpDestroyServiceData (MnpServiceData);\r
646     if (EFI_ERROR (Status)) {\r
647       return Status;\r
648     }\r
649   }\r
650 \r
651   if ((VlanId != 0) && (MnpDeviceData->NumberOfVlan == 0)) {\r
652     //\r
653     // This is the last VLAN to be removed, restore the default MNP service data\r
654     //\r
655     MnpServiceData = MnpCreateServiceData (MnpDeviceData, 0, 0);\r
656     if (MnpServiceData == NULL) {\r
657       return EFI_OUT_OF_RESOURCES;\r
658     }\r
659   }\r
660 \r
661   //\r
662   // Update VLAN configuration variable\r
663   //\r
664   VlanVariable = NULL;\r
665   if (MnpDeviceData->NumberOfVlan != 0) {\r
666     VlanVariable = AllocatePool (MnpDeviceData->NumberOfVlan * sizeof (VLAN_TCI));\r
667     if (VlanVariable == NULL) {\r
668       return EFI_OUT_OF_RESOURCES;\r
669     }\r
670 \r
671     VlanData = VlanVariable;\r
672     NET_LIST_FOR_EACH (Entry, &MnpDeviceData->ServiceList) {\r
673       MnpServiceData = MNP_SERVICE_DATA_FROM_LINK (Entry);\r
674 \r
675       VlanData->Bits.Vid      = MnpServiceData->VlanId;\r
676       VlanData->Bits.Priority = MnpServiceData->Priority;\r
677       VlanData++;\r
678     }\r
679   }\r
680 \r
681   Status = MnpSetVlanVariable (MnpDeviceData, MnpDeviceData->NumberOfVlan, VlanVariable);\r
682 \r
683   if (VlanVariable != NULL) {\r
684     FreePool (VlanVariable);\r
685   }\r
686 \r
687   return Status;\r
688 }\r