hc
2024-03-22 a0752693d998599af469473b8dc239ef973a012f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/** @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;
}