/** @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; }