/** @file CapabilityDescriptor.c
|
* @brief Definitions for reading USB capability descriptor DisplayLink dock
|
*
|
* Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
#include "UsbDisplayLink.h"
|
#include "UsbDescriptors.h"
|
|
/**
|
* Check that the Capability Descriptor is valid and hasn't been tampered with.
|
* @param Data Binary data of the Capability Descriptor received from the DisplayLink device
|
* @param Length
|
* @param out
|
* @return
|
*/
|
STATIC EFI_STATUS
|
ValidateHeader (
|
CONST IN VOID* Data,
|
IN UINTN Length,
|
OUT CONST VendorDescriptorGeneric** Out
|
)
|
{
|
if (Length < sizeof (VendorDescriptorGeneric)) {
|
DEBUG ((DEBUG_ERROR, "Data too short (%d bytes) for capability descriptor header section\n", Length));
|
return EFI_INVALID_PARAMETER;
|
}
|
CONST VendorDescriptorGeneric* Desc = (VendorDescriptorGeneric*)Data;
|
if (Desc->Length > Length) {
|
DEBUG ((DEBUG_ERROR, "Capability descriptor: Descriptor (%d bytes) exceeds buffer (%d bytes)\n",
|
Length, Desc->Length));
|
return EFI_BUFFER_TOO_SMALL;
|
}
|
if (Desc->Type != DESCRIPTOR_TYPE_DIRECTFB_CAPABILITY) {
|
DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid type (0x%08x)\n", Desc->Type));
|
return EFI_UNSUPPORTED;
|
}
|
if (Desc->CapabilityVersion != 1) {
|
DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid version (%d)\n", Desc->CapabilityVersion));
|
return EFI_INCOMPATIBLE_VERSION;
|
}
|
// Capability length must fit within remaining space
|
if (Desc->CapabilityLength > (Desc->Length - (sizeof (Desc->Length) + sizeof (Desc->Type)))) {
|
DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid CapabilityLength (%d)\n", Desc->CapabilityLength));
|
return EFI_INVALID_PARAMETER;
|
}
|
*Out = Desc;
|
return EFI_SUCCESS;
|
}
|
|
|
/**
|
* Check that the connected DisplayLink device supports the functionality that this driver requires, e.g. video formats
|
* @param Data Binary data of the Capability Descriptor received from the DisplayLink device
|
* @param Length
|
* @param Output
|
* @return
|
*/
|
EFI_STATUS
|
UsbDisplayLinkParseCapabilitiesDescriptor (
|
CONST IN VOID* Data,
|
IN UINTN Length,
|
OUT VendorDescriptor* Output
|
)
|
{
|
CONST VendorDescriptorGeneric* Desc;
|
EFI_STATUS Status;
|
|
Desc = NULL;
|
Status = ValidateHeader (Data, Length, &Desc);
|
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
// Default capabilities
|
Output->Capabilities1 = 0;
|
|
CONST UINTN CapsHeaderLength = sizeof (Desc->CapabilityVersion) + sizeof (Desc->CapabilityLength);
|
ASSERT (CapsHeaderLength < MAX_UINT8);
|
|
UINTN DataRemaining;
|
UINTN Offset;
|
|
DataRemaining = Desc->CapabilityLength - CapsHeaderLength;
|
Offset = 0;
|
|
while (DataRemaining >= sizeof (DescriptorKLV)) {
|
CONST DescriptorKLV* KeyHeader = (CONST DescriptorKLV*)(Desc->Klv + Offset);
|
CONST UINTN KeyValueSize = sizeof (DescriptorKLV) + KeyHeader->Length;
|
if (KeyValueSize > DataRemaining) {
|
DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid value Length (%d)\n", Desc->CapabilityLength));
|
return EFI_INVALID_PARAMETER;
|
}
|
|
switch (KeyHeader->Key) {
|
case CAPABILITIES1_KEY: {
|
if (KeyHeader->Length != CAPABILITIES1_LENGTH) {
|
DEBUG ((DEBUG_ERROR, "Capability descriptor: invalid length (%d) for Capabilities 1\n", KeyHeader->Length));
|
return EFI_INVALID_PARAMETER;
|
}
|
Output->Capabilities1 = *(UINT32*)KeyHeader->Value;
|
break;
|
default:
|
// Ignore unknown types
|
break;
|
}
|
}
|
DataRemaining -= KeyValueSize;
|
Offset += KeyValueSize;
|
}
|
return EFI_SUCCESS;
|
}
|
|
|
/**
|
* Check that the DisplayLink device supports the basic level of functionality to display GOP pixels.
|
* @param Descriptor The USB descriptor received from the DisplayLink device
|
* @return True we can bind, False we can't
|
*/
|
BOOLEAN
|
UsbDisplayLinkCapabilitiesSufficientToBind (
|
CONST IN VendorDescriptor* Descriptor
|
)
|
{
|
BOOLEAN Sufficient;
|
Sufficient = (BOOLEAN)(Descriptor->Capabilities1 & CAPABILITIES1_BASE_PROTOCOL);
|
|
if (Sufficient == FALSE) {
|
DEBUG ((DEBUG_ERROR, "DisplayLink device does not report support for base capabilites - reports x%x, required x%x\n", Descriptor->Capabilities1 & CAPABILITIES1_BASE_PROTOCOL));
|
}
|
return Sufficient;
|
}
|
|