1 /** @file
2   ISA Bus UEFI driver.
3 
4   Discovers all the ISA Controllers and their resources by using the ISA ACPI
5   Protocol, produces an instance of the ISA I/O Protocol for every ISA
6   Controller found. This driver is designed to manage a PCI-to-ISA bridge Device
7   such as LPC bridge.
8 
9 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
10 SPDX-License-Identifier: BSD-2-Clause-Patent
11 
12 **/
13 
14 #include "InternalIsaBus.h"
15 
16 //
17 // ISA Bus Driver Global Variables
18 //
19 EFI_DRIVER_BINDING_PROTOCOL gIsaBusControllerDriver = {
20   IsaBusControllerDriverSupported,
21   IsaBusControllerDriverStart,
22   IsaBusControllerDriverStop,
23   0xa,
24   NULL,
25   NULL
26 };
27 
28 /**
29   The main entry point for the ISA Bus driver.
30 
31   @param[in] ImageHandle        The firmware allocated handle for the EFI image.
32   @param[in] SystemTable        A pointer to the EFI System Table.
33 
34   @retval EFI_SUCCESS           The entry point is executed successfully.
35   @retval EFI_OUT_OF_RESOURCES  There was not enough memory in pool to install all the protocols.
36 **/
37 EFI_STATUS
38 EFIAPI
InitializeIsaBus(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)39 InitializeIsaBus(
40   IN EFI_HANDLE           ImageHandle,
41   IN EFI_SYSTEM_TABLE     *SystemTable
42   )
43 {
44   EFI_STATUS              Status;
45 
46   //
47   // Install driver model protocol(s).
48   //
49   Status = EfiLibInstallDriverBindingComponentName2 (
50              ImageHandle,
51              SystemTable,
52              &gIsaBusControllerDriver,
53              ImageHandle,
54              &gIsaBusComponentName,
55              &gIsaBusComponentName2
56              );
57   ASSERT_EFI_ERROR (Status);
58 
59   return Status;
60 }
61 
62 /**
63   Tests to see if a controller can be managed by the ISA Bus Driver. If a child device is provided,
64   it further tests to see if this driver supports creating a handle for the specified child device.
65 
66   Note that the ISA Bus driver always creates all of its child handles on the first call to Start().
67   How the Start() function of a driver is implemented can affect how the Supported() function is implemented.
68 
69   @param[in] This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
70   @param[in] Controller           The handle of the controller to test.
71   @param[in] RemainingDevicePath  A pointer to the remaining portion of a device path.
72 
73   @retval EFI_SUCCESS             The device is supported by this driver.
74   @retval EFI_ALREADY_STARTED     The device is already being managed by this driver.
75   @retval EFI_ACCESS_DENIED       The device is already being managed by a different driver
76                                   or an application that requires exclusive access.
77   @retval EFI_UNSUPPORTED         The device is is not supported by this driver.
78 
79 **/
80 EFI_STATUS
81 EFIAPI
IsaBusControllerDriverSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)82 IsaBusControllerDriverSupported (
83   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
84   IN EFI_HANDLE                   Controller,
85   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
86   )
87 {
88   EFI_STATUS                Status;
89   EFI_DEVICE_PATH_PROTOCOL  *ParentDevicePath;
90   EFI_ISA_ACPI_PROTOCOL     *IsaAcpi;
91 
92   //
93   // If RemainingDevicePath is not NULL, it should verify that the first device
94   // path node in RemainingDevicePath is an ACPI Device path node which is a
95   // legal Device Path Node for this bus driver's children.
96   //
97   if (RemainingDevicePath != NULL) {
98     if (RemainingDevicePath->Type != ACPI_DEVICE_PATH) {
99       return EFI_UNSUPPORTED;
100     } else if (RemainingDevicePath->SubType == ACPI_DP) {
101       if (DevicePathNodeLength (RemainingDevicePath) != sizeof (ACPI_HID_DEVICE_PATH)) {
102         return EFI_UNSUPPORTED;
103       }
104     } else if (RemainingDevicePath->SubType == ACPI_EXTENDED_DP) {
105       if (DevicePathNodeLength (RemainingDevicePath) != sizeof (ACPI_EXTENDED_HID_DEVICE_PATH)) {
106         return EFI_UNSUPPORTED;
107       }
108     } else {
109       return EFI_UNSUPPORTED;
110     }
111   }
112   //
113   // Try to open EFI DEVICE PATH protocol on the controller
114   //
115   Status = gBS->OpenProtocol (
116                   Controller,
117                   &gEfiDevicePathProtocolGuid,
118                   (VOID **) &ParentDevicePath,
119                   This->DriverBindingHandle,
120                   Controller,
121                   EFI_OPEN_PROTOCOL_BY_DRIVER
122                   );
123   //
124   // Although this driver creates all child handles at one time,
125   // but because all child handles may be not stopped at one time in EFI Driver Binding.Stop(),
126   // So it is allowed to create child handles again in successive calls to EFI Driver Binding.Start().
127   //
128   if (Status == EFI_ALREADY_STARTED) {
129     return EFI_SUCCESS;
130   }
131 
132   if (EFI_ERROR (Status)) {
133     return Status;
134   }
135 
136   gBS->CloseProtocol (
137          Controller,
138          &gEfiDevicePathProtocolGuid,
139          This->DriverBindingHandle,
140          Controller
141          );
142 
143   //
144   // Try to get Pci IO Protocol because it is assumed
145   // to have been opened by ISA ACPI driver
146   //
147   Status = gBS->OpenProtocol (
148                   Controller,
149                   &gEfiPciIoProtocolGuid,
150                   NULL,
151                   This->DriverBindingHandle,
152                   Controller,
153                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
154                   );
155   if (EFI_ERROR (Status)) {
156     return Status;
157   }
158 
159   //
160   // Try to open the Isa Acpi protocol on the controller
161   //
162   Status = gBS->OpenProtocol (
163                   Controller,
164                   &gEfiIsaAcpiProtocolGuid,
165                   (VOID **) &IsaAcpi,
166                   This->DriverBindingHandle,
167                   Controller,
168                   EFI_OPEN_PROTOCOL_BY_DRIVER
169                   );
170   if (EFI_ERROR (Status)) {
171     return Status;
172   }
173 
174   //
175   // Add more check to see if the child device is valid by calling IsaAcpi->DeviceEnumerate?
176   //
177 
178   gBS->CloseProtocol (
179          Controller,
180          &gEfiIsaAcpiProtocolGuid,
181          This->DriverBindingHandle,
182          Controller
183          );
184 
185   return Status;
186 }
187 
188 /**
189   Start this driver on ControllerHandle.
190 
191   Note that the ISA Bus driver always creates all of its child handles on the first call to Start().
192   The Start() function is designed to be invoked from the EFI boot service ConnectController().
193   As a result, much of the error checking on the parameters to Start() has been moved into this
194   common boot service. It is legal to call Start() from other locations, but the following calling
195   restrictions must be followed or the system behavior will not be deterministic.
196   1. ControllerHandle must be a valid EFI_HANDLE.
197   2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
198      EFI_DEVICE_PATH_PROTOCOL.
199   3. Prior to calling Start(), the Supported() function for the driver specified by This must
200      have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
201 
202   @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
203   @param[in]  ControllerHandle     The handle of the controller to start. This handle
204                                    must support a protocol interface that supplies
205                                    an I/O abstraction to the driver.
206   @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.
207                                    This parameter is ignored by device drivers, and is optional for bus drivers.
208 
209   @retval EFI_SUCCESS              The device was started.
210   @retval EFI_DEVICE_ERROR         The device could not be started due to a device error.
211                                    Currently not implemented.
212   @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a lack of resources.
213   @retval Others                   The driver failded to start the device.
214 **/
215 EFI_STATUS
216 EFIAPI
IsaBusControllerDriverStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)217 IsaBusControllerDriverStart (
218   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
219   IN EFI_HANDLE                   Controller,
220   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
221   )
222 {
223   EFI_STATUS                            Status;
224   EFI_PCI_IO_PROTOCOL                   *PciIo;
225   EFI_DEVICE_PATH_PROTOCOL              *ParentDevicePath;
226   EFI_ISA_ACPI_PROTOCOL                 *IsaAcpi;
227   EFI_ISA_ACPI_DEVICE_ID                *IsaDevice;
228   EFI_ISA_ACPI_RESOURCE_LIST            *ResourceList;
229   EFI_GENERIC_MEMORY_TEST_PROTOCOL      *GenMemoryTest;
230 
231   //
232   // Local variables declaration for StatusCode reporting
233   //
234   EFI_DEVICE_PATH_PROTOCOL              *DevicePathData;
235 
236   DevicePathData = NULL;
237 
238   //
239   // Get Pci IO Protocol
240   //
241   Status = gBS->OpenProtocol (
242                   Controller,
243                   &gEfiPciIoProtocolGuid,
244                   (VOID **) &PciIo,
245                   This->DriverBindingHandle,
246                   Controller,
247                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
248                   );
249   if (EFI_ERROR (Status)) {
250     return Status;
251   }
252 
253   //
254   // Open Device Path Protocol
255   //
256   Status = gBS->OpenProtocol (
257                   Controller,
258                   &gEfiDevicePathProtocolGuid,
259                   (VOID **) &ParentDevicePath,
260                   This->DriverBindingHandle,
261                   Controller,
262                   EFI_OPEN_PROTOCOL_BY_DRIVER
263                   );
264   if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
265     return Status;
266   }
267 
268   //
269   // Open ISA Acpi Protocol
270   //
271   Status = gBS->OpenProtocol (
272                   Controller,
273                   &gEfiIsaAcpiProtocolGuid,
274                   (VOID **) &IsaAcpi,
275                   This->DriverBindingHandle,
276                   Controller,
277                   EFI_OPEN_PROTOCOL_BY_DRIVER
278                   );
279   if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
280     //
281     // Close opened protocol
282     //
283     gBS->CloseProtocol (
284            Controller,
285            &gEfiDevicePathProtocolGuid,
286            This->DriverBindingHandle,
287            Controller
288            );
289     return Status;
290   }
291   //
292   // The IsaBus driver will use memory below 16M, which is not tested yet,
293   // so call CompatibleRangeTest to test them. Since memory below 1M should
294   // be reserved to CSM, and 15M~16M might be reserved for Isa hole, test 1M
295   // ~15M here
296   //
297   Status = gBS->LocateProtocol (
298                   &gEfiGenericMemTestProtocolGuid,
299                   NULL,
300                   (VOID **) &GenMemoryTest
301                   );
302 
303   if (!EFI_ERROR (Status)) {
304     Status = GenMemoryTest->CompatibleRangeTest (
305                               GenMemoryTest,
306                               0x100000,
307                               0xE00000
308                               );
309   }
310   //
311   // Report Status Code here since we will initialize the host controller
312   //
313   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
314     EFI_PROGRESS_CODE,
315     (EFI_IO_BUS_LPC | EFI_IOB_PC_INIT),
316     ParentDevicePath
317     );
318 
319   //
320   // first init ISA interface
321   //
322   IsaAcpi->InterfaceInit (IsaAcpi);
323 
324   //
325   // Report Status Code here since we will enable the host controller
326   //
327   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
328     EFI_PROGRESS_CODE,
329     (EFI_IO_BUS_LPC | EFI_IOB_PC_ENABLE),
330     ParentDevicePath
331     );
332 
333   //
334   // Create each ISA device handle in this ISA bus
335   //
336   IsaDevice = NULL;
337   do {
338     Status = IsaAcpi->DeviceEnumerate (IsaAcpi, &IsaDevice);
339     if (EFI_ERROR (Status)) {
340       break;
341     }
342     //
343     // Get current resource of this ISA device
344     //
345     ResourceList  = NULL;
346     Status        = IsaAcpi->GetCurResource (IsaAcpi, IsaDevice, &ResourceList);
347     if (EFI_ERROR (Status)) {
348       continue;
349     }
350 
351     //
352     // Create handle for this ISA device
353     //
354     // If any child device handle was created in previous call to Start() and not stopped
355     // in previous call to Stop(), it will not be created again because the
356     // InstallMultipleProtocolInterfaces() boot service will reject same device path.
357     //
358     Status = IsaCreateDevice (
359                This,
360                Controller,
361                PciIo,
362                ParentDevicePath,
363                ResourceList,
364                &DevicePathData
365                );
366 
367     if (EFI_ERROR (Status)) {
368       continue;
369     }
370     //
371     // Initialize ISA device
372     //
373     IsaAcpi->InitDevice (IsaAcpi, IsaDevice);
374 
375     //
376     // Set resources for this ISA device
377     //
378     Status = IsaAcpi->SetResource (IsaAcpi, IsaDevice, ResourceList);
379 
380     //
381     // Report Status Code here when failed to resource conflicts
382     //
383     if (EFI_ERROR (Status) && (Status != EFI_UNSUPPORTED)) {
384       //
385       // It's hard to tell which resource conflicts
386       //
387       REPORT_STATUS_CODE_WITH_DEVICE_PATH (
388          EFI_ERROR_CODE,
389          (EFI_IO_BUS_LPC | EFI_IOB_EC_RESOURCE_CONFLICT),
390          DevicePathData
391          );
392 
393     }
394     //
395     // Set power for this ISA device
396     //
397     IsaAcpi->SetPower (IsaAcpi, IsaDevice, TRUE);
398 
399     //
400     // Enable this ISA device
401     //
402     IsaAcpi->EnableDevice (IsaAcpi, IsaDevice, TRUE);
403 
404   } while (TRUE);
405 
406   return EFI_SUCCESS;
407 }
408 
409 /**
410   Stop this driver on ControllerHandle.
411 
412   The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
413   As a result, much of the error checking on the parameters to Stop() has been moved
414   into this common boot service. It is legal to call Stop() from other locations,
415   but the following calling restrictions must be followed or the system behavior will not be deterministic.
416   1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
417      same driver's Start() function.
418   2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
419      EFI_HANDLE. In addition, all of these handles must have been created in this driver's
420      Start() function, and the Start() function must have called OpenProtocol() on
421      ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
422 
423   @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
424   @param[in]  ControllerHandle  A handle to the device being stopped. The handle must
425                                 support a bus specific I/O protocol for the driver
426                                 to use to stop the device.
427   @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.
428   @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL
429                                 if NumberOfChildren is 0.
430 
431   @retval EFI_SUCCESS           The device was stopped.
432   @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.
433 **/
434 EFI_STATUS
435 EFIAPI
IsaBusControllerDriverStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer OPTIONAL)436 IsaBusControllerDriverStop (
437   IN  EFI_DRIVER_BINDING_PROTOCOL  * This,
438   IN  EFI_HANDLE                   Controller,
439   IN  UINTN                        NumberOfChildren,
440   IN  EFI_HANDLE                   * ChildHandleBuffer OPTIONAL
441   )
442 {
443   EFI_STATUS                          Status;
444   UINTN                               Index;
445   BOOLEAN                             AllChildrenStopped;
446   ISA_IO_DEVICE                       *IsaIoDevice;
447   EFI_ISA_IO_PROTOCOL                 *IsaIo;
448   EFI_PCI_IO_PROTOCOL                 *PciIo;
449 
450   if (NumberOfChildren == 0) {
451     //
452     // Close the bus driver
453     //
454     Status = gBS->CloseProtocol (
455                     Controller,
456                     &gEfiDevicePathProtocolGuid,
457                     This->DriverBindingHandle,
458                     Controller
459                     );
460     if (EFI_ERROR (Status)) {
461       return Status;
462     }
463 
464     Status = gBS->CloseProtocol (
465                     Controller,
466                     &gEfiIsaAcpiProtocolGuid,
467                     This->DriverBindingHandle,
468                     Controller
469                     );
470     if (EFI_ERROR (Status)) {
471       return Status;
472     }
473 
474     return EFI_SUCCESS;
475   }
476   //
477   // Complete all outstanding transactions to Controller.
478   // Don't allow any new transaction to Controller to be started.
479   //
480   //
481   // Stop all the children
482   // Find all the ISA devices that were discovered on this PCI to ISA Bridge
483   // with the Start() function.
484   //
485   AllChildrenStopped = TRUE;
486 
487   for (Index = 0; Index < NumberOfChildren; Index++) {
488 
489     Status = gBS->OpenProtocol (
490                     ChildHandleBuffer[Index],
491                     &gEfiIsaIoProtocolGuid,
492                     (VOID **) &IsaIo,
493                     This->DriverBindingHandle,
494                     Controller,
495                     EFI_OPEN_PROTOCOL_GET_PROTOCOL
496                     );
497     if (!EFI_ERROR (Status)) {
498 
499       IsaIoDevice = ISA_IO_DEVICE_FROM_ISA_IO_THIS (IsaIo);
500 
501       //
502       // Close the child handle
503       //
504 
505       Status = gBS->CloseProtocol (
506                       Controller,
507                       &gEfiPciIoProtocolGuid,
508                       This->DriverBindingHandle,
509                       ChildHandleBuffer[Index]
510                       );
511       Status = gBS->UninstallMultipleProtocolInterfaces (
512                       ChildHandleBuffer[Index],
513                       &gEfiDevicePathProtocolGuid,
514                       IsaIoDevice->DevicePath,
515                       &gEfiIsaIoProtocolGuid,
516                       &IsaIoDevice->IsaIo,
517                       NULL
518                       );
519 
520       if (!EFI_ERROR (Status)) {
521         FreePool (IsaIoDevice->DevicePath);
522         FreePool (IsaIoDevice);
523       } else {
524         //
525         // Re-open PCI IO Protocol on behalf of the child device
526         // because of failure of destroying the child device handle
527         //
528         gBS->OpenProtocol (
529                Controller,
530                &gEfiPciIoProtocolGuid,
531                (VOID **) &PciIo,
532                This->DriverBindingHandle,
533                ChildHandleBuffer[Index],
534                EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
535                );
536       }
537     }
538 
539     if (EFI_ERROR (Status)) {
540       AllChildrenStopped = FALSE;
541     }
542   }
543 
544   if (!AllChildrenStopped) {
545     return EFI_DEVICE_ERROR;
546   }
547 
548   return EFI_SUCCESS;
549 }
550 
551 //
552 // Internal Function
553 //
554 
555 /**
556   Create EFI Handle for a ISA device found via ISA ACPI Protocol
557 
558   @param[in] This                   The EFI_DRIVER_BINDING_PROTOCOL instance.
559   @param[in] Controller             The handle of ISA bus controller(PCI to ISA bridge)
560   @param[in] PciIo                  The Pointer to the PCI protocol
561   @param[in] ParentDevicePath       Device path of the ISA bus controller
562   @param[in] IsaDeviceResourceList  The resource list of the ISA device
563   @param[out] ChildDevicePath       The pointer to the child device.
564 
565   @retval EFI_SUCCESS               The handle for the child device was created.
566   @retval EFI_OUT_OF_RESOURCES      The request could not be completed due to a lack of resources.
567   @retval EFI_DEVICE_ERROR          The handle for the child device can not be created.
568 **/
569 EFI_STATUS
IsaCreateDevice(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE Controller,IN EFI_PCI_IO_PROTOCOL * PciIo,IN EFI_DEVICE_PATH_PROTOCOL * ParentDevicePath,IN EFI_ISA_ACPI_RESOURCE_LIST * IsaDeviceResourceList,OUT EFI_DEVICE_PATH_PROTOCOL ** ChildDevicePath)570 IsaCreateDevice (
571   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
572   IN EFI_HANDLE                   Controller,
573   IN EFI_PCI_IO_PROTOCOL          *PciIo,
574   IN EFI_DEVICE_PATH_PROTOCOL     *ParentDevicePath,
575   IN EFI_ISA_ACPI_RESOURCE_LIST   *IsaDeviceResourceList,
576   OUT EFI_DEVICE_PATH_PROTOCOL    **ChildDevicePath
577   )
578 {
579   EFI_STATUS    Status;
580   ISA_IO_DEVICE *IsaIoDevice;
581   EFI_DEV_PATH  Node;
582 
583   //
584   // Initialize the PCI_IO_DEVICE structure
585   //
586   IsaIoDevice = AllocateZeroPool (sizeof (ISA_IO_DEVICE));
587   if (IsaIoDevice == NULL) {
588     return EFI_OUT_OF_RESOURCES;
589   }
590 
591   IsaIoDevice->Signature  = ISA_IO_DEVICE_SIGNATURE;
592   IsaIoDevice->Handle     = NULL;
593   IsaIoDevice->PciIo      = PciIo;
594 
595   //
596   // Initialize the ISA I/O instance structure
597   //
598   InitializeIsaIoInstance (IsaIoDevice, IsaDeviceResourceList);
599 
600   //
601   // Build the child device path
602   //
603   Node.DevPath.Type     = ACPI_DEVICE_PATH;
604   Node.DevPath.SubType  = ACPI_DP;
605   SetDevicePathNodeLength (&Node.DevPath, sizeof (ACPI_HID_DEVICE_PATH));
606   Node.Acpi.HID = IsaDeviceResourceList->Device.HID;
607   Node.Acpi.UID = IsaDeviceResourceList->Device.UID;
608 
609   IsaIoDevice->DevicePath = AppendDevicePathNode (
610                               ParentDevicePath,
611                               &Node.DevPath
612                               );
613 
614   if (IsaIoDevice->DevicePath == NULL) {
615     Status = EFI_OUT_OF_RESOURCES;
616     goto Done;
617   }
618 
619   *ChildDevicePath = IsaIoDevice->DevicePath;
620 
621   //
622   // Create a child handle and install Device Path and ISA I/O protocols
623   //
624   Status = gBS->InstallMultipleProtocolInterfaces (
625                   &IsaIoDevice->Handle,
626                   &gEfiDevicePathProtocolGuid,
627                   IsaIoDevice->DevicePath,
628                   &gEfiIsaIoProtocolGuid,
629                   &IsaIoDevice->IsaIo,
630                   NULL
631                   );
632   if (EFI_ERROR (Status)) {
633     goto Done;
634   }
635 
636   Status = gBS->OpenProtocol (
637                   Controller,
638                   &gEfiPciIoProtocolGuid,
639                   (VOID **) &PciIo,
640                   This->DriverBindingHandle,
641                   IsaIoDevice->Handle,
642                   EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
643                   );
644   if (EFI_ERROR (Status)) {
645     gBS->UninstallMultipleProtocolInterfaces (
646            IsaIoDevice->Handle,
647            &gEfiDevicePathProtocolGuid,
648            IsaIoDevice->DevicePath,
649            &gEfiIsaIoProtocolGuid,
650            &IsaIoDevice->IsaIo,
651            NULL
652            );
653   }
654 
655 Done:
656 
657   if (EFI_ERROR (Status)) {
658     if (IsaIoDevice->DevicePath != NULL) {
659       FreePool (IsaIoDevice->DevicePath);
660     }
661 
662     FreePool (IsaIoDevice);
663   }
664 
665   return Status;
666 }
667 
668