11.3.1 Device Drivers

Device drivers implementing GetControllerName() must verify that ChildHandle is NULL and that ControllerHandle represents a device the device driver is currently managing. In addition, GetControllerName() must verify that the requested Language is in the set of languages the UEFI Driver supports. The example below shows the steps required to check these parameters. If the checks pass, the name of the controller is returned. In this specific example, the driver opens the PCI I/O Protocol in its Driver Binding Start() function. This is why gEfiPciIoProtocolGuid is used in the call to the EDK II Library UefiLib function EfiTestManagedDevice() that checks to see if the UEFI Drivers providing the GetControllerName() service is currently managing ControllerHandle. Just like the GetDriverName() example in the previous section, a static table of Unicode strings for the controller names is declared as a global variable and the LookupUnicodeString2() service is used to lookup the name of the controller in the requested Language.

Example 131-GetControllerName() for a Device Driver
#include <Uefi.h>
#include <Protocol/ComponentName2.h>
#include <Protocol/PciIo.h>
#include <Library/UefiLib.h>

GLOBAL_REMOVE_IF_UNREFERENCED
EFI_UNICODE_STRING_TABLE mAbcControllerNameTable[] = {
  { "eng;en", (CHAR16 *)L"ABC Controller in English"},
  { "fra;fr", (CHAR16 *)L"ABC Controller in French"},
  { "spa;sp", (CHAR16 *)L"ABC Controller in Spanish"},
  { NULL, NULL }
};

