58451608e5737f30be2cc7f719068f1233b06ae1
[mirror_edk2.git] / MdeModulePkg / Universal / Network / SnpDxe / Snp.c
1 /** @file\r
2     Implementation of driver entry point and driver binding protocol.\r
3  \r
4 Copyright (c) 2004 - 2008, Intel Corporation. <BR> \r
5 All rights reserved. This program and the accompanying materials are licensed \r
6 and made available under the terms and conditions of the BSD License which \r
7 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 "Snp.h"\r
16 \r
17 //\r
18 //  Module global variables needed to support undi 3.0 interface\r
19 //\r
20 EFI_PCI_IO_PROTOCOL         *mPciIo;\r
21 V2P                         *mV2p = NULL; // undi3.0 map_list head\r
22 // End Global variables\r
23 //\r
24 \r
25 \r
26 /**\r
27   Send command to UNDI. It does nothing currently.\r
28   \r
29   @param Cdb   command to be sent to UNDI.\r
30    \r
31   @retval EFI_INVALID_PARAMETER  The command is 0. \r
32   @retval EFI_UNSUPPORTED        Default return status because it's not \r
33                                  supported currently.\r
34    \r
35 **/\r
36 EFI_STATUS\r
37 IssueHwUndiCommand (\r
38   UINT64 Cdb\r
39   )\r
40 {\r
41   DEBUG ((EFI_D_ERROR, "\nIssueHwUndiCommand() - This should not be called!"));\r
42 \r
43   if (Cdb == 0) {\r
44     return EFI_INVALID_PARAMETER;\r
45 \r
46   }\r
47   //\r
48   //  %%TBD - For now, nothing is done.\r
49   //\r
50   return EFI_UNSUPPORTED;\r
51 }\r
52 \r
53 \r
54 /**\r
55   Compute 8-bit checksum of a buffer.\r
56 \r
57   @param  Buffer               Pointer to buffer.\r
58   @param  Length               Length of buffer in bytes.\r
59 \r
60   @return 8-bit checksum of all bytes in buffer, or zero if ptr is NULL or len \r
61           is zero.\r
62 \r
63 **/\r
64 UINT8\r
65 Calc8BitCksum (\r
66   VOID  *Buffer,\r
67   UINTN Length\r
68   )\r
69 {\r
70   UINT8 *Ptr;\r
71   UINT8 Cksum;\r
72 \r
73   Ptr   = Buffer;\r
74   Cksum = 0;\r
75 \r
76   if (Ptr == NULL || Length == 0) {\r
77     return 0;\r
78   }\r
79 \r
80   while (Length-- != 0) {\r
81     Cksum = (UINT8) (Cksum + *Ptr++);\r
82   }\r
83 \r
84   return Cksum;\r
85 }\r
86 \r
87 /**\r
88   Test to see if this driver supports ControllerHandle. This service\r
89   is called by the EFI boot service ConnectController(). In\r
90   order to make drivers as small as possible, there are a few calling\r
91   restrictions for this service. ConnectController() must\r
92   follow these calling restrictions. If any other agent wishes to call\r
93   Supported() it must also follow these calling restrictions.\r
94 \r
95   @param  This                Protocol instance pointer.\r
96   @param  ControllerHandle    Handle of device to test.\r
97   @param  RemainingDevicePath Optional parameter use to pick a specific child\r
98                               device to start.\r
99 \r
100   @retval EFI_SUCCESS         This driver supports this device.\r
101   @retval EFI_ALREADY_STARTED This driver is already running on this device.\r
102   @retval other               This driver does not support this device.\r
103 \r
104 **/\r
105 EFI_STATUS\r
106 EFIAPI\r
107 SimpleNetworkDriverSupported (\r
108   IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
109   IN EFI_HANDLE                     Controller,\r
110   IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
111   )\r
112 {\r
113   EFI_STATUS                                Status;\r
114   EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *NiiProtocol;\r
115   PXE_UNDI                                  *Pxe;\r
116 \r
117   Status = gBS->OpenProtocol (\r
118                   Controller,\r
119                   &gEfiDevicePathProtocolGuid,\r
120                   NULL,\r
121                   This->DriverBindingHandle,\r
122                   Controller,\r
123                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
124                   );\r
125   if (EFI_ERROR (Status)) {\r
126     return Status;\r
127   }\r
128 \r
129   Status = gBS->OpenProtocol (\r
130                   Controller,\r
131                   &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
132                   (VOID **) &NiiProtocol,\r
133                   This->DriverBindingHandle,\r
134                   Controller,\r
135                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
136                   );\r
137 \r
138   if (EFI_ERROR (Status)) {\r
139     if (Status == EFI_ALREADY_STARTED) {\r
140       DEBUG ((EFI_D_INFO, "Support(): Already Started. on handle %p\n", Controller));\r
141     }\r
142     return Status;\r
143   }\r
144 \r
145   DEBUG ((EFI_D_INFO, "Support(): UNDI3.1 found on handle %p\n", Controller));\r
146 \r
147   //\r
148   // check the version, we don't want to connect to the undi16\r
149   //\r
150   if (NiiProtocol->Type != EfiNetworkInterfaceUndi) {\r
151     Status = EFI_UNSUPPORTED;\r
152     goto Done;\r
153   }\r
154   //\r
155   // Check to see if !PXE structure is valid. Paragraph alignment of !PXE structure is required.\r
156   //\r
157   if (NiiProtocol->ID & 0x0F) {\r
158     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\n!PXE structure is not paragraph aligned.\n"));\r
159     Status = EFI_UNSUPPORTED;\r
160     goto Done;\r
161   }\r
162 \r
163   Pxe = (PXE_UNDI *) (UINTN) (NiiProtocol->ID);\r
164 \r
165   //\r
166   //  Verify !PXE revisions.\r
167   //\r
168   if (Pxe->hw.Signature != PXE_ROMID_SIGNATURE) {\r
169     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\n!PXE signature is not valid.\n"));\r
170     Status = EFI_UNSUPPORTED;\r
171     goto Done;\r
172   }\r
173 \r
174   if (Pxe->hw.Rev < PXE_ROMID_REV) {\r
175     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\n!PXE.Rev is not supported.\n"));\r
176     Status = EFI_UNSUPPORTED;\r
177     goto Done;\r
178   }\r
179 \r
180   if (Pxe->hw.MajorVer < PXE_ROMID_MAJORVER) {\r
181 \r
182     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\n!PXE.MajorVer is not supported.\n"));\r
183     Status = EFI_UNSUPPORTED;\r
184     goto Done;\r
185 \r
186   } else if (Pxe->hw.MajorVer == PXE_ROMID_MAJORVER && Pxe->hw.MinorVer < PXE_ROMID_MINORVER) {\r
187     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\n!PXE.MinorVer is not supported."));\r
188     Status = EFI_UNSUPPORTED;\r
189     goto Done;\r
190   }\r
191   //\r
192   // Do S/W UNDI specific checks.\r
193   //\r
194   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) == 0) {\r
195     if (Pxe->sw.EntryPoint < Pxe->sw.Len) {\r
196       DEBUG ((EFI_D_ERROR | EFI_D_NET, "\n!PXE S/W entry point is not valid."));\r
197       Status = EFI_UNSUPPORTED;\r
198       goto Done;\r
199     }\r
200 \r
201     if (Pxe->sw.BusCnt == 0) {\r
202       DEBUG ((EFI_D_ERROR | EFI_D_NET, "\n!PXE.BusCnt is zero."));\r
203       Status = EFI_UNSUPPORTED;\r
204       goto Done;\r
205     }\r
206   }\r
207 \r
208   Status = EFI_SUCCESS;\r
209   DEBUG ((EFI_D_INFO, "Support(): supported on %p\n", Controller));\r
210 \r
211 Done:\r
212   gBS->CloseProtocol (\r
213         Controller,\r
214         &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
215         This->DriverBindingHandle,\r
216         Controller\r
217         );\r
218 \r
219   return Status;\r
220 }\r
221 \r
222 /**\r
223   Start this driver on ControllerHandle. This service is called by the\r
224   EFI boot service ConnectController(). In order to make\r
225   drivers as small as possible, there are a few calling restrictions for\r
226   this service. ConnectController() must follow these\r
227   calling restrictions. If any other agent wishes to call Start() it\r
228   must also follow these calling restrictions.\r
229 \r
230   @param  This                 Protocol instance pointer.\r
231   @param  ControllerHandle     Handle of device to bind driver to.\r
232   @param  RemainingDevicePath  Optional parameter use to pick a specific child\r
233                                device to start.\r
234 \r
235   @retval EFI_SUCCESS          This driver is added to ControllerHandle\r
236   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle\r
237   @retval other                This driver does not support this device\r
238 \r
239 **/\r
240 EFI_STATUS\r
241 EFIAPI\r
242 SimpleNetworkDriverStart (\r
243   IN EFI_DRIVER_BINDING_PROTOCOL    *This,\r
244   IN EFI_HANDLE                     Controller,\r
245   IN EFI_DEVICE_PATH_PROTOCOL       *RemainingDevicePath\r
246   )\r
247 {\r
248   EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;\r
249   EFI_DEVICE_PATH_PROTOCOL                  *NiiDevicePath;\r
250   EFI_STATUS                                Status;\r
251   PXE_UNDI                                  *Pxe;\r
252   SNP_DRIVER                                *Snp;\r
253   VOID                                      *Address;\r
254   EFI_HANDLE                                Handle;\r
255   PXE_PCI_CONFIG_INFO                       ConfigInfo;\r
256   PCI_TYPE00                                *ConfigHeader;\r
257   UINT32                                    *TempBar;\r
258   UINT8                                     BarIndex;\r
259   PXE_STATFLAGS                             InitStatFlags;\r
260 \r
261   DEBUG ((EFI_D_INFO, "\nSnpNotifyNetworkInterfaceIdentifier()  "));\r
262 \r
263   Status = gBS->OpenProtocol (\r
264                   Controller,\r
265                   &gEfiDevicePathProtocolGuid,\r
266                   (VOID **) &NiiDevicePath,\r
267                   This->DriverBindingHandle,\r
268                   Controller,\r
269                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
270                   );\r
271 \r
272   if (EFI_ERROR (Status)) {\r
273     return Status;\r
274   }\r
275 \r
276   Status = gBS->LocateDevicePath (\r
277                   &gEfiPciIoProtocolGuid,\r
278                   &NiiDevicePath,\r
279                   &Handle\r
280                   );\r
281 \r
282   if (EFI_ERROR (Status)) {\r
283     return Status;\r
284   }\r
285 \r
286   Status = gBS->OpenProtocol (\r
287                   Handle,\r
288                   &gEfiPciIoProtocolGuid,\r
289                   (VOID **) &mPciIo,\r
290                   This->DriverBindingHandle,\r
291                   Controller,\r
292                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
293                   );\r
294   if (EFI_ERROR (Status)) {\r
295     return Status;\r
296   }\r
297   //\r
298   // Get the NII interface.\r
299   //\r
300   Status = gBS->OpenProtocol (\r
301                   Controller,\r
302                   &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
303                   (VOID **) &Nii,\r
304                   This->DriverBindingHandle,\r
305                   Controller,\r
306                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
307                   );\r
308   if (EFI_ERROR (Status)) {\r
309     gBS->CloseProtocol (\r
310           Controller,\r
311           &gEfiDevicePathProtocolGuid,\r
312           This->DriverBindingHandle,\r
313           Controller\r
314           );\r
315     return Status;\r
316   }\r
317 \r
318   DEBUG ((EFI_D_INFO, "Start(): UNDI3.1 found\n"));\r
319 \r
320   Pxe = (PXE_UNDI *) (UINTN) (Nii->ID);\r
321 \r
322   if (Calc8BitCksum (Pxe, Pxe->hw.Len) != 0) {\r
323     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\n!PXE checksum is not correct.\n"));\r
324     goto NiiError;\r
325   }\r
326 \r
327   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) {\r
328     //\r
329     //  We can get any packets.\r
330     //\r
331   } else if ((Pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) {\r
332     //\r
333     //  We need to be able to get broadcast packets for DHCP.\r
334     //  If we do not have promiscuous support, we must at least have\r
335     //  broadcast support or we cannot do DHCP!\r
336     //\r
337   } else {\r
338     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\nUNDI does not have promiscuous or broadcast support."));\r
339     goto NiiError;\r
340   }\r
341   //\r
342   // OK, we like this UNDI, and we know snp is not already there on this handle\r
343   // Allocate and initialize a new simple network protocol structure.\r
344   //\r
345   Status = mPciIo->AllocateBuffer (\r
346                         mPciIo,\r
347                         AllocateAnyPages,\r
348                         EfiBootServicesData,\r
349                         SNP_MEM_PAGES (sizeof (SNP_DRIVER)),\r
350                         &Address,\r
351                         0\r
352                         );\r
353 \r
354   if (Status != EFI_SUCCESS) {\r
355     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\nCould not allocate SNP_DRIVER structure.\n"));\r
356     goto NiiError;\r
357   }\r
358 \r
359   Snp = (SNP_DRIVER *) (UINTN) Address;\r
360 \r
361   ZeroMem (Snp, sizeof (SNP_DRIVER));\r
362 \r
363   Snp->PciIo      = mPciIo;\r
364   Snp->Signature  = SNP_DRIVER_SIGNATURE;\r
365 \r
366   EfiInitializeLock (&Snp->Lock, TPL_NOTIFY);\r
367 \r
368   Snp->Snp.Revision       = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION;\r
369   Snp->Snp.Start          = SnpUndi32Start;\r
370   Snp->Snp.Stop           = SnpUndi32Stop;\r
371   Snp->Snp.Initialize     = SnpUndi32Initialize;\r
372   Snp->Snp.Reset          = SnpUndi32Reset;\r
373   Snp->Snp.Shutdown       = SnpUndi32Shutdown;\r
374   Snp->Snp.ReceiveFilters = SnpUndi32ReceiveFilters;\r
375   Snp->Snp.StationAddress = SnpUndi32StationAddress;\r
376   Snp->Snp.Statistics     = SnpUndi32Statistics;\r
377   Snp->Snp.MCastIpToMac   = SnpUndi32McastIpToMac;\r
378   Snp->Snp.NvData         = SnpUndi32NvData;\r
379   Snp->Snp.GetStatus      = SnpUndi32GetStatus;\r
380   Snp->Snp.Transmit       = SnpUndi32Transmit;\r
381   Snp->Snp.Receive        = SnpUndi32Receive;\r
382   Snp->Snp.WaitForPacket  = NULL;\r
383 \r
384   Snp->Snp.Mode           = &Snp->Mode;\r
385 \r
386   Snp->TxRxBufferSize     = 0;\r
387   Snp->TxRxBuffer         = NULL;\r
388 \r
389   Snp->IfNum              = Nii->IfNum;\r
390 \r
391   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_HW_UNDI) != 0) {\r
392     Snp->IsSwUndi             = FALSE;\r
393     Snp->IssueUndi32Command   = &IssueHwUndiCommand;\r
394   } else {\r
395     Snp->IsSwUndi = TRUE;\r
396 \r
397     if ((Pxe->sw.Implementation & PXE_ROMID_IMP_SW_VIRT_ADDR) != 0) {\r
398       Snp->IssueUndi32Command = (ISSUE_UNDI32_COMMAND) (UINTN) Pxe->sw.EntryPoint;\r
399     } else {\r
400       Snp->IssueUndi32Command = (ISSUE_UNDI32_COMMAND) (UINTN) ((UINT8) (UINTN) Pxe + Pxe->sw.EntryPoint);\r
401     }\r
402   }\r
403   //\r
404   // Allocate a global CPB and DB buffer for this UNDI interface.\r
405   // we do this because:\r
406   //\r
407   // -UNDI 3.0 wants all the addresses passed to it (even the cpb and db) to be\r
408   // within 2GB limit, create them here and map them so that when undi calls\r
409   // v2p callback to check if the physical address is < 2gb, we will pass.\r
410   //\r
411   // -This is not a requirement for 3.1 or later UNDIs but the code looks\r
412   // simpler if we use the same cpb, db variables for both old and new undi\r
413   // interfaces from all the SNP interface calls (we don't map the buffers\r
414   // for the newer undi interfaces though)\r
415   // .\r
416   // -it is OK to allocate one global set of CPB, DB pair for each UNDI\r
417   // interface as EFI does not multi-task and so SNP will not be re-entered!\r
418   //\r
419   Status = mPciIo->AllocateBuffer (\r
420                         mPciIo,\r
421                         AllocateAnyPages,\r
422                         EfiBootServicesData,\r
423                         SNP_MEM_PAGES (4096),\r
424                         &Address,\r
425                         0\r
426                         );\r
427 \r
428   if (Status != EFI_SUCCESS) {\r
429     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\nCould not allocate CPB and DB structures.\n"));\r
430     goto Error_DeleteSNP;\r
431   }\r
432 \r
433   Snp->Cpb  = (VOID *) (UINTN) Address;\r
434   Snp->Db   = (VOID *) ((UINTN) Address + 2048);\r
435 \r
436   //\r
437   // PxeStart call is going to give the callback functions to UNDI, these callback\r
438   // functions use the BarIndex values from the snp structure, so these must be initialized\r
439   // with default values before doing a PxeStart. The correct values can be obtained after\r
440   // getting the config information from UNDI\r
441   //\r
442   Snp->MemoryBarIndex = 0;\r
443   Snp->IoBarIndex     = 1;\r
444 \r
445   //\r
446   // we need the undi init information many times in this snp code, just get it\r
447   // once here and store it in the snp driver structure. to get Init Info\r
448   // from UNDI we have to start undi first.\r
449   //\r
450   Status = PxeStart (Snp);\r
451 \r
452   if (Status != EFI_SUCCESS) {\r
453     goto Error_DeleteSNP;\r
454   }\r
455 \r
456   Snp->Cdb.OpCode     = PXE_OPCODE_GET_INIT_INFO;\r
457   Snp->Cdb.OpFlags    = PXE_OPFLAGS_NOT_USED;\r
458 \r
459   Snp->Cdb.CPBsize    = PXE_CPBSIZE_NOT_USED;\r
460   Snp->Cdb.CPBaddr    = PXE_DBADDR_NOT_USED;\r
461 \r
462   Snp->Cdb.DBsize     = sizeof Snp->InitInfo;\r
463   Snp->Cdb.DBaddr     = (UINT64)(UINTN) &Snp->InitInfo;\r
464 \r
465   Snp->Cdb.StatCode   = PXE_STATCODE_INITIALIZE;\r
466   Snp->Cdb.StatFlags  = PXE_STATFLAGS_INITIALIZE;\r
467 \r
468   Snp->Cdb.IFnum      = Snp->IfNum;\r
469   Snp->Cdb.Control    = PXE_CONTROL_LAST_CDB_IN_LIST;\r
470 \r
471   DEBUG ((EFI_D_INFO | EFI_D_NET, "\nSnp->undi.get_init_info()  "));\r
472 \r
473   (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);\r
474 \r
475   //\r
476   // Save the INIT Stat Code...\r
477   //\r
478   InitStatFlags = Snp->Cdb.StatFlags;\r
479 \r
480   if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {\r
481     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\nSnp->undi.init_info()  %xh:%xh\n", Snp->Cdb.StatFlags, Snp->Cdb.StatCode));\r
482     PxeStop (Snp);\r
483     goto Error_DeleteSNP;\r
484   }\r
485 \r
486   Snp->Cdb.OpCode     = PXE_OPCODE_GET_CONFIG_INFO;\r
487   Snp->Cdb.OpFlags    = PXE_OPFLAGS_NOT_USED;\r
488 \r
489   Snp->Cdb.CPBsize    = PXE_CPBSIZE_NOT_USED;\r
490   Snp->Cdb.CPBaddr    = PXE_DBADDR_NOT_USED;\r
491 \r
492   Snp->Cdb.DBsize     = sizeof ConfigInfo;\r
493   Snp->Cdb.DBaddr     = (UINT64)(UINTN) &ConfigInfo;\r
494 \r
495   Snp->Cdb.StatCode   = PXE_STATCODE_INITIALIZE;\r
496   Snp->Cdb.StatFlags  = PXE_STATFLAGS_INITIALIZE;\r
497 \r
498   Snp->Cdb.IFnum      = Snp->IfNum;\r
499   Snp->Cdb.Control    = PXE_CONTROL_LAST_CDB_IN_LIST;\r
500 \r
501   DEBUG ((EFI_D_INFO | EFI_D_NET, "\nSnp->undi.get_config_info()  "));\r
502 \r
503   (*Snp->IssueUndi32Command) ((UINT64)(UINTN) &Snp->Cdb);\r
504 \r
505   if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {\r
506     DEBUG ((EFI_D_ERROR | EFI_D_NET, "\nSnp->undi.config_info()  %xh:%xh\n", Snp->Cdb.StatFlags, Snp->Cdb.StatCode));\r
507     PxeStop (Snp);\r
508     goto Error_DeleteSNP;\r
509   }\r
510   //\r
511   // Find the correct BAR to do IO.\r
512   //\r
513   //\r
514   // Enumerate through the PCI BARs for the device to determine which one is\r
515   // the IO BAR.  Save the index of the BAR into the adapter info structure.\r
516   // for  regular 32bit BARs, 0 is memory mapped, 1 is io mapped\r
517   //\r
518   ConfigHeader  = (PCI_TYPE00 *) &ConfigInfo.Config.Byte[0];\r
519   TempBar       = (UINT32 *) &ConfigHeader->Device.Bar[0];\r
520   for (BarIndex = 0; BarIndex <= 5; BarIndex++) {\r
521     if ((*TempBar & PCI_BAR_MEM_MASK) == PCI_BAR_MEM_64BIT) {\r
522       //\r
523       // This is a 64-bit memory bar, skip this and the\r
524       // next bar as well.\r
525       //\r
526       TempBar++;\r
527     }\r
528 \r
529     if ((*TempBar & PCI_BAR_IO_MASK) == PCI_BAR_IO_MODE) {\r
530       Snp->IoBarIndex = BarIndex;\r
531       break;\r
532     }\r
533 \r
534     TempBar++;\r
535   }\r
536 \r
537   //\r
538   //  Initialize simple network protocol mode structure\r
539   //\r
540   Snp->Mode.State               = EfiSimpleNetworkStopped;\r
541   Snp->Mode.HwAddressSize       = Snp->InitInfo.HWaddrLen;\r
542   Snp->Mode.MediaHeaderSize     = Snp->InitInfo.MediaHeaderLen;\r
543   Snp->Mode.MaxPacketSize       = Snp->InitInfo.FrameDataLen;\r
544   Snp->Mode.NvRamAccessSize     = Snp->InitInfo.NvWidth;\r
545   Snp->Mode.NvRamSize           = Snp->InitInfo.NvCount * Snp->Mode.NvRamAccessSize;\r
546   Snp->Mode.IfType              = Snp->InitInfo.IFtype;\r
547   Snp->Mode.MaxMCastFilterCount = Snp->InitInfo.MCastFilterCnt;\r
548   Snp->Mode.MCastFilterCount    = 0;\r
549 \r
550   switch (InitStatFlags & PXE_STATFLAGS_CABLE_DETECT_MASK) {\r
551   case PXE_STATFLAGS_CABLE_DETECT_SUPPORTED:\r
552     Snp->Mode.MediaPresentSupported = TRUE;\r
553     break;\r
554 \r
555   case PXE_STATFLAGS_CABLE_DETECT_NOT_SUPPORTED:\r
556   default:\r
557     Snp->Mode.MediaPresentSupported = FALSE;\r
558   }\r
559 \r
560   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE) != 0) {\r
561     Snp->Mode.MacAddressChangeable = TRUE;\r
562   } else {\r
563     Snp->Mode.MacAddressChangeable = FALSE;\r
564   }\r
565 \r
566   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_MULTI_FRAME_SUPPORTED) != 0) {\r
567     Snp->Mode.MultipleTxSupported = TRUE;\r
568   } else {\r
569     Snp->Mode.MultipleTxSupported = FALSE;\r
570   }\r
571 \r
572   Snp->Mode.ReceiveFilterMask = EFI_SIMPLE_NETWORK_RECEIVE_UNICAST;\r
573 \r
574   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) != 0) {\r
575     Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;\r
576 \r
577   }\r
578 \r
579   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED) != 0) {\r
580     Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS;\r
581 \r
582   }\r
583 \r
584   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED) != 0) {\r
585     Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST;\r
586 \r
587   }\r
588 \r
589   if ((Pxe->hw.Implementation & PXE_ROMID_IMP_FILTERED_MULTICAST_RX_SUPPORTED) != 0) {\r
590     Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST;\r
591 \r
592   }\r
593 \r
594   if (Pxe->hw.Implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED) {\r
595     Snp->Mode.ReceiveFilterMask |= EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST;\r
596 \r
597   }\r
598 \r
599   Snp->Mode.ReceiveFilterSetting = 0;\r
600 \r
601   //\r
602   //  need to get the station address to save in the mode structure. we need to\r
603   // initialize the UNDI first for this.\r
604   //\r
605   Snp->TxRxBufferSize = Snp->InitInfo.MemoryRequired;\r
606   Status              = PxeInit (Snp, PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE);\r
607 \r
608   if (EFI_ERROR (Status)) {\r
609     PxeStop (Snp);\r
610     goto Error_DeleteSNP;\r
611   }\r
612 \r
613   Status = PxeGetStnAddr (Snp);\r
614 \r
615   if (Status != EFI_SUCCESS) {\r
616     DEBUG ((EFI_D_ERROR, "\nSnp->undi.get_station_addr() failed.\n"));\r
617     PxeShutdown (Snp);\r
618     PxeStop (Snp);\r
619     goto Error_DeleteSNP;\r
620   }\r
621 \r
622   Snp->Mode.MediaPresent = FALSE;\r
623 \r
624   //\r
625   // We should not leave UNDI started and initialized here. this DriverStart()\r
626   // routine must only find and attach the SNP interface to UNDI layer that it\r
627   // finds on the given handle!\r
628   // The UNDI layer will be started when upper layers call Snp->start.\r
629   // How ever, this DriverStart() must fill up the snp mode structure which\r
630   // contains the MAC address of the NIC. For this reason we started and\r
631   // initialized UNDI here, now we are done, do a shutdown and stop of the\r
632   // UNDI interface!\r
633   //\r
634   PxeShutdown (Snp);\r
635   PxeStop (Snp);\r
636 \r
637   //\r
638   //  add SNP to the undi handle\r
639   //\r
640   Status = gBS->InstallProtocolInterface (\r
641                   &Controller,\r
642                   &gEfiSimpleNetworkProtocolGuid,\r
643                   EFI_NATIVE_INTERFACE,\r
644                   &(Snp->Snp)\r
645                   );\r
646 \r
647   if (!EFI_ERROR (Status)) {\r
648     return Status;\r
649   }\r
650 \r
651   Status = mPciIo->FreeBuffer (\r
652                         mPciIo,\r
653                         SNP_MEM_PAGES (4096),\r
654                         Snp->Cpb\r
655                         );\r
656 \r
657 Error_DeleteSNP:\r
658 \r
659   mPciIo->FreeBuffer (\r
660                 mPciIo,\r
661                 SNP_MEM_PAGES (sizeof (SNP_DRIVER)),\r
662                 Snp\r
663                 );\r
664 NiiError:\r
665   gBS->CloseProtocol (\r
666         Controller,\r
667         &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
668         This->DriverBindingHandle,\r
669         Controller\r
670         );\r
671 \r
672   gBS->CloseProtocol (\r
673         Controller,\r
674         &gEfiDevicePathProtocolGuid,\r
675         This->DriverBindingHandle,\r
676         Controller\r
677         );\r
678 \r
679   return Status;\r
680 }\r
681 \r
682 /**\r
683   Stop this driver on ControllerHandle. This service is called by the\r
684   EFI boot service DisconnectController(). In order to\r
685   make drivers as small as possible, there are a few calling\r
686   restrictions for this service. DisconnectController()\r
687   must follow these calling restrictions. If any other agent wishes\r
688   to call Stop() it must also follow these calling restrictions.\r
689   \r
690   @param  This              Protocol instance pointer.\r
691   @param  ControllerHandle  Handle of device to stop driver on\r
692   @param  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of\r
693                             children is zero stop the entire bus driver.\r
694   @param  ChildHandleBuffer List of Child Handles to Stop.\r
695 \r
696   @retval EFI_SUCCESS       This driver is removed ControllerHandle\r
697   @retval other             This driver was not removed from this device\r
698 \r
699 **/\r
700 EFI_STATUS\r
701 EFIAPI\r
702 SimpleNetworkDriverStop (\r
703   IN  EFI_DRIVER_BINDING_PROTOCOL    *This,\r
704   IN  EFI_HANDLE                     Controller,\r
705   IN  UINTN                          NumberOfChildren,\r
706   IN  EFI_HANDLE                     *ChildHandleBuffer\r
707   )\r
708 {\r
709   EFI_STATUS                  Status;\r
710   EFI_SIMPLE_NETWORK_PROTOCOL *SnpProtocol;\r
711   SNP_DRIVER                  *Snp;\r
712 \r
713   //\r
714   // Get our context back.\r
715   //\r
716   Status = gBS->OpenProtocol (\r
717                   Controller,\r
718                   &gEfiSimpleNetworkProtocolGuid,\r
719                   (VOID **) &SnpProtocol,\r
720                   This->DriverBindingHandle,\r
721                   Controller,\r
722                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
723                   );\r
724 \r
725   if (EFI_ERROR (Status)) {\r
726     return EFI_UNSUPPORTED;\r
727   }\r
728 \r
729   Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (SnpProtocol);\r
730 \r
731   Status = gBS->UninstallProtocolInterface (\r
732                   Controller,\r
733                   &gEfiSimpleNetworkProtocolGuid,\r
734                   &Snp->Snp\r
735                   );\r
736 \r
737   if (EFI_ERROR (Status)) {\r
738     return Status;\r
739   }\r
740 \r
741   Status = gBS->CloseProtocol (\r
742                   Controller,\r
743                   &gEfiNetworkInterfaceIdentifierProtocolGuid_31,\r
744                   This->DriverBindingHandle,\r
745                   Controller\r
746                   );\r
747 \r
748   Status = gBS->CloseProtocol (\r
749                   Controller,\r
750                   &gEfiDevicePathProtocolGuid,\r
751                   This->DriverBindingHandle,\r
752                   Controller\r
753                   );\r
754 \r
755   PxeShutdown (Snp);\r
756   PxeStop (Snp);\r
757 \r
758   mPciIo->FreeBuffer (\r
759                 mPciIo,\r
760                 SNP_MEM_PAGES (4096),\r
761                 Snp->Cpb\r
762                 );\r
763 \r
764   mPciIo->FreeBuffer (\r
765                 mPciIo,\r
766                 SNP_MEM_PAGES (sizeof (SNP_DRIVER)),\r
767                 Snp\r
768                 );\r
769 \r
770   return Status;\r
771 }\r
772 \r
773 //\r
774 // Simple Network Protocol Driver Global Variables\r
775 //\r
776 EFI_DRIVER_BINDING_PROTOCOL mSimpleNetworkDriverBinding = {\r
777   SimpleNetworkDriverSupported,\r
778   SimpleNetworkDriverStart,\r
779   SimpleNetworkDriverStop,\r
780   0xa,\r
781   NULL,\r
782   NULL\r
783 };\r
784 \r
785 \r
786 /**\r
787   This routine maps the given CPU address to a Device address. It creates a\r
788   an entry in the map list with the virtual and physical addresses and the\r
789   un map cookie.\r
790 \r
791   @param  V2p                  pointer to return a map list node pointer.\r
792   @param  Type                 the direction in which the data flows from the given\r
793                                virtual address device->cpu or cpu->device or both\r
794                                ways.\r
795   @param  VirtualAddress       virtual address (or CPU address) to be mapped.\r
796   @param  BufferSize           size of the buffer to be mapped.\r
797 \r
798   @retval EFI_SUCEESS           routine has completed the mapping.\r
799   @retval EFI_INVALID_PARAMETER invalid parameter.\r
800   @retval EFI_OUT_OF_RESOURCES  out of resource.\r
801   @retval other                 error as indicated.\r
802 \r
803 **/\r
804 EFI_STATUS\r
805 AddV2P (\r
806   IN OUT V2P                    **V2p,\r
807   EFI_PCI_IO_PROTOCOL_OPERATION Type,\r
808   VOID                          *VirtualAddress,\r
809   UINTN                         BufferSize\r
810   )\r
811 {\r
812   EFI_STATUS  Status;\r
813 \r
814   if ((V2p == NULL) || (VirtualAddress == NULL) || (BufferSize == 0)) {\r
815     return EFI_INVALID_PARAMETER;\r
816   }\r
817 \r
818   *V2p = AllocatePool (sizeof (V2P));\r
819   if (*V2p != NULL) {\r
820     return EFI_OUT_OF_RESOURCES;\r
821   }\r
822 \r
823   Status = mPciIo->Map (\r
824                      mPciIo,\r
825                      Type,\r
826                      VirtualAddress,\r
827                      &BufferSize,\r
828                      &(*V2p)->PhysicalAddress,\r
829                      &(*V2p)->Unmap\r
830                      );\r
831   if (Status != EFI_SUCCESS) {\r
832     FreePool (*V2p);\r
833     return Status;\r
834   }\r
835   (*V2p)->VirtualAddress = VirtualAddress;\r
836   (*V2p)->BufferSize     = BufferSize;\r
837   (*V2p)->Next           = mV2p;\r
838   mV2p                   = *V2p;\r
839 \r
840   return EFI_SUCCESS;\r
841 }\r
842 \r
843 \r
844 /**\r
845   This routine searches the linked list of mapped address nodes (for undi3.0\r
846   interface) to find the node that corresponds to the given virtual address and\r
847   returns a pointer to that node.\r
848 \r
849   @param  V2p                  pointer to return a map list node pointer.\r
850   @param  VirtualAddr          virtual address (or CPU address) to be searched in\r
851                                the map list\r
852 \r
853   @retval EFI_SUCEESS          A match was found.\r
854   @retval Other                A match cannot be found.\r
855 \r
856 **/\r
857 EFI_STATUS\r
858 FindV2p (\r
859   V2P          **V2p,\r
860   VOID         *VirtualAddr\r
861   )\r
862 {\r
863   V2P    *Ptr;\r
864 \r
865   if (V2p == NULL || VirtualAddr == NULL) {\r
866     return EFI_INVALID_PARAMETER;\r
867   }\r
868 \r
869   for (Ptr = mV2p; Ptr != NULL; Ptr = Ptr->Next) {\r
870     if (Ptr->VirtualAddress == VirtualAddr) {\r
871       *V2p = Ptr;\r
872       return EFI_SUCCESS;\r
873     }\r
874   }\r
875 \r
876   return EFI_NOT_FOUND;\r
877 }\r
878 \r
879 \r
880 /**\r
881   Unmap the given virtual address and free the memory allocated for the map list\r
882   node corresponding to that address.\r
883 \r
884   @param  VirtualAddress       virtual address (or CPU address) to be unmapped.\r
885 \r
886   @retval EFI_SUCEESS          Successfully unmapped.\r
887   @retval Other                Other errors as indicated.\r
888 \r
889 **/\r
890 EFI_STATUS\r
891 DelV2p (\r
892   VOID *VirtualAddress\r
893   )\r
894 {\r
895   V2P           *Current;\r
896   V2P           *Next;\r
897   EFI_STATUS    Status;\r
898 \r
899   if (VirtualAddress == NULL) {\r
900     return EFI_INVALID_PARAMETER;\r
901   }\r
902 \r
903   if (mV2p == NULL) {\r
904     return EFI_NOT_FOUND;\r
905   }\r
906   //\r
907   // Is our node at the head of the list??\r
908   //\r
909   if ((Current = mV2p)->VirtualAddress == VirtualAddress) {\r
910     mV2p    = mV2p->Next;\r
911 \r
912     Status  = mPciIo->Unmap (mPciIo, Current->Unmap);\r
913 \r
914     FreePool (Current);\r
915 \r
916     if (EFI_ERROR (Status)) {\r
917       DEBUG ((EFI_D_ERROR, "Unmap failed with status = %r\n", Status));\r
918     }\r
919     return Status;\r
920   }\r
921 \r
922   for (; Current->Next != NULL; Current = Next) {\r
923     if ((Next = Current->Next)->VirtualAddress == VirtualAddress) {\r
924       Current->Next = Next->Next;\r
925       Status  = mPciIo->Unmap (mPciIo, Next->Unmap);\r
926       FreePool (Next);\r
927 \r
928       if (EFI_ERROR (Status)) {\r
929         DEBUG ((EFI_D_ERROR, "Unmap failed with status = %r\n", Status));\r
930       }\r
931       return Status;\r
932     }\r
933   }\r
934 \r
935   return EFI_NOT_FOUND;\r
936 }\r
937 \r
938 /**\r
939   The SNP driver entry point.\r
940 \r
941   @param ImageHandle       The driver image handle.\r
942   @param SystemTable       The system table.\r
943 \r
944   @retval EFI_SUCEESS      Initialization routine has found UNDI hardware,\r
945                            loaded it's ROM, and installed a notify event for\r
946                            the Network Indentifier Interface Protocol\r
947                            successfully.\r
948   @retval Other            Return value from HandleProtocol for\r
949                            DeviceIoProtocol or LoadedImageProtocol\r
950 \r
951 **/\r
952 EFI_STATUS\r
953 EFIAPI\r
954 InitializeSnpNiiDriver (\r
955   IN EFI_HANDLE       ImageHandle,\r
956   IN EFI_SYSTEM_TABLE *SystemTable\r
957   )\r
958 {\r
959   return EfiLibInstallDriverBindingComponentName2 (\r
960            ImageHandle,\r
961            SystemTable,\r
962            &mSimpleNetworkDriverBinding,\r
963            NULL,\r
964            &gSimpleNetworkComponentName,\r
965            &gSimpleNetworkComponentName2\r
966            );\r
967 }\r