• 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 #include "edid_helper.h"
18 #include "vkms_tester.h"
19 #include <log/log.h>
20 #include <sstream>
21 #include <stdlib.h>
22 #include <string>
23 #include <unistd.h>
24 #include <vector>
25 
26 // clang-format off
27 /*
28  * A binary that turns on vkms and creates connectors.
29  *
30  * Usage:
31  *   Simple mode:
32  *     ./setup_vkms_connectors_for_atest <number_of_connectors>
33  *
34  *   Advanced mode:
35  *     ./setup_vkms_connectors_for_atest --config TYPE,NUMBER_OF_OVERLAY_PLANES[,EDID_NAME]
36  * TYPE,NUMBER_OF_OVERLAY_PLANES[,EDID_NAME] ...
37  *
38  *   Where:
39  *     TYPE = connector type (DP, HDMIA, HDMIB, eDP, DSI, VGA, VIRTUAL, etc.)
40  *     NUMBER_OF_OVERLAY_PLANES = number of additional overlay planes (integer)
41  *     EDID_NAME = optional EDID profile name (e.g., ACI_9713_ASUS_VE258_DP)
42  *
43  * Examples:
44  *   ./setup_vkms_connectors_for_atest 3                      # Creates 3 virtual connectors
45  *   ./setup_vkms_connectors_for_atest --config DP,2 HDMIA,1  # Creates 2 connectors with specific configs
46  *   ./setup_vkms_connectors_for_atest --config DP,2,ACI_9713_ASUS_VE258_DP HDMIA,1,ACI_9155_ASUS_VH238_HDMI
47  *      # Creates connectors with specific EDID profiles
48  *
49  * The binary will set up the vkms (virtual kernel mode setting) driver
50  * with the specified configuration. It disables cleanup on destruction
51  * so that the vkms setup persists after program termination.
52  */
53 // clang-format on
54 
stringToMonitorName(const std::string & edidName,hcct::edid::MonitorName & monitorName)55 bool stringToMonitorName(const std::string &edidName,
56                          hcct::edid::MonitorName &monitorName) {
57   if (edidName.empty()) {
58     return false;
59   }
60 
61   // Check if it's an eDP
62 #define CHECK_EDP_MONITOR(monitor)                                             \
63   if (edidName == #monitor) {                                                  \
64     monitorName =                                                              \
65         hcct::edid::MonitorName(hcct::edid::EdpMonitorName::monitor);          \
66     return true;                                                               \
67   }
68   // Generate checks for eDPs
69   EDP_MONITOR_LIST(CHECK_EDP_MONITOR)
70 
71   // Check if it's a DP monitor
72 #define CHECK_DP_MONITOR(monitor)                                              \
73   if (edidName == #monitor) {                                                  \
74     monitorName = hcct::edid::MonitorName(hcct::edid::DpMonitorName::monitor); \
75     return true;                                                               \
76   }
77   // Generate checks for all DP monitors
78   DP_MONITOR_LIST(CHECK_DP_MONITOR)
79 
80   // Check if it's an HDMI monitor
81 #define CHECK_HDMI_MONITOR(monitor)                                            \
82   if (edidName == #monitor) {                                                  \
83     monitorName =                                                              \
84         hcct::edid::MonitorName(hcct::edid::HdmiMonitorName::monitor);         \
85     return true;                                                               \
86   }
87   // Generate checks for all HDMI monitors
88   HDMI_MONITOR_LIST(CHECK_HDMI_MONITOR)
89 
90   return false;
91 }
92 
parseConnectorConfig(const std::string & configStr)93 hcct::VkmsTester::VkmsConnectorBuilder parseConnectorConfig(
94     const std::string &configStr) {
95   std::istringstream ss(configStr);
96   std::string typeStr, planesStr, edidStr;
97 
98   auto builder = hcct::VkmsTester::VkmsConnectorBuilder::create();
99 
100   // Parse type
101   if (std::getline(ss, typeStr, ',')) {
102     builder.withType(typeStr);
103   }
104 
105   // Parse additional planes
106   if (std::getline(ss, planesStr, ',')) {
107     char *endptr;
108     int planes = strtol(planesStr.c_str(), &endptr, 10);
109     if (*endptr != '\0' || planes < 0) {
110       ALOGE("Invalid number of planes: %s", planesStr.c_str());
111     } else {
112       builder.withAdditionalOverlayPlanes(planes);
113     }
114   }
115 
116   // Parse optional EDID name
117   if (std::getline(ss, edidStr, ',')) {
118     hcct::edid::MonitorName monitorName;
119     if (stringToMonitorName(edidStr, monitorName)) {
120       builder.withMonitor(monitorName);
121     } else {
122       ALOGE("Unknown EDID profile name: %s", edidStr.c_str());
123     }
124   }
125 
126   return builder;
127 }
128 
129 // Helper function to parse all connector configurations
parseConnectorConfigs(int argc,char * argv[],int startIndex)130 std::vector<hcct::VkmsTester::VkmsConnectorBuilder> parseConnectorConfigs(
131     int argc, char *argv[], int startIndex) {
132   std::vector<hcct::VkmsTester::VkmsConnectorBuilder> builders;
133 
134   for (int i = startIndex; i < argc; i++) {
135     std::string configStr(argv[i]);
136     builders.push_back(parseConnectorConfig(configStr));
137   }
138 
139   return builders;
140 }
141 
printUsage(const char * programName)142 void printUsage(const char *programName) {
143   // clang-format off
144   ALOGI("Usage:");
145   ALOGI("  Simple mode:   %s <number_of_connectors>", programName);
146   ALOGI("  Advanced mode: %s --config TYPE,NUMBER_OF_OVERLAY_PLANES[,EDID_NAME] TYPE,NUMBER_OF_OVERLAY_PLANES[,EDID_NAME] ...", programName);
147   ALOGI("  Where:");
148   ALOGI("    TYPE = connector type (DP, HDMIA, HDMIB, eDP, DSI, VGA, VIRTUAL, "
149         "WRITEBACK, DPI)");
150   ALOGI("    NUMBER_OF_OVERLAY_PLANES = number of additional overlay planes "
151         "(integer)");
152   ALOGI("Examples:");
153   ALOGI("  %s 3", programName);
154   ALOGI("  %s --config DP,2 HDMIA,1", programName);
155   ALOGI("  %s --config DP,2,ACI_9713_ASUS_VE258_DP HDMIA,1,ACI_9155_ASUS_VH238_HDMI", programName);
156   // clang-format on
157 }
158 
main(int argc,char * argv[])159 int main(int argc, char *argv[]) {
160   if (argc <= 1) {
161     ALOGE("Error: No arguments provided");
162     printUsage(argv[0]);
163     return -1;
164   }
165 
166   std::unique_ptr<hcct::VkmsTester> vkmsTester;
167 
168   // Check for advanced mode with --config flag
169   if (strcmp(argv[1], "--config") == 0) {
170     if (argc <= 2) {
171       ALOGE("Error: No configuration parameters provided after --config");
172       printUsage(argv[0]);
173       return -1;
174     }
175 
176     // Parse configs from multiple arguments starting from index 2
177     auto builders = parseConnectorConfigs(argc, argv, 2);
178     if (builders.empty()) {
179       ALOGE("Failed to parse connector configurations");
180       return -1;
181     }
182 
183     ALOGI("Setting up vkms with %zu custom connectors", builders.size());
184     vkmsTester = hcct::VkmsTester::CreateWithBuilders(builders);
185   } else {
186     // Simple mode - just a number of connectors
187     char *endptr;
188     int numConnectors = strtol(argv[1], &endptr, 10);
189 
190     if (*endptr != '\0' || numConnectors <= 0) {
191       ALOGE("Error: Invalid number of connectors: %s. Must be a positive "
192             "integer.",
193             argv[1]);
194       printUsage(argv[0]);
195       return -1;
196     }
197 
198     ALOGI("Setting up vkms with %d generic connectors", numConnectors);
199     vkmsTester = hcct::VkmsTester::CreateWithGenericConnectors(numConnectors);
200   }
201 
202   if (!vkmsTester) {
203     ALOGE("Failed to create VkmsTester");
204     return -1;
205   }
206 
207   vkmsTester->DisableCleanupOnDestruction();
208 
209   // Enable all connectors to run tests on them
210   for (int i = 0; i < vkmsTester->getActiveConnectorsCount(); i++) {
211     if (!vkmsTester->ToggleConnector(i, true)) {
212       ALOGE("Failed to enable connector %d", i);
213       return -1;
214     }
215   }
216 
217   return 0;
218 }