EFI_STATUS
EFIAPI
AbcGetControllerName (
  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN  EFI_HANDLE                    ControllerHandle,
  IN  EFI_HANDLE                    ChildHandle,       OPTIONAL
  IN  CHAR8                         *Language,
  OUT CHAR16                        **ControllerName
  )
{
  EFI_STATUS  Status;

  //
  // ChildHandle must be NULL for a Device Driver
  //
  if (ChildHandle != NULL) {
    return EFI_UNSUPPORTED;
  }

  //
  // Make sure this driver is currently managing ControllerHandle
  //
  Status = EfiTestManagedDevice (
             ControllerHandle,
             gAbcDriverBinding.DriverBindingHandle,
             &gEfiPciIoProtocolGuid
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  return LookupUnicodeString2 (
           Language,
           This->SupportedLanguages,
           mAbcControllerNameTable,
           ControllerName,
           (BOOLEAN)(This != &gAbcComponentName2)
           );
}

If the private context structure is required, use the UEFI Boot Service OpenProtocol()to open one of the protocols on ControllerHandle produced by the UEFI Driver and then use a CR() based macro to retrieve a pointer to the private context structure.

Some device drivers can extract name information from the devices they manage and are then able to provide more specific device names. The dynamic generation of controller names does increase the complexity of the UEFI Driver implementation, but it may provide users with the detailed information they require to identify a specific device. For example, a driver for a mass storage device may be able to produce a static name such as "Hard Disk," but a more specific name, such as "XYZ Manufacturer SATA Model 123 Hard Disk", may be much more useful.

To support the dynamic generation of controller names, a few additional steps must be taken. First, a pointer to the dynamic table of names must be added to the private context data structure for the controllers a device driver manages. The example below shows the addition of an EFI_UNICODE_STRING_TABLE field to the private context data structure discussed in Chapter 8 of this guide.

Example 132-Controller names in private context data structure
#define ABC_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('A','B','C',' ')

typedef struct {
  UINTN                     Signature;
  EFI_PCI_IO_PROTOCOL       *PciIo;
  //
  // Dynamically allocated table of controller names
  //
  EFI_UNICODE_STRING_TABLE  *ControllerNameTable;
} ABC_PRIVATE_DATA;
#define ABC_PRIVATE_DATA_FROM_PCI_IO_THIS(a) \
CR (a, ABC_PRIVATE_DATA, PciIo, ABC_PRIVATE_DATA_SIGNATURE)

The next update is to the Start() service of the Driver Binding Protocol. It needs to add a controller name in each supported language to ControllerNameTable in the private context data structure. Use the UefiLib function AddUnicodeString2() to add one or more names to a table. The ControllerNameTable must be initialized to NULL before the first name is added.

The following example shows the addition of an English name to a dynamically allocated table of Unicode names. If more than one language is supported, then AddUnicodeString2() is called for each language. The construction of the Unicode string for each language is not covered here. The format of names stored with devices varies depending on the bus type, and the translation from a bus-specific name format to a Unicode string cannot be standardized.

Example 133-Adding a controller name to a dynamic controller name table
#include <Uefi.h>
#include <Library/UefiLib.h>

ABC_PRIVATE_DATA *Private
CHAR16 *ControllerName

//
// Get dynamic name from the device being managed
//

//
// Convert the device name to a Unicode string in a supported language
//

//
// Add the device name to the table of names stored in the private
// context data structure using ISO 639-2 language code
//
AddUnicodeString2 (
  "eng",
  gAbcComponentName.SupportedLanguages,
  &Private->ControllerNameTable,
  ControllerName,
  TRUE
  );

//
// Add the device name to the table of names stored in the private
// context data structure using RFC 4646 language code
//
AddUnicodeString2 (
  "en",
  gAbcComponentName2.SupportedLanguages,
  &Private->ControllerNameTable,
  ControllerName,
  FALSE
  );

The Stop() service of the Driver Binding Protocol also needs to be updated. When a request is made for a driver to stop managing a controller, the table of controller names built in the Start() service must be freed. Use the UEFI driver library function FreeUnicodeStringTable()to free the table of controller names.

The code to add to the Driver Binding Protocol Stop() service follows. The private context data structure is required by the Stop() service so the private context data structure can be freed. The call to FreeUnicodeStringTable()should be made just before the private context data structure is freed.

Example 134-Freeing a dynamic controller name table
#include <Uefi.h>
#include <Library/UefiLib.h>

ABC_PRIVATE_DATA *Private

FreeUnicodeStringTable (Private->ControllerNameTable);

Lastly, the GetControllerName() service is slightly different because the dynamic table of controller names from the private context structure is used instead of the static table of controller names. Because the table of controller names is now maintained in the private context data structure, the private context data structure needs to be retrieved based on the parameters passed into GetControllerName(). This retrieval is achieved by looking up a protocol that the driver has produced on ControllerHandle and using a pointer to that protocol and a CR() macro to retrieve a pointer to the private context data structure. The private context data structure can then be used with the UefiLib function LookupUnicodeString2() to look up the controller's name in the dynamic table of controller names.

The example below shows the GetControllerName() service that retrieves the controller name from a dynamic table stored in the private context data structure.

Example 135-Device driver with dynamic controller names
#include <Uefi.h>
#include <Protocol/ComponentName2.h>
#include <Protocol/PciIo.h>
#include <Library/UefiLib.h>

EFI_STATUS
EFIAPI
AbcGetControllerName (
  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN  EFI_HANDLE                    ControllerHandle,
  IN  EFI_HANDLE                    ChildHandle,       OPTIONAL
  IN  CHAR8                         *Language,
  OUT CHAR16                        **ControllerName
  )
{
  EFI_STATUS           Status;
  EFI_PCI_IO_PROTOCOL  *PciIo;
  ABC_PRIVATE_DATA     *Private;

  //
  // ChildHandle must be NULL for a Device Driver
  //
  if (ChildHandle != NULL) {
    return EFI_UNSUPPORTED;
  }

  //
  // Make sure this driver is currently managing ControllerHandle
  //
  Status = EfiTestManagedDevice (
             ControllerHandle,
             gAbcDriverBinding.DriverBindingHandle,
             &gEfiPciIoProtocolGuid
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Retrieve an instance of a produced protocol from ControllerHandle
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiPciIoProtocolGuid,
                  (VOID **)&PciIo,
                  gAbcDriverBinding.DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Retrieve the private context data structure for ControllerHandle
  //
  Private = ABC_PRIVATE_DATA_FROM_PCI_IO_THIS (PciIo);

  //
  // Look up the controller name from a dynamic table of controller names
  //
  return LookupUnicodeString2 (
           Language,
           This->SupportedLanguages,
           Private->ControllerNameTable,
           ControllerName,
           (BOOLEAN)(This != &gAbcComponentName2)
           );
}