/** @file | |
Driver for virtio-serial devices. | |
The virtio serial device also known as virtio console device because | |
initially it had only support for a single tty, intended to be used | |
as console. Support for multiple streams and named data ports has | |
been added later on. | |
https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-2900003 | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/DevicePathLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiLib.h> | |
#include <Library/VirtioLib.h> | |
#include "VirtioSerial.h" | |
STATIC LIST_ENTRY mVirtioSerialList; | |
STATIC CONST CHAR8 *EventNames[] = { | |
[VIRTIO_SERIAL_DEVICE_READY] = "device-ready", | |
[VIRTIO_SERIAL_DEVICE_ADD] = "device-add", | |
[VIRTIO_SERIAL_DEVICE_REMOVE] = "device-remove", | |
[VIRTIO_SERIAL_PORT_READY] = "port-ready", | |
[VIRTIO_SERIAL_CONSOLE_PORT] = "console-port", | |
[VIRTIO_SERIAL_RESIZE] = "resize", | |
[VIRTIO_SERIAL_PORT_OPEN] = "port-open", | |
[VIRTIO_SERIAL_PORT_NAME] = "port-name", | |
}; | |
VOID | |
EFIAPI | |
LogDevicePath ( | |
UINT32 Level, | |
const CHAR8 *Func, | |
CHAR16 *Note, | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
) | |
{ | |
CHAR16 *Str; | |
Str = ConvertDevicePathToText (DevicePath, FALSE, FALSE); | |
if (!Str) { | |
DEBUG ((DEBUG_INFO, "ConvertDevicePathToText failed\n")); | |
return; | |
} | |
DEBUG ((Level, "%a: %s%s%s\n", Func, Note ? Note : L"", Note ? L": " : L"", Str)); | |
FreePool (Str); | |
} | |
EFI_STATUS | |
EFIAPI | |
VirtioSerialTxControl ( | |
IN OUT VIRTIO_SERIAL_DEV *Dev, | |
IN UINT32 Id, | |
IN UINT16 Event, | |
IN UINT16 Value | |
) | |
{ | |
VIRTIO_SERIAL_CONTROL Control; | |
Control.Id = Id; | |
Control.Event = Event; | |
Control.Value = Value; | |
DEBUG (( | |
DEBUG_INFO, | |
"%a:%d: >>> event %a, port-id %d, value %d\n", | |
__func__, | |
__LINE__, | |
EventNames[Control.Event], | |
Control.Id, | |
Control.Value | |
)); | |
VirtioSerialRingClearTx (Dev, VIRTIO_SERIAL_Q_TX_CTRL); | |
return VirtioSerialRingSendBuffer (Dev, VIRTIO_SERIAL_Q_TX_CTRL, &Control, sizeof (Control), TRUE); | |
} | |
STATIC | |
VOID | |
EFIAPI | |
VirtioSerialRxControl ( | |
IN OUT VIRTIO_SERIAL_DEV *Dev | |
) | |
{ | |
UINT8 Data[CTRL_RX_BUFSIZE+1]; | |
UINT32 DataSize; | |
VIRTIO_SERIAL_CONTROL Control; | |
EFI_STATUS Status; | |
BOOLEAN HasData; | |
UINT16 Ready; | |
for ( ; ;) { | |
HasData = VirtioSerialRingGetBuffer (Dev, VIRTIO_SERIAL_Q_RX_CTRL, Data, &DataSize); | |
if (!HasData) { | |
return; | |
} | |
if (DataSize < sizeof (Control)) { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a:%d: length mismatch: %d != %d\n", | |
__func__, | |
__LINE__, | |
DataSize, | |
sizeof (Control) | |
)); | |
continue; | |
} | |
CopyMem (&Control, Data, sizeof (Control)); | |
if (Control.Event < ARRAY_SIZE (EventNames)) { | |
DEBUG (( | |
DEBUG_INFO, | |
"%a:%d: <<< event %a, port-id %d, value %d\n", | |
__func__, | |
__LINE__, | |
EventNames[Control.Event], | |
Control.Id, | |
Control.Value | |
)); | |
} else { | |
DEBUG (( | |
DEBUG_ERROR, | |
"%a:%d: unknown event: %d\n", | |
__func__, | |
__LINE__, | |
Control.Event | |
)); | |
} | |
switch (Control.Event) { | |
case VIRTIO_SERIAL_DEVICE_ADD: | |
if (Control.Id < MAX_PORTS) { | |
Status = VirtioSerialPortAdd (Dev, Control.Id); | |
Ready = (Status == EFI_SUCCESS) ? 1 : 0; | |
} else { | |
Ready = 0; | |
} | |
VirtioSerialTxControl (Dev, Control.Id, VIRTIO_SERIAL_PORT_READY, Ready); | |
if (Ready) { | |
Dev->NumPorts++; | |
} | |
break; | |
case VIRTIO_SERIAL_DEVICE_REMOVE: | |
if (Control.Id < MAX_PORTS) { | |
VirtioSerialPortRemove (Dev, Control.Id); | |
} | |
break; | |
case VIRTIO_SERIAL_CONSOLE_PORT: | |
if (Control.Id < MAX_PORTS) { | |
VirtioSerialPortSetConsole (Dev, Control.Id); | |
Dev->NumConsoles++; | |
} | |
break; | |
case VIRTIO_SERIAL_PORT_NAME: | |
if (Control.Id < MAX_PORTS) { | |
Data[DataSize] = 0; | |
VirtioSerialPortSetName (Dev, Control.Id, Data + sizeof (Control)); | |
Dev->NumNamedPorts++; | |
} | |
break; | |
case VIRTIO_SERIAL_PORT_OPEN: | |
if (Control.Id < MAX_PORTS) { | |
VirtioSerialPortSetDeviceOpen (Dev, Control.Id, Control.Value); | |
} | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
STATIC | |
VOID | |
EFIAPI | |
VirtioSerialTimer ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
VIRTIO_SERIAL_DEV *Dev = Context; | |
VirtioSerialRxControl (Dev); | |
} | |
STATIC | |
VOID | |
EFIAPI | |
VirtioSerialUninitAllRings ( | |
IN OUT VIRTIO_SERIAL_DEV *Dev | |
) | |
{ | |
UINT16 Index; | |
for (Index = 0; Index < MAX_RINGS; Index++) { | |
VirtioSerialUninitRing (Dev, Index); | |
} | |
} | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioSerialInit ( | |
IN OUT VIRTIO_SERIAL_DEV *Dev | |
) | |
{ | |
UINT8 NextDevStat; | |
EFI_STATUS Status; | |
UINT64 Features; | |
UINTN Retries; | |
// | |
// Execute virtio-0.9.5, 2.2.1 Device Initialization Sequence. | |
// | |
NextDevStat = 0; // step 1 -- reset device | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
NextDevStat |= VSTAT_ACK; // step 2 -- acknowledge device presence | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
NextDevStat |= VSTAT_DRIVER; // step 3 -- we know how to drive it | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// Set Page Size - MMIO VirtIo Specific | |
// | |
Status = Dev->VirtIo->SetPageSize (Dev->VirtIo, EFI_PAGE_SIZE); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// step 4a -- retrieve and validate features | |
// | |
Status = Dev->VirtIo->GetDeviceFeatures (Dev->VirtIo, &Features); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
Features &= (VIRTIO_F_VERSION_1 | | |
VIRTIO_F_IOMMU_PLATFORM | | |
VIRTIO_SERIAL_F_MULTIPORT); | |
// | |
// In virtio-1.0, feature negotiation is expected to complete before queue | |
// discovery, and the device can also reject the selected set of features. | |
// | |
if (Dev->VirtIo->Revision >= VIRTIO_SPEC_REVISION (1, 0, 0)) { | |
Status = Virtio10WriteFeatures (Dev->VirtIo, Features, &NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"%a:%d: features ok:%a%a%a\n", | |
__func__, | |
__LINE__, | |
(Features & VIRTIO_F_VERSION_1) ? " v1.0" : "", | |
(Features & VIRTIO_F_IOMMU_PLATFORM) ? " iommu" : "", | |
(Features & VIRTIO_SERIAL_F_MULTIPORT) ? " multiport" : "" | |
)); | |
if (Features & VIRTIO_SERIAL_F_MULTIPORT) { | |
Dev->VirtIo->ReadDevice ( | |
Dev->VirtIo, | |
OFFSET_OF (VIRTIO_SERIAL_CONFIG, MaxPorts), | |
sizeof (Dev->Config.MaxPorts), | |
sizeof (Dev->Config.MaxPorts), | |
&Dev->Config.MaxPorts | |
); | |
DEBUG (( | |
DEBUG_INFO, | |
"%a:%d: max device ports: %d\n", | |
__func__, | |
__LINE__, | |
Dev->Config.MaxPorts | |
)); | |
} | |
Status = VirtioSerialInitRing (Dev, VIRTIO_SERIAL_Q_RX_CTRL, CTRL_RX_BUFSIZE); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
Status = VirtioSerialInitRing (Dev, VIRTIO_SERIAL_Q_TX_CTRL, CTRL_TX_BUFSIZE); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
// | |
// step 5 -- Report understood features and guest-tuneables. | |
// | |
if (Dev->VirtIo->Revision < VIRTIO_SPEC_REVISION (1, 0, 0)) { | |
Features &= ~(UINT64)(VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM); | |
Status = Dev->VirtIo->SetGuestFeatures (Dev->VirtIo, Features); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
} | |
// | |
// step 6 -- initialization complete | |
// | |
NextDevStat |= VSTAT_DRIVER_OK; | |
Status = Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
VirtioSerialRingFillRx (Dev, VIRTIO_SERIAL_Q_RX_CTRL); | |
VirtioSerialTxControl (Dev, 0, VIRTIO_SERIAL_DEVICE_READY, 1); | |
for (Retries = 0; Retries < 100; Retries++) { | |
gBS->Stall (1000); | |
VirtioSerialRxControl (Dev); | |
if (Dev->NumPorts && (Dev->NumConsoles + Dev->NumNamedPorts == Dev->NumPorts)) { | |
// port discovery complete | |
break; | |
} | |
} | |
Status = gBS->CreateEvent ( | |
EVT_TIMER | EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
VirtioSerialTimer, | |
Dev, | |
&Dev->Timer | |
); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
Status = gBS->SetTimer ( | |
Dev->Timer, | |
TimerPeriodic, | |
EFI_TIMER_PERIOD_MILLISECONDS (10) | |
); | |
if (EFI_ERROR (Status)) { | |
goto Failed; | |
} | |
DEBUG (( | |
DEBUG_INFO, | |
"%a:%d: OK, %d consoles, %d named ports\n", | |
__func__, | |
__LINE__, | |
Dev->NumConsoles, | |
Dev->NumNamedPorts | |
)); | |
return EFI_SUCCESS; | |
Failed: | |
VirtioSerialUninitAllRings (Dev); | |
// | |
// Notify the host about our failure to setup: virtio-0.9.5, 2.2.2.1 Device | |
// Status. VirtIo access failure here should not mask the original error. | |
// | |
NextDevStat |= VSTAT_FAILED; | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, NextDevStat); | |
DEBUG ((DEBUG_INFO, "%a:%d: ERROR: %r\n", __func__, __LINE__, Status)); | |
return Status; // reached only via Failed above | |
} | |
STATIC | |
VOID | |
EFIAPI | |
VirtioSerialUninit ( | |
IN OUT VIRTIO_SERIAL_DEV *Dev | |
) | |
{ | |
UINT32 PortId; | |
gBS->CloseEvent (Dev->Timer); | |
// | |
// Reset the virtual device -- see virtio-0.9.5, 2.2.2.1 Device Status. When | |
// VIRTIO_CFG_WRITE() returns, the host will have learned to stay away from | |
// the old comms area. | |
// | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); | |
for (PortId = 0; PortId < MAX_PORTS; PortId++) { | |
VirtioSerialPortRemove (Dev, PortId); | |
} | |
VirtioSerialUninitAllRings (Dev); | |
} | |
// | |
// Event notification function enqueued by ExitBootServices(). | |
// | |
STATIC | |
VOID | |
EFIAPI | |
VirtioSerialExitBoot ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
VIRTIO_SERIAL_DEV *Dev; | |
DEBUG ((DEBUG_INFO, "%a: Context=0x%p\n", __func__, Context)); | |
// | |
// Reset the device. This causes the hypervisor to forget about the virtio | |
// ring. | |
// | |
// We allocated said ring in EfiBootServicesData type memory, and code | |
// executing after ExitBootServices() is permitted to overwrite it. | |
// | |
Dev = Context; | |
Dev->VirtIo->SetDeviceStatus (Dev->VirtIo, 0); | |
} | |
STATIC | |
VIRTIO_SERIAL_DEV * | |
VirtioSerialFind ( | |
EFI_HANDLE DeviceHandle | |
) | |
{ | |
VIRTIO_SERIAL_DEV *Dev; | |
LIST_ENTRY *Entry; | |
BASE_LIST_FOR_EACH (Entry, &mVirtioSerialList) { | |
Dev = CR (Entry, VIRTIO_SERIAL_DEV, Link, VIRTIO_SERIAL_SIG); | |
if (DeviceHandle == Dev->DeviceHandle) { | |
return Dev; | |
} | |
} | |
return NULL; | |
} | |
// | |
// Probe, start and stop functions of this driver, called by the DXE core for | |
// specific devices. | |
// | |
// The following specifications document these interfaces: | |
// - Driver Writer's Guide for UEFI 2.3.1 v1.01, 9 Driver Binding Protocol | |
// - UEFI Spec 2.3.1 + Errata C, 10.1 EFI Driver Binding Protocol | |
// | |
// The implementation follows: | |
// - Driver Writer's Guide for UEFI 2.3.1 v1.01 | |
// - 5.1.3.4 OpenProtocol() and CloseProtocol() | |
// - UEFI Spec 2.3.1 + Errata C | |
// - 6.3 Protocol Handler Services | |
// | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioSerialDriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
VIRTIO_DEVICE_PROTOCOL *VirtIo; | |
// | |
// Attempt to open the device with the VirtIo set of interfaces. On success, | |
// the protocol is "instantiated" for the VirtIo device. Covers duplicate | |
// open attempts (EFI_ALREADY_STARTED). | |
// | |
Status = gBS->OpenProtocol ( | |
DeviceHandle, // candidate device | |
&gVirtioDeviceProtocolGuid, // for generic VirtIo access | |
(VOID **)&VirtIo, // handle to instantiate | |
This->DriverBindingHandle, // requestor driver identity | |
DeviceHandle, // ControllerHandle, according to | |
// the UEFI Driver Model | |
EFI_OPEN_PROTOCOL_BY_DRIVER // get exclusive VirtIo access to | |
// the device; to be released | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (VirtIo->SubSystemDeviceId != VIRTIO_SUBSYSTEM_CONSOLE) { | |
Status = EFI_UNSUPPORTED; | |
} | |
// | |
// We needed VirtIo access only transitorily, to see whether we support the | |
// device or not. | |
// | |
gBS->CloseProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
This->DriverBindingHandle, | |
DeviceHandle | |
); | |
return Status; | |
} | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioSerialDriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath | |
) | |
{ | |
VIRTIO_SERIAL_DEV *Dev; | |
EFI_STATUS Status; | |
Dev = (VIRTIO_SERIAL_DEV *)AllocateZeroPool (sizeof *Dev); | |
if (Dev == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Status = gBS->OpenProtocol ( | |
DeviceHandle, | |
&gEfiDevicePathProtocolGuid, | |
(VOID **)&Dev->DevicePath, | |
This->DriverBindingHandle, | |
DeviceHandle, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
goto FreeVirtioSerial; | |
} | |
Status = gBS->OpenProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
(VOID **)&Dev->VirtIo, | |
This->DriverBindingHandle, | |
DeviceHandle, | |
EFI_OPEN_PROTOCOL_BY_DRIVER | |
); | |
if (EFI_ERROR (Status)) { | |
goto FreeVirtioSerial; | |
} | |
LogDevicePath (DEBUG_INFO, __func__, L"Dev", Dev->DevicePath); | |
// | |
// VirtIo access granted, configure virtio-serial device. | |
// | |
Dev->Signature = VIRTIO_SERIAL_SIG; | |
Dev->DriverBindingHandle = This->DriverBindingHandle; | |
Dev->DeviceHandle = DeviceHandle; | |
Status = VirtioSerialInit (Dev); | |
if (EFI_ERROR (Status)) { | |
goto CloseVirtIo; | |
} | |
Status = gBS->CreateEvent ( | |
EVT_SIGNAL_EXIT_BOOT_SERVICES, | |
TPL_CALLBACK, | |
&VirtioSerialExitBoot, | |
Dev, | |
&Dev->ExitBoot | |
); | |
if (EFI_ERROR (Status)) { | |
goto UninitDev; | |
} | |
InsertTailList (&mVirtioSerialList, &(Dev->Link)); | |
return EFI_SUCCESS; | |
UninitDev: | |
VirtioSerialUninit (Dev); | |
CloseVirtIo: | |
gBS->CloseProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
This->DriverBindingHandle, | |
DeviceHandle | |
); | |
FreeVirtioSerial: | |
FreePool (Dev); | |
return Status; | |
} | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioSerialDriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer | |
) | |
{ | |
VIRTIO_SERIAL_DEV *Dev; | |
UINT32 PortId; | |
UINT32 Child; | |
Dev = VirtioSerialFind (DeviceHandle); | |
if (!Dev) { | |
return EFI_SUCCESS; | |
} | |
if (NumberOfChildren) { | |
for (Child = 0; Child < NumberOfChildren; Child++) { | |
DEBUG ((DEBUG_INFO, "%a:%d: child handle 0x%x\n", __func__, __LINE__, ChildHandleBuffer[Child])); | |
for (PortId = 0; PortId < MAX_PORTS; PortId++) { | |
if (Dev->Ports[PortId].Ready && | |
Dev->Ports[PortId].SerialIo && | |
(Dev->Ports[PortId].SerialIo->DeviceHandle == ChildHandleBuffer[Child])) | |
{ | |
VirtioSerialPortRemove (Dev, PortId); | |
} | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
DEBUG ((DEBUG_INFO, "%a:%d: controller handle 0x%x\n", __func__, __LINE__, DeviceHandle)); | |
gBS->CloseEvent (Dev->ExitBoot); | |
VirtioSerialUninit (Dev); | |
gBS->CloseProtocol ( | |
DeviceHandle, | |
&gVirtioDeviceProtocolGuid, | |
This->DriverBindingHandle, | |
DeviceHandle | |
); | |
RemoveEntryList (&(Dev->Link)); | |
ZeroMem (Dev, sizeof (*Dev)); | |
FreePool (Dev); | |
return EFI_SUCCESS; | |
} | |
// | |
// The static object that groups the Supported() (ie. probe), Start() and | |
// Stop() functions of the driver together. Refer to UEFI Spec 2.3.1 + Errata | |
// C, 10.1 EFI Driver Binding Protocol. | |
// | |
STATIC EFI_DRIVER_BINDING_PROTOCOL gDriverBinding = { | |
&VirtioSerialDriverBindingSupported, | |
&VirtioSerialDriverBindingStart, | |
&VirtioSerialDriverBindingStop, | |
0x10, // Version, must be in [0x10 .. 0xFFFFFFEF] for IHV-developed drivers | |
NULL, // ImageHandle, to be overwritten by | |
// EfiLibInstallDriverBindingComponentName2() in VirtioSerialEntryPoint() | |
NULL // DriverBindingHandle, ditto | |
}; | |
// | |
// The purpose of the following scaffolding (EFI_COMPONENT_NAME_PROTOCOL and | |
// EFI_COMPONENT_NAME2_PROTOCOL implementation) is to format the driver's name | |
// in English, for display on standard console devices. This is recommended for | |
// UEFI drivers that follow the UEFI Driver Model. Refer to the Driver Writer's | |
// Guide for UEFI 2.3.1 v1.01, 11 UEFI Driver and Controller Names. | |
// | |
STATIC | |
EFI_UNICODE_STRING_TABLE mDriverNameTable[] = { | |
{ "eng;en", L"Virtio Serial Driver" }, | |
{ NULL, NULL } | |
}; | |
STATIC | |
EFI_UNICODE_STRING_TABLE mDeviceNameTable[] = { | |
{ "eng;en", L"Virtio Serial Device" }, | |
{ NULL, NULL } | |
}; | |
STATIC | |
EFI_UNICODE_STRING_TABLE mPortNameTable[] = { | |
{ "eng;en", L"Virtio Serial Port" }, | |
{ NULL, NULL } | |
}; | |
STATIC | |
EFI_COMPONENT_NAME_PROTOCOL gComponentName; | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioSerialGetDriverName ( | |
IN EFI_COMPONENT_NAME_PROTOCOL *This, | |
IN CHAR8 *Language, | |
OUT CHAR16 **DriverName | |
) | |
{ | |
return LookupUnicodeString2 ( | |
Language, | |
This->SupportedLanguages, | |
mDriverNameTable, | |
DriverName, | |
(BOOLEAN)(This == &gComponentName) // Iso639Language | |
); | |
} | |
STATIC | |
EFI_STATUS | |
EFIAPI | |
VirtioSerialGetDeviceName ( | |
IN EFI_COMPONENT_NAME_PROTOCOL *This, | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_HANDLE ChildHandle, | |
IN CHAR8 *Language, | |
OUT CHAR16 **ControllerName | |
) | |
{ | |
EFI_UNICODE_STRING_TABLE *Table; | |
VIRTIO_SERIAL_DEV *Dev; | |
UINT32 PortId; | |
Dev = VirtioSerialFind (DeviceHandle); | |
if (!Dev) { | |
return EFI_UNSUPPORTED; | |
} | |
if (ChildHandle) { | |
for (PortId = 0; PortId < MAX_PORTS; PortId++) { | |
if (Dev->Ports[PortId].Ready && | |
Dev->Ports[PortId].SerialIo && | |
(Dev->Ports[PortId].SerialIo->DeviceHandle == ChildHandle)) | |
{ | |
*ControllerName = Dev->Ports[PortId].Name; | |
return EFI_SUCCESS; | |
} | |
} | |
Table = mPortNameTable; | |
} else { | |
Table = mDeviceNameTable; | |
} | |
return LookupUnicodeString2 ( | |
Language, | |
This->SupportedLanguages, | |
Table, | |
ControllerName, | |
(BOOLEAN)(This == &gComponentName) | |
); | |
} | |
STATIC | |
EFI_COMPONENT_NAME_PROTOCOL gComponentName = { | |
&VirtioSerialGetDriverName, | |
&VirtioSerialGetDeviceName, | |
"eng" // SupportedLanguages, ISO 639-2 language codes | |
}; | |
STATIC | |
EFI_COMPONENT_NAME2_PROTOCOL gComponentName2 = { | |
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME)&VirtioSerialGetDriverName, | |
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME)&VirtioSerialGetDeviceName, | |
"en" // SupportedLanguages, RFC 4646 language codes | |
}; | |
// | |
// Entry point of this driver. | |
// | |
EFI_STATUS | |
EFIAPI | |
VirtioSerialEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
InitializeListHead (&mVirtioSerialList); | |
return EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gDriverBinding, | |
ImageHandle, | |
&gComponentName, | |
&gComponentName2 | |
); | |
} |