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