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