1 /* 2 * Copyright (C) 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include "edid_helper.h" 20 #include <memory> 21 #include <string> 22 #include <unordered_map> 23 #include <vector> 24 25 namespace hcct { 26 27 // https://cs.android.com/android/platform/superproject/main/+/main:external/libdrm/include/drm/drm_mode.h;l=403 28 #define CONNECTOR_TYPES \ 29 CONNECTOR_TYPE(kUnknown, 0, "UNKNOWN") \ 30 CONNECTOR_TYPE(kVGA, 1, "VGA") \ 31 CONNECTOR_TYPE(kDisplayPort, 10, "DP") \ 32 CONNECTOR_TYPE(kHDMIA, 11, "HDMIA") \ 33 CONNECTOR_TYPE(keDP, 14, "eDP") \ 34 CONNECTOR_TYPE(kVirtual, 15, "VIRTUAL") \ 35 CONNECTOR_TYPE(kDSI, 16, "DSI") \ 36 CONNECTOR_TYPE(kDPI, 17, "DPI") \ 37 CONNECTOR_TYPE(kWriteback, 18, "WRITEBACK") 38 39 /** 40 * @class VkmsTester 41 * @brief Handles setup and configuration of Virtual KMS (VKMS) for display 42 * emulation 43 * 44 * This class manages the creation of VKMS directory structures and file system 45 * entries needed to configure virtual displays through the VKMS driver. 46 */ 47 class VkmsTester { 48 public: 49 enum class ConnectorType { 50 #define CONNECTOR_TYPE(enumName, value, stringName) enumName = value, 51 CONNECTOR_TYPES 52 #undef CONNECTOR_TYPE 53 }; 54 55 class VkmsConnectorBuilder { 56 public: 57 // Create a builder with default settings create()58 static VkmsConnectorBuilder create() { return VkmsConnectorBuilder(); } 59 withType(ConnectorType type)60 VkmsConnectorBuilder& withType(ConnectorType type) { 61 mType = type; 62 return *this; 63 } 64 withType(const std::string & typeStr)65 VkmsConnectorBuilder& withType(const std::string& typeStr) { 66 #define CONNECTOR_TYPE(enumName, value, stringName) \ 67 if (typeStr == stringName) mType = ConnectorType::enumName; 68 CONNECTOR_TYPES 69 #undef CONNECTOR_TYPE 70 return *this; 71 } 72 73 VkmsConnectorBuilder& enabledAtStart(bool enabled = true) { 74 mEnabledAtStart = enabled; 75 return *this; 76 } 77 withAdditionalOverlayPlanes(int count)78 VkmsConnectorBuilder& withAdditionalOverlayPlanes(int count) { 79 mAdditionalOverlayPlanes = count; 80 return *this; 81 } 82 withMonitor(edid::MonitorName monitorName)83 VkmsConnectorBuilder& withMonitor(edid::MonitorName monitorName) { 84 mMonitorName = monitorName; 85 return *this; 86 } 87 88 // Friend access for VkmsTester to read the configuration 89 friend class VkmsTester; 90 91 private: 92 ConnectorType mType = ConnectorType::kDisplayPort; 93 bool mEnabledAtStart = true; 94 int mAdditionalOverlayPlanes = 0; 95 edid::MonitorName mMonitorName; 96 }; 97 98 /** 99 * Creates a VKMS configuration with a specified number of virtual displays, 100 * each with a default setup. 101 * 102 * Each connector is configured with: 103 * - 1 CRTC 104 * - 1 Encoder 105 * - 2 Planes: 1 Primary and 1 Cursor 106 * 107 * The first connector is set to eDP, and the remaining connectors are set to 108 * DisplayPort. 109 * 110 * @param displaysCount The number of virtual displays to configure. 111 * @return A unique pointer to the created VkmsTester instance, or nullptr if 112 * creation failed. 113 */ 114 static std::unique_ptr<VkmsTester> 115 CreateWithGenericConnectors(int displaysCount); 116 117 /** 118 * Creates a VKMS configuration based on a provided vector of connector 119 * builders. 120 * 121 * This method allows for fine-grained control over the configuration of each 122 * virtual display. Each builder in the vector defines a single connector and 123 * its associated configuration. 124 * 125 * @param builders A vector of VkmsConnectorBuilder objects, each defining the 126 * configuration for a single connector. The size of the vector determines the 127 * number of virtual displays to create. 128 * @return A unique pointer to the created VkmsTester instance, or nullptr if 129 * creation failed. 130 */ 131 static std::unique_ptr<VkmsTester> CreateWithBuilders( 132 const std::vector<VkmsConnectorBuilder>& builders); 133 134 static void ForceDeleteVkmsDir(); 135 136 ~VkmsTester(); 137 138 // Returns the number of connectors that have been successfully created 139 // regardless of their connection status. getActiveConnectorsCount()140 int getActiveConnectorsCount() const { return mActiveConnectorsCount; } 141 bool ToggleConnector(int connectorIndex, bool enable); 142 143 // Prevent the VkmsTester instance from cleaning up the VKMS directories upon 144 // destruction. 145 void DisableCleanupOnDestruction(); 146 147 private: 148 enum class DrmResource { 149 kConnector, 150 kCrtc, 151 kEncoder, 152 kPlane, 153 }; 154 155 // https://cs.android.com/android/platform/superproject/main/+/main:external/libdrm/xf86drmMode.h;l=225 156 enum class PlaneType { 157 kOverlay = 0, 158 kPrimary = 1, 159 kCursor = 2, 160 }; 161 162 // Create a map of the base directory for each resource type to maintain 163 // string consistency throughout the code. 164 const std::unordered_map<DrmResource, std::string> kDrmResourceBase = { 165 {DrmResource::kConnector, "connectors/CONNECTOR_"}, 166 {DrmResource::kCrtc, "crtcs/CRTC_"}, 167 {DrmResource::kEncoder, "encoders/ENCODER_"}, 168 {DrmResource::kPlane, "planes/PLANE_"}, 169 }; 170 171 // Private constructor to prevent direct instantiation without the Create 172 // functions. 173 explicit VkmsTester(size_t displaysCount, 174 const std::vector<VkmsConnectorBuilder>& builders = {}); 175 176 bool SetVkmsAsDisplayDriver(); 177 bool SetupDisplays(int displaysCount, 178 const std::vector<VkmsConnectorBuilder>& builders); 179 static bool ToggleVkms(bool enable); 180 static bool ToggleHwc3(bool enable); 181 182 bool CreateResource(DrmResource resource, int index); 183 bool SetConnectorStatus(int index, bool enable); 184 bool SetConnectorType(int index, ConnectorType type); 185 bool SetConnectorEdid(int index, edid::MonitorName monitorName); 186 bool SetPlaneType(int index, PlaneType type); 187 bool SetPlaneFormat(int index); 188 bool LinkToCrtc(DrmResource resource, int resourceIdx, int crtcIdx); 189 bool LinkConnectorToEncoder(int connectorIdx, int encoderIdx); 190 191 static void ShutdownAndCleanUpVkms(); 192 static void FindAndCleanupPossibleLinks(const std::string &dirPath); 193 static void CleanUpDirAndChildren(const std::string &rootDir); 194 195 size_t mActiveConnectorsCount = 0; 196 // Used to track the most recently created plane ID, as the number of planes 197 // can vary per connector. This value is updated whenever a new plane is 198 // created. 199 int mLatestPlaneId = 0; 200 201 bool mDisableCleanupOnDestruction = false; 202 bool mInitialized = false; 203 }; 204 205 } // namespace hcct