• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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