18.3.1 Supported()

A PCI driver must implement the EFI_DRIVER_BINDING_PROTOCOL containing the Supported(), Start(), and Stop() services. The Supported() service evaluates the ControllerHandle passed in to see if the ControllerHandle represents a PCI device the PCI driver can manage.

The most common method of implementing the test is for the PCI driver to retrieve the PCI configuration header from the PCI controller and evaluate the device ID, vendor ID, and, possibly, the class code fields of the PCI configuration header. If these fields match the values the PCI driver knows how to manage, Supported() returns

EFI_SUCCESS. Otherwise, the Supported() service returns EFI_UNSUPPORTED. The PCI driver must be careful not to disturb the state of the PCI controller because a different PCI driver may be managing the PCI controller.

Caution: Do not allow functions to "touch" or change the state of any hardware device in the Supported() function of the Driver Binding Protocol. Doing so can significantly degrade the driver's performance and/or cause the device, the driver, and/or other drivers to lose sync and behave badly and unpredictably.

TIP: When modifying PCI device registers, be careful with the bits in the PCI device configuration space. Perform a read, then modify the desired bits, then do a write. Do not perform only a write operation to the bits, since that can reset other bits in the register.

The following example shows an example of the Driver Binding Protocol Supported() service for the ABC PCI driver managing a PCI controller with a vendor ID of 0x8086 and a device ID of 0xFFFE.

First, it attempts to open the PCI I/O Protocol EFI_OPEN_PROTOCOL_BY_DRIVER with OpenProtocol(). If the PCI I/O Protocol cannot be opened, the PCI driver does not support the controller specified by ControllerHandle. If the PCI I/O Protocol is opened, the services of the PCI I/O Protocol are used to read the vendor ID and device ID from the PCI configuration header. Always closed the PCI I/O Protocol with CloseProtocol(). EFI_SUCCESS is returned if the vendor ID and device ID match.

Example 169-Supported() Reading partial PCI Configuration Header

#include <Uefi.h>
#include <Protocol/DriverBinding.h> #include <Protocol/PciIo.h>
#include <IndustryStandard/Pci.h>
#include <Library/UefiBootServicesTableLib.h>
#define ABC_VENDOR_ID 0x8086
#define ABC_DEVICE_ID 0xFFFE
EFI_STATUS
EFIAPI
AbcSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
EFI_PCI_IO_PROTOCOL *PciIo;
UINT16 VendorId;
UINT16 DeviceId;
//
// Open the PCI I/O Protocol on ControllerHandle
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Read 16-bit Vendor ID from the PCI configuration header at offset 0x00
//
Status = PciIo->Pci.Read (
PciIo, // This
EfiPciIoWidthUint16, // Width
PCI_VENDOR_ID_OFFSET, // Offset
sizeof (VendorId), // Count
&VendorId // Buffer
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// Read 16-bit Device ID from the PCI configuration header at offset 0x02
//
Status = PciIo->Pci.Read (
PciIo, // This
EfiPciIoWidthUint16, // Width
PCI_DEVICE_ID_OFFSET, // Offset
sizeof (DeviceId), // Count
&DeviceId // Buffer
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// Evaluate Vendor ID and Device ID
//
Status = EFI_SUCCESS;
if (VendorId != ABC_VENDOR_ID || DeviceId != ABC_DEVICE_ID) {
Status = EFI_UNSUPPORTED;
}
Done:
//
// Close the PCI I/O Protocol
//
gBS->CloseProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return Status;
}

The previous example performs two 16-bit reads from the PCI configuration header.

The code would be smaller if the entire PCI configuration header were read at once. However, this would increase the execution time because the Supported() service reads the entire PCI configuration header for every ControllerHandle passed in.

The Supported() service is intended to be a small, quick check. If a more extensive evaluation of the PCI configuration header is required, it may make sense to read the entire PCI configuration header at once. The example below shows the same example as above, but differs in that it reads the entire PCI configuration header in a single call to the PCI I/O Protocol reading, 32-bits at a time.

Example 170-Supported() Reading entire PCI Configuration Header

#include <Uefi.h>
#include <Protocol/DriverBinding.h> #include <Protocol/PciIo.h>
#include <IndustryStandard/Pci.h>
#include <Library/UefiBootServicesTableLib.h>
#define ABC_VENDOR_ID 0x8086
#define ABC_DEVICE_ID 0xFFFE
EFI_STATUS
EFIAPI
AbcSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
EFI_PCI_IO_PROTOCOL *PciIo;
PCI_TYPE00 Pci;
//
// Open the PCI I/O Protocol on ControllerHandle
//
Status = gBS->OpenProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
(VOID **)&PciIo,
This->DriverBindingHandle,
ControllerHandle,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Read the entire PCI configuration header using 32-bit reads
//
Status = PciIo->Pci.Read (
PciIo, // This
EfiPciIoWidthUint32, // Width
0, // Offset sizeof
(Pci) / sizeof (UINT32), // Count
&Pci // Buffer
);
if (EFI_ERROR (Status)) {
goto Done;
}
//
// Evaluate Vendor ID and Device ID
//
Status = EFI_SUCCESS;
if (Pci.Hdr.VendorId != ABC_VENDOR_ID || Pci.Hdr.DeviceId != ABC_DEVICE_ID ) {
Status = EFI_UNSUPPORTED;
}
Done:
//
// Close the PCI I/O Protocol
//
gBS->CloseProtocol (
ControllerHandle,
&gEfiPciIoProtocolGuid,
This->DriverBindingHandle,
ControllerHandle
);
return Status;
}