File indexing completed on 2024-11-10 04:55:54
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "mock_drm.h" 0010 0011 #include <errno.h> 0012 extern "C" { 0013 #include <libxcvt/libxcvt.h> 0014 } 0015 #include <math.h> 0016 0017 #include <memory> 0018 0019 #include <QMap> 0020 #include <QDebug> 0021 0022 // Mock impls 0023 0024 static QMap<int, MockGpu*> s_gpus; 0025 0026 static MockGpu *getGpu(int fd) 0027 { 0028 return s_gpus[fd]; 0029 } 0030 0031 MockGpu::MockGpu(int fd, const QString &devNode, int numCrtcs, int gammaSize) 0032 : fd(fd) 0033 , devNode(devNode) 0034 { 0035 s_gpus.insert(fd, this); 0036 for (int i = 0; i < numCrtcs; i++) { 0037 const auto &plane = std::make_shared<MockPlane>(this, PlaneType::Primary, i); 0038 crtcs << std::make_shared<MockCrtc>(this, plane, i, gammaSize); 0039 planes << plane; 0040 } 0041 deviceCaps.insert(MOCKDRM_DEVICE_CAP_ATOMIC, 1); 0042 deviceCaps.insert(DRM_CAP_DUMB_BUFFER, 1); 0043 deviceCaps.insert(DRM_CAP_PRIME, DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT); 0044 deviceCaps.insert(DRM_CAP_ASYNC_PAGE_FLIP, 0); 0045 deviceCaps.insert(DRM_CAP_ADDFB2_MODIFIERS, 1); 0046 } 0047 0048 MockGpu::~MockGpu() 0049 { 0050 s_gpus.remove(fd); 0051 } 0052 0053 MockPropertyBlob *MockGpu::getBlob(uint32_t id) const 0054 { 0055 auto it = std::find_if(propertyBlobs.begin(), propertyBlobs.end(), [id](const auto &propBlob) { 0056 return propBlob->id == id; 0057 }); 0058 return it == propertyBlobs.end() ? nullptr : it->get(); 0059 } 0060 0061 MockConnector *MockGpu::findConnector(uint32_t id) const 0062 { 0063 auto it = std::find_if(connectors.constBegin(), connectors.constEnd(), [id](const auto &c){return c->id == id;}); 0064 return it == connectors.constEnd() ? nullptr : (*it).get(); 0065 } 0066 0067 MockCrtc *MockGpu::findCrtc(uint32_t id) const 0068 { 0069 auto it = std::find_if(crtcs.constBegin(), crtcs.constEnd(), [id](const auto &c){return c->id == id;}); 0070 return it == crtcs.constEnd() ? nullptr : (*it).get(); 0071 } 0072 0073 MockPlane *MockGpu::findPlane(uint32_t id) const 0074 { 0075 auto it = std::find_if(planes.constBegin(), planes.constEnd(), [id](const auto &p){return p->id == id;}); 0076 return it == planes.constEnd() ? nullptr : (*it).get(); 0077 } 0078 0079 void MockGpu::flipPage(uint32_t crtcId) 0080 { 0081 auto crtc = findCrtc(crtcId); 0082 Q_ASSERT(crtc); 0083 for (const auto &plane : std::as_const(planes)) { 0084 if (plane->getProp(QStringLiteral("CRTC_ID")) == crtc->id) { 0085 plane->currentFb = plane->nextFb; 0086 } 0087 } 0088 // TODO page flip event? 0089 } 0090 0091 // 0092 0093 MockObject::MockObject(MockGpu *gpu) 0094 : id(gpu->idCounter++) 0095 , gpu(gpu) 0096 { 0097 gpu->objects << this; 0098 } 0099 0100 MockObject::~MockObject() 0101 { 0102 gpu->objects.removeOne(this); 0103 } 0104 0105 uint64_t MockObject::getProp(const QString &propName) const 0106 { 0107 for (const auto &prop : std::as_const(props)) { 0108 if (prop.name == propName) { 0109 return prop.value; 0110 } 0111 } 0112 Q_UNREACHABLE(); 0113 } 0114 0115 void MockObject::setProp(const QString &propName, uint64_t value) 0116 { 0117 for (auto &prop : props) { 0118 if (prop.name == propName) { 0119 prop.value = value; 0120 return; 0121 } 0122 } 0123 Q_UNREACHABLE(); 0124 } 0125 0126 uint32_t MockObject::getPropId(const QString &propName) const 0127 { 0128 for (const auto &prop : std::as_const(props)) { 0129 if (prop.name == propName) { 0130 return prop.id; 0131 } 0132 } 0133 Q_UNREACHABLE(); 0134 } 0135 0136 // 0137 0138 MockProperty::MockProperty(MockObject *obj, QString name, uint64_t initialValue, uint32_t flags, QList<QByteArray> enums) 0139 : obj(obj) 0140 , id(obj->gpu->idCounter++) 0141 , flags(flags) 0142 , name(name) 0143 , value(initialValue) 0144 , enums(enums) 0145 { 0146 qDebug("Added property %s with id %u to object %u", qPrintable(name), id, obj->id); 0147 } 0148 0149 MockPropertyBlob::MockPropertyBlob(MockGpu *gpu, const void *d, size_t size) 0150 : gpu(gpu) 0151 , id(gpu->idCounter++) 0152 , data(malloc(size)) 0153 , size(size) 0154 { 0155 memcpy(data, d, size); 0156 } 0157 0158 MockPropertyBlob::~MockPropertyBlob() 0159 { 0160 free(data); 0161 } 0162 0163 // 0164 0165 #define addProp(name, value, flags) props << MockProperty(this, QStringLiteral(name), value, flags) 0166 0167 MockConnector::MockConnector(MockGpu *gpu, bool nonDesktop) 0168 : MockObject(gpu) 0169 , connection(DRM_MODE_CONNECTED) 0170 , type(DRM_MODE_CONNECTOR_DisplayPort) 0171 , encoder(std::make_shared<MockEncoder>(gpu, 0xFF)) 0172 { 0173 gpu->encoders << encoder; 0174 addProp("CRTC_ID", 0, DRM_MODE_PROP_ATOMIC); 0175 0176 addProp("Subpixel", DRM_MODE_SUBPIXEL_UNKNOWN, DRM_MODE_PROP_IMMUTABLE); 0177 addProp("non-desktop", nonDesktop, DRM_MODE_PROP_IMMUTABLE); 0178 addProp("vrr_capable", 0, DRM_MODE_PROP_IMMUTABLE); 0179 0180 addProp("DPMS", DRM_MODE_DPMS_OFF, 0); 0181 addProp("EDID", 0, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE); 0182 0183 addMode(1920, 1080, 60.0); 0184 } 0185 0186 void MockConnector::addMode(uint32_t width, uint32_t height, float refreshRate, bool preferred) 0187 { 0188 auto modeInfo = libxcvt_gen_mode_info(width, height, refreshRate, false, false); 0189 0190 drmModeModeInfo mode{ 0191 .clock = uint32_t(modeInfo->dot_clock), 0192 .hdisplay = uint16_t(modeInfo->hdisplay), 0193 .hsync_start = modeInfo->hsync_start, 0194 .hsync_end = modeInfo->hsync_end, 0195 .htotal = modeInfo->htotal, 0196 .hskew = 0, 0197 .vdisplay = uint16_t(modeInfo->vdisplay), 0198 .vsync_start = modeInfo->vsync_start, 0199 .vsync_end = modeInfo->vsync_end, 0200 .vtotal = modeInfo->vtotal, 0201 .vscan = 1, 0202 .vrefresh = uint32_t(modeInfo->vrefresh), 0203 .flags = modeInfo->mode_flags, 0204 .type = DRM_MODE_TYPE_DRIVER, 0205 }; 0206 if (preferred) { 0207 mode.type |= DRM_MODE_TYPE_PREFERRED; 0208 } 0209 sprintf(mode.name, "%dx%d@%d", width, height, mode.vrefresh); 0210 0211 modes.push_back(mode); 0212 free(modeInfo); 0213 } 0214 0215 // 0216 0217 MockCrtc::MockCrtc(MockGpu *gpu, const std::shared_ptr<MockPlane> &legacyPlane, int pipeIndex, int gamma_size) 0218 : MockObject(gpu) 0219 , pipeIndex(pipeIndex) 0220 , gamma_size(gamma_size) 0221 , legacyPlane(legacyPlane) 0222 { 0223 addProp("MODE_ID", 0, DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB); 0224 addProp("ACTIVE", 0, DRM_MODE_PROP_ATOMIC); 0225 addProp("GAMMA_LUT", 0, DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB); 0226 addProp("GAMMA_LUT_SIZE", gamma_size, DRM_MODE_PROP_ATOMIC); 0227 } 0228 0229 0230 // 0231 0232 MockPlane::MockPlane(MockGpu *gpu, PlaneType type, int crtcIndex) 0233 : MockObject(gpu) 0234 , possibleCrtcs(1 << crtcIndex) 0235 , type(type) 0236 { 0237 props << MockProperty(this, QStringLiteral("type"), static_cast<uint64_t>(type), DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ENUM, 0238 {QByteArrayLiteral("Primary"), QByteArrayLiteral("Overlay"), QByteArrayLiteral("Cursor")}); 0239 addProp("FB_ID", 0, DRM_MODE_PROP_ATOMIC); 0240 addProp("CRTC_ID", 0, DRM_MODE_PROP_ATOMIC); 0241 addProp("CRTC_X", 0, DRM_MODE_PROP_ATOMIC); 0242 addProp("CRTC_Y", 0, DRM_MODE_PROP_ATOMIC); 0243 addProp("CRTC_W", 0, DRM_MODE_PROP_ATOMIC); 0244 addProp("CRTC_H", 0, DRM_MODE_PROP_ATOMIC); 0245 addProp("SRC_X", 0, DRM_MODE_PROP_ATOMIC); 0246 addProp("SRC_Y", 0, DRM_MODE_PROP_ATOMIC); 0247 addProp("SRC_W", 0, DRM_MODE_PROP_ATOMIC); 0248 addProp("SRC_H", 0, DRM_MODE_PROP_ATOMIC); 0249 } 0250 0251 // 0252 0253 MockEncoder::MockEncoder(MockGpu* gpu, uint32_t possible_crtcs) 0254 : MockObject(gpu) 0255 , possible_crtcs(possible_crtcs) 0256 { 0257 } 0258 0259 0260 // 0261 0262 MockFb::MockFb(MockGpu *gpu, uint32_t width, uint32_t height) 0263 : id(gpu->idCounter++) 0264 , width(width) 0265 , height(height) 0266 , gpu(gpu) 0267 { 0268 gpu->fbs << this; 0269 } 0270 0271 MockFb::~MockFb() 0272 { 0273 gpu->fbs.removeOne(this); 0274 } 0275 0276 // drm functions 0277 0278 #define GPU(fd, error) \ 0279 auto gpu = getGpu(fd); \ 0280 if (!gpu) { \ 0281 qWarning("invalid fd %d", fd); \ 0282 errno = EINVAL; \ 0283 return error; \ 0284 } \ 0285 std::scoped_lock lock(gpu->m_mutex); 0286 0287 drmVersionPtr drmGetVersion(int fd) 0288 { 0289 GPU(fd, nullptr); 0290 drmVersionPtr ptr = new drmVersion; 0291 ptr->name = new char[gpu->name.size() + 1]; 0292 strcpy(ptr->name, gpu->name.data()); 0293 return ptr; 0294 } 0295 0296 void drmFreeVersion(drmVersionPtr ptr) 0297 { 0298 Q_ASSERT(ptr); 0299 delete[] ptr->name; 0300 delete ptr; 0301 } 0302 0303 int drmSetClientCap(int fd, uint64_t capability, uint64_t value) 0304 { 0305 GPU(fd, -EINVAL); 0306 if (capability == DRM_CLIENT_CAP_ATOMIC) { 0307 if (!gpu->deviceCaps[MOCKDRM_DEVICE_CAP_ATOMIC]) { 0308 return -(errno = ENOTSUP); 0309 } 0310 qDebug("Setting DRM_CLIENT_CAP_ATOMIC to %lu", value); 0311 } 0312 gpu->clientCaps.insert(capability, value); 0313 return 0; 0314 } 0315 0316 int drmGetCap(int fd, uint64_t capability, uint64_t *value) 0317 { 0318 GPU(fd, -EINVAL); 0319 if (gpu->deviceCaps.contains(capability)) { 0320 *value = gpu->deviceCaps[capability]; 0321 return 0; 0322 } 0323 qDebug("Could not find capability %lu", capability); 0324 return -(errno = EINVAL); 0325 } 0326 0327 int drmHandleEvent(int fd, drmEventContextPtr evctx) 0328 { 0329 GPU(fd, -EINVAL); 0330 return -(errno = ENOTSUP); 0331 } 0332 0333 int drmIoctl(int fd, unsigned long request, void *arg) 0334 { 0335 if (request == DRM_IOCTL_PRIME_FD_TO_HANDLE) { 0336 GPU(fd, -EINVAL); 0337 auto args = static_cast<drm_prime_handle *>(arg); 0338 args->handle = 42; // just pass a dummy value so the request doesn't fail 0339 return 0; 0340 } else if (request == DRM_IOCTL_PRIME_HANDLE_TO_FD) { 0341 return -(errno = ENOTSUP); 0342 } else if (request == DRM_IOCTL_GEM_CLOSE) { 0343 GPU(fd, -EINVAL); 0344 return 0; 0345 } else if (request == DRM_IOCTL_MODE_ATOMIC) { 0346 const auto args = static_cast<drm_mode_atomic *>(arg); 0347 auto req = drmModeAtomicAlloc(); 0348 const uint32_t *const objects = reinterpret_cast<const uint32_t *>(args->objs_ptr); 0349 const uint32_t *const propsCounts = reinterpret_cast<const uint32_t *>(args->count_props_ptr); 0350 const uint32_t *const props = reinterpret_cast<const uint32_t *>(args->props_ptr); 0351 const uint64_t *const values = reinterpret_cast<const uint64_t *>(args->prop_values_ptr); 0352 uint32_t propIndex = 0; 0353 for (uint32_t objIndex = 0; objIndex < args->count_objs; objIndex++) { 0354 const uint32_t objectId = objects[objIndex]; 0355 const uint32_t count = propsCounts[objIndex]; 0356 for (uint32_t i = 0; i < count; i++) { 0357 drmModeAtomicAddProperty(req, objectId, props[propIndex + i], values[propIndex + i]); 0358 } 0359 propIndex += count; 0360 } 0361 int ret = drmModeAtomicCommit(fd, req, args->flags, reinterpret_cast<void *>(args->user_data)); 0362 drmModeAtomicFree(req); 0363 return ret; 0364 } else if (request == DRM_IOCTL_MODE_RMFB) { 0365 drmModeRmFB(fd, *static_cast<const uint32_t *>(arg)); 0366 } 0367 return -(errno = ENOTSUP); 0368 } 0369 0370 drmModeResPtr drmModeGetResources(int fd) 0371 { 0372 GPU(fd, nullptr) 0373 drmModeResPtr res = new drmModeRes; 0374 0375 res->count_connectors = gpu->connectors.count(); 0376 res->connectors = res->count_connectors ? new uint32_t[res->count_connectors] : nullptr; 0377 int i = 0; 0378 for (const auto &conn : std::as_const(gpu->connectors)) { 0379 res->connectors[i++] = conn->id; 0380 } 0381 0382 res->count_encoders = gpu->encoders.count(); 0383 res->encoders = res->count_encoders ? new uint32_t[res->count_encoders] : nullptr; 0384 i = 0; 0385 for (const auto &enc : std::as_const(gpu->encoders)) { 0386 res->encoders[i++] = enc->id; 0387 } 0388 0389 res->count_crtcs = gpu->crtcs.count(); 0390 res->crtcs = res->count_crtcs ? new uint32_t[res->count_crtcs] : nullptr; 0391 i = 0; 0392 for (const auto &crtc : std::as_const(gpu->crtcs)) { 0393 res->crtcs[i++] = crtc->id; 0394 } 0395 0396 res->count_fbs = gpu->fbs.count(); 0397 res->fbs = res->count_fbs ? new uint32_t[res->count_fbs] : nullptr; 0398 i = 0; 0399 for (const auto &fb : std::as_const(gpu->fbs)) { 0400 res->fbs[i++] = fb->id; 0401 } 0402 0403 res->min_width = 0; 0404 res->min_height = 0; 0405 res->max_width = 2 << 14; 0406 res->max_height = 2 << 14; 0407 0408 gpu->resPtrs << res; 0409 return res; 0410 } 0411 0412 int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth, 0413 uint8_t bpp, uint32_t pitch, uint32_t bo_handle, 0414 uint32_t *buf_id) 0415 { 0416 GPU(fd, EINVAL) 0417 auto fb = new MockFb(gpu, width, height); 0418 *buf_id = fb->id; 0419 return 0; 0420 } 0421 0422 int drmModeAddFB2(int fd, uint32_t width, uint32_t height, 0423 uint32_t pixel_format, const uint32_t bo_handles[4], 0424 const uint32_t pitches[4], const uint32_t offsets[4], 0425 uint32_t *buf_id, uint32_t flags) 0426 { 0427 GPU(fd, EINVAL) 0428 auto fb = new MockFb(gpu, width, height); 0429 *buf_id = fb->id; 0430 return 0; 0431 } 0432 0433 int drmModeAddFB2WithModifiers(int fd, uint32_t width, uint32_t height, 0434 uint32_t pixel_format, const uint32_t bo_handles[4], 0435 const uint32_t pitches[4], const uint32_t offsets[4], 0436 const uint64_t modifier[4], uint32_t *buf_id, 0437 uint32_t flags) 0438 { 0439 GPU(fd, EINVAL) 0440 if (!gpu->deviceCaps.contains(DRM_CAP_ADDFB2_MODIFIERS)) { 0441 return -(errno = ENOTSUP); 0442 } 0443 auto fb = new MockFb(gpu, width, height); 0444 *buf_id = fb->id; 0445 return 0; 0446 } 0447 0448 int drmModeRmFB(int fd, uint32_t bufferId) 0449 { 0450 GPU(fd, EINVAL) 0451 auto it = std::find_if(gpu->fbs.begin(), gpu->fbs.end(), [bufferId](const auto &fb){return fb->id == bufferId;}); 0452 if (it == gpu->fbs.end()) { 0453 qWarning("invalid bufferId %u passed to drmModeRmFB", bufferId); 0454 return EINVAL; 0455 } else { 0456 auto fb = *it; 0457 gpu->fbs.erase(it); 0458 for (const auto &plane : std::as_const(gpu->planes)) { 0459 if (plane->nextFb == fb) { 0460 plane->nextFb = nullptr; 0461 } 0462 if (plane->currentFb == fb) { 0463 qWarning("current fb %u of plane %u got removed. Deactivating plane", bufferId, plane->id); 0464 plane->setProp(QStringLiteral("CRTC_ID"), 0); 0465 plane->setProp(QStringLiteral("FB_ID"), 0); 0466 plane->currentFb = nullptr; 0467 0468 auto crtc = gpu->findCrtc(plane->getProp(QStringLiteral("CRTC_ID"))); 0469 Q_ASSERT(crtc); 0470 crtc->setProp(QStringLiteral("ACTIVE"), 0); 0471 qWarning("deactvating crtc %u", crtc->id); 0472 0473 for (const auto &conn : std::as_const(gpu->connectors)) { 0474 if (conn->getProp(QStringLiteral("CRTC_ID")) == crtc->id) { 0475 conn->setProp(QStringLiteral("CRTC_ID"), 0); 0476 qWarning("deactvating connector %u", conn->id); 0477 } 0478 } 0479 } 0480 } 0481 delete fb; 0482 return 0; 0483 } 0484 } 0485 0486 drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId) 0487 { 0488 GPU(fd, nullptr); 0489 if (auto crtc = gpu->findCrtc(crtcId)) { 0490 drmModeCrtcPtr c = new drmModeCrtc; 0491 c->crtc_id = crtcId; 0492 c->buffer_id = crtc->currentFb ? crtc->currentFb->id : 0; 0493 c->gamma_size = crtc->gamma_size; 0494 c->mode_valid = crtc->modeValid; 0495 c->mode = crtc->mode; 0496 c->x = 0; 0497 c->y = 0; 0498 c->width = crtc->mode.hdisplay; 0499 c->height = crtc->mode.vdisplay; 0500 gpu->drmCrtcs << c; 0501 return c; 0502 } else { 0503 qWarning("invalid crtcId %u passed to drmModeGetCrtc", crtcId); 0504 errno = EINVAL; 0505 return nullptr; 0506 } 0507 } 0508 0509 int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId, 0510 uint32_t x, uint32_t y, uint32_t *connectors, int count, 0511 drmModeModeInfoPtr mode) 0512 { 0513 GPU(fd, -EINVAL); 0514 auto crtc = gpu->findCrtc(crtcId); 0515 if (!crtc) { 0516 qWarning("invalid crtcId %u passed to drmModeSetCrtc", crtcId); 0517 return -(errno = EINVAL); 0518 } 0519 auto oldModeBlob = crtc->getProp(QStringLiteral("MODE_ID")); 0520 uint32_t modeBlob = 0; 0521 if (mode) { 0522 drmModeCreatePropertyBlob(fd, mode, sizeof(drmModeModeInfo), &modeBlob); 0523 } 0524 0525 auto req = drmModeAtomicAlloc(); 0526 req->legacyEmulation = true; 0527 drmModeAtomicAddProperty(req, crtcId, crtc->getPropId(QStringLiteral("MODE_ID")), modeBlob); 0528 drmModeAtomicAddProperty(req, crtcId, crtc->getPropId(QStringLiteral("ACTIVE")), modeBlob && count); 0529 QList<uint32_t> conns; 0530 for (int i = 0; i < count; i++) { 0531 conns << connectors[i]; 0532 } 0533 for (const auto &conn : std::as_const(gpu->connectors)) { 0534 if (conns.contains(conn->id)) { 0535 drmModeAtomicAddProperty(req, conn->id, conn->getPropId(QStringLiteral("CRTC_ID")), modeBlob ? crtc->id : 0); 0536 conns.removeOne(conn->id); 0537 } else if (conn->getProp(QStringLiteral("CRTC_ID")) == crtc->id) { 0538 drmModeAtomicAddProperty(req, conn->id, conn->getPropId(QStringLiteral("CRTC_ID")), 0); 0539 } 0540 } 0541 if (!conns.isEmpty()) { 0542 for (const auto &c : std::as_const(conns)) { 0543 qWarning("invalid connector %u passed to drmModeSetCrtc", c); 0544 } 0545 drmModeAtomicFree(req); 0546 return -(errno = EINVAL); 0547 } 0548 drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_ID")), modeBlob && count ? crtc->id : 0); 0549 drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_X")), x); 0550 drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_Y")), y); 0551 drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_W")), mode->hdisplay - x); 0552 drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("CRTC_H")), mode->vdisplay - y); 0553 drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("FB_ID")), bufferId); 0554 int result = drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, nullptr); 0555 drmModeAtomicFree(req); 0556 if (result == 0) { 0557 drmModeDestroyPropertyBlob(fd, oldModeBlob); 0558 } 0559 return result; 0560 } 0561 0562 int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height) 0563 { 0564 GPU(fd, -EINVAL); 0565 if (auto crtc = gpu->findCrtc(crtcId)) { 0566 crtc->cursorRect.setSize(QSize(width, height)); 0567 return 0; 0568 } else { 0569 qWarning("invalid crtcId %u passed to drmModeSetCursor", crtcId); 0570 return -(errno = EINVAL); 0571 } 0572 } 0573 0574 int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y) 0575 { 0576 GPU(fd, -EINVAL); 0577 return -(errno = ENOTSUP); 0578 } 0579 0580 int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y) 0581 { 0582 GPU(fd, -EINVAL); 0583 if (auto crtc = gpu->findCrtc(crtcId)) { 0584 crtc->cursorRect.moveTo(x, y); 0585 return 0; 0586 } else { 0587 qWarning("invalid crtcId %u passed to drmModeMoveCursor", crtcId); 0588 return -(errno = EINVAL); 0589 } 0590 } 0591 0592 drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id) 0593 { 0594 GPU(fd, nullptr); 0595 auto it = std::find_if(gpu->encoders.constBegin(), gpu->encoders.constEnd(), [encoder_id](const auto &e){return e->id == encoder_id;}); 0596 if (it == gpu->encoders.constEnd()) { 0597 qWarning("invalid encoder_id %u passed to drmModeGetEncoder", encoder_id); 0598 errno = EINVAL; 0599 return nullptr; 0600 } else { 0601 auto encoder = *it; 0602 drmModeEncoderPtr enc = new drmModeEncoder; 0603 enc->encoder_id = encoder_id; 0604 enc->crtc_id = encoder->crtc ? encoder->crtc->id : 0; 0605 enc->encoder_type = 0; 0606 enc->possible_crtcs = encoder->possible_crtcs; 0607 enc->possible_clones = encoder->possible_clones; 0608 0609 gpu->drmEncoders << enc; 0610 return enc; 0611 } 0612 } 0613 0614 // Instance ID of (some) specific connector type, incremented 0615 // for each new connector (of any type) being created. 0616 // There are no particular guarantees on the _stability_ of 0617 // connector type "instance IDs" issued by the kernel, 0618 // so simply giving each (new) connector a fresh ID is 0619 // acceptable. 0620 static std::atomic<int> autoIncrementedConnectorId{}; 0621 0622 drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connectorId) 0623 { 0624 GPU(fd, nullptr); 0625 if (auto conn = gpu->findConnector(connectorId)) { 0626 drmModeConnectorPtr c = new drmModeConnector{}; 0627 c->connector_id = conn->id; 0628 c->connection = conn->connection; 0629 0630 c->connector_type = conn->type; 0631 c->connector_type_id = autoIncrementedConnectorId++; 0632 0633 c->encoder_id = conn->encoder ? conn->encoder->id : 0; 0634 c->count_encoders = conn->encoder ? 1 : 0; 0635 c->encoders = c->count_encoders ? new uint32_t[1] : nullptr; 0636 if (c->encoders) { 0637 c->encoders[0] = conn->encoder->id; 0638 } 0639 c->count_modes = conn->modes.count(); 0640 c->modes = c->count_modes ? new drmModeModeInfo[c->count_modes] : nullptr; 0641 for (int i = 0; i < c->count_modes; i++) { 0642 c->modes[i] = conn->modes[i]; 0643 } 0644 c->mmHeight = 900; 0645 c->mmWidth = 1600; 0646 c->subpixel = DRM_MODE_SUBPIXEL_HORIZONTAL_RGB; 0647 0648 // these are not used nor will they be 0649 c->count_props = -1; 0650 c->props = nullptr; 0651 c->prop_values = nullptr; 0652 0653 gpu->drmConnectors << c; 0654 return c; 0655 } else { 0656 qWarning("invalid connectorId %u passed to drmModeGetConnector", connectorId); 0657 errno = EINVAL; 0658 return nullptr; 0659 } 0660 } 0661 0662 drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, uint32_t connector_id) 0663 { 0664 return drmModeGetConnector(fd, connector_id); 0665 } 0666 0667 int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size, uint16_t *red, uint16_t *green, uint16_t *blue) 0668 { 0669 return -(errno = ENOTSUP); 0670 } 0671 0672 int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id, uint32_t flags, void *user_data) 0673 { 0674 GPU(fd, -EINVAL); 0675 auto crtc = gpu->findCrtc(crtc_id); 0676 if (!crtc) { 0677 qWarning("invalid crtc_id %u passed to drmModePageFlip", crtc_id); 0678 return -(errno = EINVAL); 0679 } 0680 auto req = drmModeAtomicAlloc(); 0681 req->legacyEmulation = true; 0682 drmModeAtomicAddProperty(req, crtc->legacyPlane->id, crtc->legacyPlane->getPropId(QStringLiteral("FB_ID")), fb_id); 0683 int result = drmModeAtomicCommit(fd, req, flags, user_data); 0684 drmModeAtomicFree(req); 0685 return result; 0686 } 0687 0688 0689 drmModePlaneResPtr drmModeGetPlaneResources(int fd) 0690 { 0691 GPU(fd, nullptr); 0692 drmModePlaneResPtr res = new drmModePlaneRes; 0693 res->count_planes = gpu->planes.count(); 0694 res->planes = res->count_planes ? new uint32_t[res->count_planes] : nullptr; 0695 for (uint i = 0; i < res->count_planes; i++) { 0696 res->planes[i] = gpu->planes[i]->id; 0697 } 0698 gpu->drmPlaneRes << res; 0699 return res; 0700 } 0701 0702 drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id) 0703 { 0704 GPU(fd, nullptr); 0705 if (auto plane = gpu->findPlane(plane_id)) { 0706 drmModePlanePtr p = new drmModePlane; 0707 p->plane_id = plane_id; 0708 p->crtc_id = plane->getProp(QStringLiteral("CRTC_ID")); 0709 p->crtc_x = plane->getProp(QStringLiteral("CRTC_X")); 0710 p->crtc_y = plane->getProp(QStringLiteral("CRTC_Y")); 0711 p->fb_id = plane->getProp(QStringLiteral("FB_ID")); 0712 p->x = plane->getProp(QStringLiteral("SRC_X")); 0713 p->y = plane->getProp(QStringLiteral("SRC_Y")); 0714 p->possible_crtcs = plane->possibleCrtcs; 0715 0716 // unused atm: 0717 p->count_formats = 0; 0718 p->formats = nullptr; 0719 p->gamma_size = 0; 0720 0721 gpu->drmPlanes << p; 0722 return p; 0723 } else { 0724 qWarning("invalid plane_id %u passed to drmModeGetPlane", plane_id); 0725 errno = EINVAL; 0726 return nullptr; 0727 } 0728 } 0729 0730 drmModePropertyPtr drmModeGetProperty(int fd, uint32_t propertyId) 0731 { 0732 GPU(fd, nullptr); 0733 for (const auto &obj : std::as_const(gpu->objects)) { 0734 for (auto &prop : std::as_const(obj->props)) { 0735 if (prop.id == propertyId) { 0736 drmModePropertyPtr p = new drmModePropertyRes; 0737 p->prop_id = prop.id; 0738 p->flags = prop.flags; 0739 auto arr = prop.name.toLocal8Bit(); 0740 strcpy(p->name, arr.constData()); 0741 0742 p->count_blobs = prop.flags & DRM_MODE_PROP_BLOB ? 1 : 0; 0743 if (p->count_blobs) { 0744 p->blob_ids = new uint32_t[1]; 0745 p->blob_ids[0] = prop.value; 0746 } else { 0747 p->blob_ids = nullptr; 0748 } 0749 0750 p->count_enums = prop.enums.count(); 0751 p->enums = new drm_mode_property_enum[p->count_enums]; 0752 for (int i = 0; i < p->count_enums; i++) { 0753 strcpy(p->enums[i].name, prop.enums[i].constData()); 0754 p->enums[i].value = i; 0755 } 0756 0757 p->count_values = 1; 0758 p->values = new uint64_t[1]; 0759 p->values[0] = prop.value; 0760 0761 gpu->drmProps << p; 0762 return p; 0763 } 0764 } 0765 } 0766 qWarning("invalid propertyId %u passed to drmModeGetProperty", propertyId); 0767 errno = EINVAL; 0768 return nullptr; 0769 } 0770 0771 void drmModeFreeProperty(drmModePropertyPtr ptr) 0772 { 0773 if (!ptr) { 0774 return; 0775 } 0776 for (const auto &gpu : std::as_const(s_gpus)) { 0777 if (gpu->drmProps.removeOne(ptr)) { 0778 delete[] ptr->values; 0779 delete[] ptr->blob_ids; 0780 delete[] ptr->enums; 0781 delete ptr; 0782 return; 0783 } 0784 } 0785 } 0786 0787 0788 0789 drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id) 0790 { 0791 GPU(fd, nullptr); 0792 if (blob_id == 0) { 0793 return nullptr; 0794 } 0795 auto it = std::find_if(gpu->propertyBlobs.begin(), gpu->propertyBlobs.end(), [blob_id](const auto &blob) { 0796 return blob->id == blob_id; 0797 }); 0798 if (it == gpu->propertyBlobs.end()) { 0799 qWarning("invalid blob_id %u passed to drmModeGetPropertyBlob", blob_id); 0800 errno = EINVAL; 0801 return nullptr; 0802 } else { 0803 auto blob = new drmModePropertyBlobRes; 0804 blob->id = (*it)->id; 0805 blob->length = (*it)->size; 0806 blob->data = malloc(blob->length); 0807 memcpy(blob->data, (*it)->data, blob->length); 0808 return blob; 0809 } 0810 } 0811 0812 void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr) 0813 { 0814 if (!ptr) { 0815 return; 0816 } 0817 for (const auto &gpu : std::as_const(s_gpus)) { 0818 if (gpu->drmPropertyBlobs.removeOne(ptr)) { 0819 free(ptr->data); 0820 delete ptr; 0821 return; 0822 } 0823 } 0824 } 0825 0826 int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id, uint64_t value) 0827 { 0828 return drmModeObjectSetProperty(fd, connector_id, DRM_MODE_OBJECT_CONNECTOR, property_id, value); 0829 } 0830 0831 static uint32_t getType(MockObject *obj) 0832 { 0833 if (dynamic_cast<MockConnector*>(obj)) { 0834 return DRM_MODE_OBJECT_CONNECTOR; 0835 } else if (dynamic_cast<MockCrtc*>(obj)) { 0836 return DRM_MODE_OBJECT_CRTC; 0837 } else if (dynamic_cast<MockPlane*>(obj)) { 0838 return DRM_MODE_OBJECT_PLANE; 0839 } else { 0840 return DRM_MODE_OBJECT_ANY; 0841 } 0842 } 0843 0844 drmModeObjectPropertiesPtr drmModeObjectGetProperties(int fd, uint32_t object_id, uint32_t object_type) 0845 { 0846 GPU(fd, nullptr); 0847 auto it = std::find_if(gpu->objects.constBegin(), gpu->objects.constEnd(), [object_id](const auto &obj){return obj->id == object_id;}); 0848 if (it == gpu->objects.constEnd()) { 0849 qWarning("invalid object_id %u passed to drmModeObjectGetProperties", object_id); 0850 errno = EINVAL; 0851 return nullptr; 0852 } else { 0853 auto obj = *it; 0854 if (auto type = getType(obj); type != object_type) { 0855 qWarning("wrong object_type %u passed to drmModeObjectGetProperties for object %u with type %u", object_type, object_id, type); 0856 errno = EINVAL; 0857 return nullptr; 0858 } 0859 QList<MockProperty> props; 0860 bool deviceAtomic = gpu->clientCaps.contains(DRM_CLIENT_CAP_ATOMIC) && gpu->clientCaps[DRM_CLIENT_CAP_ATOMIC]; 0861 for (const auto &prop : std::as_const(obj->props)) { 0862 if (deviceAtomic || !(prop.flags & DRM_MODE_PROP_ATOMIC)) { 0863 props << prop; 0864 } 0865 } 0866 drmModeObjectPropertiesPtr p = new drmModeObjectProperties; 0867 p->count_props = props.count(); 0868 p->props = new uint32_t[p->count_props]; 0869 p->prop_values = new uint64_t[p->count_props]; 0870 int i = 0; 0871 for (const auto &prop : std::as_const(props)) { 0872 p->props[i] = prop.id; 0873 p->prop_values[i] = prop.value; 0874 i++; 0875 } 0876 gpu->drmObjectProperties << p; 0877 return p; 0878 } 0879 } 0880 0881 void drmModeFreeObjectProperties(drmModeObjectPropertiesPtr ptr) 0882 { 0883 for (const auto &gpu : std::as_const(s_gpus)) { 0884 if (gpu->drmObjectProperties.removeOne(ptr)) { 0885 delete[] ptr->props; 0886 delete[] ptr->prop_values; 0887 delete ptr; 0888 return; 0889 } 0890 } 0891 } 0892 0893 int drmModeObjectSetProperty(int fd, uint32_t object_id, uint32_t object_type, uint32_t property_id, uint64_t value) 0894 { 0895 GPU(fd, -EINVAL); 0896 auto it = std::find_if(gpu->objects.constBegin(), gpu->objects.constEnd(), [object_id](const auto &obj){return obj->id == object_id;}); 0897 if (it == gpu->objects.constEnd()) { 0898 qWarning("invalid object_id %u passed to drmModeObjectSetProperty", object_id); 0899 return -(errno = EINVAL); 0900 } else { 0901 auto obj = *it; 0902 if (auto type = getType(obj); type != object_type) { 0903 qWarning("wrong object_type %u passed to drmModeObjectSetProperty for object %u with type %u", object_type, object_id, type); 0904 return -(errno = EINVAL); 0905 } 0906 auto req = drmModeAtomicAlloc(); 0907 req->legacyEmulation = true; 0908 drmModeAtomicAddProperty(req, object_id, property_id, value); 0909 int result = drmModeAtomicCommit(fd, req, 0, nullptr); 0910 drmModeAtomicFree(req); 0911 return result; 0912 } 0913 } 0914 0915 static QList<drmModeAtomicReqPtr> s_atomicReqs; 0916 0917 drmModeAtomicReqPtr drmModeAtomicAlloc(void) 0918 { 0919 auto req = new drmModeAtomicReq; 0920 s_atomicReqs << req; 0921 return req; 0922 } 0923 0924 void drmModeAtomicFree(drmModeAtomicReqPtr req) 0925 { 0926 s_atomicReqs.removeOne(req); 0927 delete req; 0928 } 0929 0930 int drmModeAtomicAddProperty(drmModeAtomicReqPtr req, uint32_t object_id, uint32_t property_id, uint64_t value) 0931 { 0932 if (!req) { 0933 return -(errno = EINVAL); 0934 } 0935 Prop p; 0936 p.obj = object_id; 0937 p.prop = property_id; 0938 p.value = value; 0939 req->props << p; 0940 return req->props.count(); 0941 } 0942 0943 static bool checkIfEqual(const drmModeModeInfo &one, const drmModeModeInfo &two) 0944 { 0945 return one.clock == two.clock 0946 && one.hdisplay == two.hdisplay 0947 && one.hsync_start == two.hsync_start 0948 && one.hsync_end == two.hsync_end 0949 && one.htotal == two.htotal 0950 && one.hskew == two.hskew 0951 && one.vdisplay == two.vdisplay 0952 && one.vsync_start == two.vsync_start 0953 && one.vsync_end == two.vsync_end 0954 && one.vtotal == two.vtotal 0955 && one.vscan == two.vscan 0956 && one.vrefresh == two.vrefresh; 0957 } 0958 0959 int drmModeAtomicCommit(int fd, drmModeAtomicReqPtr req, uint32_t flags, void *user_data) 0960 { 0961 GPU(fd, -EINVAL); 0962 if (!req->legacyEmulation && (!gpu->clientCaps.contains(DRM_CLIENT_CAP_ATOMIC) || !gpu->clientCaps[DRM_CLIENT_CAP_ATOMIC])) { 0963 qWarning("drmModeAtomicCommit requires the atomic capability"); 0964 return -(errno = EINVAL); 0965 } 0966 0967 // verify flags 0968 if ((flags & DRM_MODE_ATOMIC_NONBLOCK) && (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { 0969 qWarning() << "NONBLOCK and MODESET are not allowed together"; 0970 return -(errno = EINVAL); 0971 } else if ((flags & DRM_MODE_ATOMIC_TEST_ONLY) && (flags & DRM_MODE_PAGE_FLIP_EVENT)) { 0972 qWarning() << "TEST_ONLY and PAGE_FLIP_EVENT are not allowed together"; 0973 return -(errno = EINVAL); 0974 } else if (flags & DRM_MODE_PAGE_FLIP_ASYNC) { 0975 qWarning() << "PAGE_FLIP_ASYNC is currently not supported with AMS"; 0976 return -(errno = EINVAL); 0977 } 0978 0979 QList<MockConnector> connCopies; 0980 for (const auto &conn : std::as_const(gpu->connectors)) { 0981 connCopies << *conn; 0982 } 0983 QList<MockCrtc> crtcCopies; 0984 for (const auto &crtc : std::as_const(gpu->crtcs)) { 0985 crtcCopies << *crtc; 0986 } 0987 QList<MockPlane> planeCopies; 0988 for (const auto &plane : std::as_const(gpu->planes)) { 0989 planeCopies << *plane; 0990 } 0991 0992 QList<MockObject *> objects; 0993 for (int i = 0; i < connCopies.count(); i++) { 0994 objects << &connCopies[i]; 0995 } 0996 for (int i = 0; i < crtcCopies.count(); i++) { 0997 objects << &crtcCopies[i]; 0998 } 0999 for (int i = 0; i < planeCopies.count(); i++) { 1000 objects << &planeCopies[i]; 1001 } 1002 1003 // apply changes to the copies 1004 for (int i = 0; i < req->props.count(); i++) { 1005 auto p = req->props[i]; 1006 auto it = std::find_if(objects.constBegin(), objects.constEnd(), [p](const auto &obj){return obj->id == p.obj;}); 1007 if (it == objects.constEnd()) { 1008 qWarning("Object %u in atomic request not found!", p.obj); 1009 return -(errno = EINVAL); 1010 } 1011 auto &obj = *it; 1012 if (obj->id == p.obj) { 1013 auto prop = std::find_if(obj->props.begin(), obj->props.end(), [p](const auto &prop){return prop.id == p.prop;}); 1014 if (prop == obj->props.end()) { 1015 qWarning("Property %u in atomic request for object %u not found!", p.prop, p.obj); 1016 return -(errno = EINVAL); 1017 } 1018 if (prop->value != p.value) { 1019 if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET) && (prop->name == QStringLiteral("CRTC_ID") || prop->name == QStringLiteral("ACTIVE"))) { 1020 qWarning("Atomic request without DRM_MODE_ATOMIC_ALLOW_MODESET tries to do a modeset with object %u", obj->id); 1021 return -(errno = EINVAL); 1022 } 1023 if (prop->flags & DRM_MODE_PROP_BLOB) { 1024 auto blobExists = gpu->getBlob(p.value) != nullptr; 1025 if (blobExists != (p.value > 0)) { 1026 qWarning("Atomic request tries to set property %s on obj %u to invalid blob id %lu", qPrintable(prop->name), obj->id, p.value); 1027 return -(errno = EINVAL); 1028 } 1029 } 1030 prop->value = p.value; 1031 } 1032 } 1033 } 1034 1035 // check if the desired changes are allowed 1036 struct Pipeline { 1037 MockCrtc *crtc; 1038 QList<MockConnector *> conns; 1039 MockPlane *primaryPlane = nullptr; 1040 }; 1041 QList<Pipeline> pipelines; 1042 for (int i = 0; i < crtcCopies.count(); i++) { 1043 if (crtcCopies[i].getProp(QStringLiteral("ACTIVE"))) { 1044 auto blob = gpu->getBlob(crtcCopies[i].getProp(QStringLiteral("MODE_ID"))); 1045 if (!blob) { 1046 qWarning("Atomic request tries to enable CRTC %u without a mode", crtcCopies[i].id); 1047 return -(errno = EINVAL); 1048 } else if (blob->size != sizeof(drmModeModeInfo)) { 1049 qWarning("Atomic request tries to enable CRTC %u with an invalid mode blob", crtcCopies[i].id); 1050 return -(errno = EINVAL); 1051 } 1052 Pipeline pipeline; 1053 pipeline.crtc = &crtcCopies[i]; 1054 pipelines << pipeline; 1055 } 1056 } 1057 for (int i = 0; i < connCopies.count(); i++) { 1058 if (auto crtc = connCopies[i].getProp(QStringLiteral("CRTC_ID"))) { 1059 bool found = false; 1060 for (int p = 0; p < pipelines.count(); p++) { 1061 if (pipelines[p].crtc->id == crtc) { 1062 pipelines[p].conns << &connCopies[i]; 1063 found = true; 1064 break; 1065 } 1066 } 1067 if (!found) { 1068 qWarning("CRTC_ID of connector %u points to inactive or wrong crtc", connCopies[i].id); 1069 return -(errno = EINVAL); 1070 } 1071 } 1072 } 1073 for (int i = 0; i < planeCopies.count(); i++) { 1074 if (auto crtc = planeCopies[i].getProp(QStringLiteral("CRTC_ID"))) { 1075 bool found = false; 1076 for (int p = 0; p < pipelines.count(); p++) { 1077 if (pipelines[p].crtc->id == crtc) { 1078 if (pipelines[p].primaryPlane) { 1079 qWarning("crtc %u has more than one primary planes assigned: %u and %u", pipelines[p].crtc->id, pipelines[p].primaryPlane->id, planeCopies[i].id); 1080 return -(errno = EINVAL); 1081 } else if (!(planeCopies[i].possibleCrtcs & (1 << pipelines[p].crtc->pipeIndex))) { 1082 qWarning("crtc %u is not suitable for primary plane %u", pipelines[p].crtc->id, planeCopies[i].id); 1083 return -(errno = EINVAL); 1084 } else { 1085 pipelines[p].primaryPlane = &planeCopies[i]; 1086 found = true; 1087 break; 1088 } 1089 } 1090 } 1091 if (!found) { 1092 qWarning("CRTC_ID of plane %u points to inactive or wrong crtc", planeCopies[i].id); 1093 return -(errno = EINVAL); 1094 } 1095 auto fbId = planeCopies[i].getProp(QStringLiteral("FB_ID")); 1096 if (!fbId) { 1097 qWarning("FB_ID of active plane %u is 0", planeCopies[i].id); 1098 return -(errno = EINVAL); 1099 } 1100 auto it = std::find_if(gpu->fbs.constBegin(), gpu->fbs.constEnd(), [fbId](auto fb){return fb->id == fbId;}); 1101 if (it == gpu->fbs.constEnd()) { 1102 qWarning("FB_ID %lu of active plane %u is invalid", fbId, planeCopies[i].id); 1103 return -(errno = EINVAL); 1104 } 1105 planeCopies[i].nextFb = *it; 1106 } else { 1107 planeCopies[i].nextFb = nullptr; 1108 } 1109 } 1110 for (const auto &p : std::as_const(pipelines)) { 1111 if (p.conns.isEmpty()) { 1112 qWarning("Active crtc %u has no assigned connectors", p.crtc->id); 1113 return -(errno = EINVAL); 1114 } else if (!p.primaryPlane) { 1115 qWarning("Active crtc %u has no assigned primary plane", p.crtc->id); 1116 return -(errno = EINVAL); 1117 } else { 1118 drmModeModeInfo mode = *static_cast<drmModeModeInfo*>(gpu->getBlob(p.crtc->getProp(QStringLiteral("MODE_ID")))->data); 1119 for (const auto &conn : p.conns) { 1120 bool modeFound = std::find_if(conn->modes.constBegin(), conn->modes.constEnd(), [mode](const auto &m){ 1121 return checkIfEqual(mode, m); 1122 }) != conn->modes.constEnd(); 1123 if (!modeFound) { 1124 qWarning("mode on crtc %u is incompatible with connector %u", p.crtc->id, conn->id); 1125 return -(errno = EINVAL); 1126 } 1127 } 1128 } 1129 } 1130 1131 // if wanted, apply them 1132 1133 if (!(flags & DRM_MODE_ATOMIC_TEST_ONLY)) { 1134 for (auto &conn : std::as_const(gpu->connectors)) { 1135 auto it = std::find_if(connCopies.constBegin(), connCopies.constEnd(), [conn](auto c){return c.id == conn->id;}); 1136 if (it == connCopies.constEnd()) { 1137 qCritical("implementation error: can't find connector %u", conn->id); 1138 return -(errno = EINVAL); 1139 } 1140 *conn = *it; 1141 } 1142 for (auto &crtc : std::as_const(gpu->crtcs)) { 1143 auto it = std::find_if(crtcCopies.constBegin(), crtcCopies.constEnd(), [crtc](auto c){return c.id == crtc->id;}); 1144 if (it == crtcCopies.constEnd()) { 1145 qCritical("implementation error: can't find crtc %u", crtc->id); 1146 return -(errno = EINVAL); 1147 } 1148 *crtc = *it; 1149 } 1150 for (auto &plane : std::as_const(gpu->planes)) { 1151 auto it = std::find_if(planeCopies.constBegin(), planeCopies.constEnd(), [plane](auto c){return c.id == plane->id;}); 1152 if (it == planeCopies.constEnd()) { 1153 qCritical("implementation error: can't find plane %u", plane->id); 1154 return -(errno = EINVAL); 1155 } 1156 *plane = *it; 1157 } 1158 1159 if (flags & DRM_MODE_PAGE_FLIP_EVENT) { 1160 // Unsupported 1161 } 1162 } 1163 1164 return 0; 1165 } 1166 1167 1168 int drmModeCreatePropertyBlob(int fd, const void *data, size_t size, uint32_t *id) 1169 { 1170 GPU(fd, -EINVAL); 1171 if (!data || !size || !id) { 1172 return -(errno = EINVAL); 1173 } 1174 auto blob = std::make_unique<MockPropertyBlob>(gpu, data, size); 1175 *id = blob->id; 1176 gpu->propertyBlobs.push_back(std::move(blob)); 1177 return 0; 1178 } 1179 1180 int drmModeDestroyPropertyBlob(int fd, uint32_t id) 1181 { 1182 GPU(fd, -EINVAL); 1183 auto it = std::remove_if(gpu->propertyBlobs.begin(), gpu->propertyBlobs.end(), [id](const auto &blob) { 1184 return blob->id == id; 1185 }); 1186 if (it == gpu->propertyBlobs.end()) { 1187 return -(errno = EINVAL); 1188 } else { 1189 gpu->propertyBlobs.erase(it, gpu->propertyBlobs.end()); 1190 return 0; 1191 } 1192 } 1193 1194 int drmModeCreateLease(int fd, const uint32_t *objects, int num_objects, int flags, uint32_t *lessee_id) 1195 { 1196 return -(errno = ENOTSUP); 1197 } 1198 1199 drmModeLesseeListPtr drmModeListLessees(int fd) 1200 { 1201 return nullptr; 1202 } 1203 1204 drmModeObjectListPtr drmModeGetLease(int fd) 1205 { 1206 return nullptr; 1207 } 1208 1209 int drmModeRevokeLease(int fd, uint32_t lessee_id) 1210 { 1211 return -(errno = ENOTSUP); 1212 } 1213 1214 void drmModeFreeResources(drmModeResPtr ptr) 1215 { 1216 for (const auto &gpu : std::as_const(s_gpus)) { 1217 if (gpu->resPtrs.removeOne(ptr)) { 1218 delete[] ptr->connectors; 1219 delete[] ptr->crtcs; 1220 delete[] ptr->encoders; 1221 delete[] ptr->fbs; 1222 delete ptr; 1223 } 1224 } 1225 } 1226 1227 void drmModeFreePlaneResources(drmModePlaneResPtr ptr) 1228 { 1229 for (const auto &gpu : std::as_const(s_gpus)) { 1230 if (gpu->drmPlaneRes.removeOne(ptr)) { 1231 delete[] ptr->planes; 1232 delete ptr; 1233 } 1234 } 1235 } 1236 1237 void drmModeFreeCrtc(drmModeCrtcPtr ptr) 1238 { 1239 for (const auto &gpu : std::as_const(s_gpus)) { 1240 if (gpu->drmCrtcs.removeOne(ptr)) { 1241 delete ptr; 1242 return; 1243 } 1244 } 1245 Q_UNREACHABLE(); 1246 } 1247 1248 void drmModeFreeConnector(drmModeConnectorPtr ptr) 1249 { 1250 for (const auto &gpu : std::as_const(s_gpus)) { 1251 if (gpu->drmConnectors.removeOne(ptr)) { 1252 delete[] ptr->encoders; 1253 delete[] ptr->props; 1254 delete[] ptr->prop_values; 1255 delete ptr; 1256 return; 1257 } 1258 } 1259 Q_UNREACHABLE(); 1260 } 1261 1262 void drmModeFreeEncoder(drmModeEncoderPtr ptr) 1263 { 1264 for (const auto &gpu : std::as_const(s_gpus)) { 1265 if (gpu->drmEncoders.removeOne(ptr)) { 1266 delete ptr; 1267 return; 1268 } 1269 } 1270 Q_UNREACHABLE(); 1271 } 1272 1273 void drmModeFreePlane(drmModePlanePtr ptr) 1274 { 1275 for (const auto &gpu : std::as_const(s_gpus)) { 1276 if (gpu->drmPlanes.removeOne(ptr)) { 1277 delete ptr; 1278 return; 1279 } 1280 } 1281 Q_UNREACHABLE(); 1282 }