File indexing completed on 2024-05-19 05:32:34

0001 /*
0002     SPDX-FileCopyrightText: 2021-2022 Xaver Hugl <xaver.hugl@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "drmlease_v1.h"
0007 #include "display.h"
0008 #include "drmlease_v1_p.h"
0009 #include "utils/common.h"
0010 #include "utils/resource.h"
0011 
0012 #include <fcntl.h>
0013 #include <unistd.h>
0014 
0015 namespace KWin
0016 {
0017 
0018 static const quint32 s_version = 1;
0019 
0020 DrmLeaseManagerV1::DrmLeaseManagerV1(DrmBackend *backend, Display *display, QObject *parent)
0021     : QObject(parent)
0022     , m_backend(backend)
0023     , m_display(display)
0024 {
0025     const auto &gpus = m_backend->gpus();
0026     for (const auto &gpu : gpus) {
0027         addGpu(gpu.get());
0028     }
0029     connect(m_backend, &DrmBackend::gpuAdded, this, &DrmLeaseManagerV1::addGpu);
0030     connect(m_backend, &DrmBackend::gpuRemoved, this, &DrmLeaseManagerV1::removeGpu);
0031     connect(m_backend, &DrmBackend::outputsQueried, this, &DrmLeaseManagerV1::handleOutputsQueried);
0032 }
0033 
0034 DrmLeaseManagerV1::~DrmLeaseManagerV1()
0035 {
0036     for (const auto device : m_leaseDevices) {
0037         device->remove();
0038     }
0039 }
0040 
0041 void DrmLeaseManagerV1::addGpu(DrmGpu *gpu)
0042 {
0043     m_leaseDevices[gpu] = new DrmLeaseDeviceV1Interface(m_display, gpu);
0044 }
0045 
0046 void DrmLeaseManagerV1::removeGpu(DrmGpu *gpu)
0047 {
0048     if (auto device = m_leaseDevices.take(gpu)) {
0049         device->remove();
0050     }
0051 }
0052 
0053 void DrmLeaseManagerV1::handleOutputsQueried()
0054 {
0055     for (const auto device : m_leaseDevices) {
0056         device->done();
0057         device->setDrmMaster(device->gpu()->isActive());
0058     }
0059 }
0060 
0061 DrmLeaseDeviceV1Interface::DrmLeaseDeviceV1Interface(Display *display, DrmGpu *gpu)
0062     : QtWaylandServer::wp_drm_lease_device_v1(*display, s_version)
0063     , m_gpu(gpu)
0064 {
0065     const auto outputs = gpu->drmOutputs();
0066     for (const auto output : outputs) {
0067         addOutput(output);
0068     }
0069     connect(gpu, &DrmGpu::outputAdded, this, &DrmLeaseDeviceV1Interface::addOutput);
0070     connect(gpu, &DrmGpu::outputRemoved, this, &DrmLeaseDeviceV1Interface::removeOutput);
0071     connect(gpu, &DrmGpu::activeChanged, this, &DrmLeaseDeviceV1Interface::setDrmMaster);
0072 }
0073 
0074 DrmLeaseDeviceV1Interface::~DrmLeaseDeviceV1Interface()
0075 {
0076     while (!m_connectors.empty()) {
0077         removeOutput(m_connectors.begin()->first);
0078     }
0079 }
0080 
0081 void DrmLeaseDeviceV1Interface::addOutput(DrmAbstractOutput *output)
0082 {
0083     DrmOutput *drmOutput = qobject_cast<DrmOutput *>(output);
0084     if (!drmOutput || !drmOutput->isNonDesktop()) {
0085         return;
0086     }
0087     m_connectors[drmOutput] = std::make_unique<DrmLeaseConnectorV1Interface>(this, drmOutput);
0088 
0089     if (m_hasDrmMaster) {
0090         offerConnector(m_connectors[drmOutput].get());
0091     }
0092 }
0093 
0094 void DrmLeaseDeviceV1Interface::removeOutput(DrmAbstractOutput *output)
0095 {
0096     const auto it = m_connectors.find(output);
0097     if (it != m_connectors.end()) {
0098         DrmLeaseConnectorV1Interface *connector = it->second.get();
0099         connector->withdraw();
0100         for (const auto &lease : std::as_const(m_leases)) {
0101             if (lease->connectors().contains(connector)) {
0102                 lease->connectors().removeOne(connector);
0103                 lease->revoke();
0104             }
0105         }
0106         for (const auto &leaseRequest : std::as_const(m_leaseRequests)) {
0107             if (leaseRequest->connectors().contains(connector)) {
0108                 leaseRequest->invalidate();
0109             }
0110         }
0111         m_connectors.erase(it);
0112     }
0113 }
0114 
0115 void DrmLeaseDeviceV1Interface::setDrmMaster(bool hasDrmMaster)
0116 {
0117     if (hasDrmMaster == m_hasDrmMaster) {
0118         return;
0119     }
0120     if (hasDrmMaster) {
0121         // send pending drm fds
0122         while (!m_pendingFds.isEmpty()) {
0123             FileDescriptor fd = m_gpu->createNonMasterFd();
0124             send_drm_fd(m_pendingFds.dequeue(), fd.get());
0125         }
0126         // offer all connectors again
0127         for (const auto &[output, connector] : m_connectors) {
0128             offerConnector(connector.get());
0129         }
0130     } else {
0131         // withdraw all connectors
0132         for (const auto &[output, connector] : m_connectors) {
0133             connector->withdraw();
0134         }
0135         // and revoke all leases
0136         for (const auto &lease : std::as_const(m_leases)) {
0137             lease->revoke();
0138         }
0139     }
0140     m_hasDrmMaster = hasDrmMaster;
0141     done();
0142 }
0143 
0144 void DrmLeaseDeviceV1Interface::done()
0145 {
0146     const auto resources = resourceMap();
0147     for (const auto resource : resources) {
0148         send_done(resource->handle);
0149     }
0150 }
0151 
0152 void DrmLeaseDeviceV1Interface::remove()
0153 {
0154     for (const auto &lease : std::as_const(m_leases)) {
0155         lease->deny();
0156     }
0157     for (const auto &[output, connector] : m_connectors) {
0158         connector->withdraw();
0159     }
0160     for (const auto &request : std::as_const(m_leaseRequests)) {
0161         request->invalidate();
0162     }
0163     done();
0164     globalRemove();
0165 }
0166 
0167 void DrmLeaseDeviceV1Interface::addLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest)
0168 {
0169     m_leaseRequests.push_back(leaseRequest);
0170 }
0171 
0172 void DrmLeaseDeviceV1Interface::removeLeaseRequest(DrmLeaseRequestV1Interface *leaseRequest)
0173 {
0174     m_leaseRequests.removeOne(leaseRequest);
0175 }
0176 
0177 void DrmLeaseDeviceV1Interface::addLease(DrmLeaseV1Interface *lease)
0178 {
0179     m_leases.push_back(lease);
0180 }
0181 
0182 void DrmLeaseDeviceV1Interface::removeLease(DrmLeaseV1Interface *lease)
0183 {
0184     m_leases.removeOne(lease);
0185 }
0186 
0187 bool DrmLeaseDeviceV1Interface::hasDrmMaster() const
0188 {
0189     return m_hasDrmMaster;
0190 }
0191 
0192 DrmGpu *DrmLeaseDeviceV1Interface::gpu() const
0193 {
0194     return m_gpu;
0195 }
0196 
0197 void DrmLeaseDeviceV1Interface::offerConnector(DrmLeaseConnectorV1Interface *connector)
0198 {
0199     for (const auto &resource : resourceMap()) {
0200         auto connectorResource = connector->add(resource->client(), 0, resource->version());
0201         send_connector(resource->handle, connectorResource->handle);
0202         connector->send(connectorResource->handle);
0203     }
0204 }
0205 
0206 void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_create_lease_request(Resource *resource, uint32_t id)
0207 {
0208     wl_resource *requestResource = wl_resource_create(resource->client(), &wp_drm_lease_request_v1_interface,
0209                                                       resource->version(), id);
0210     if (!requestResource) {
0211         wl_resource_post_no_memory(resource->handle);
0212         return;
0213     }
0214     m_leaseRequests << new DrmLeaseRequestV1Interface(this, requestResource);
0215 }
0216 
0217 void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_release(Resource *resource)
0218 {
0219     send_released(resource->handle);
0220     wl_resource_destroy(resource->handle);
0221 }
0222 
0223 void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_bind_resource(Resource *resource)
0224 {
0225     if (isGlobalRemoved()) {
0226         return;
0227     }
0228     if (!m_hasDrmMaster) {
0229         m_pendingFds << resource->handle;
0230         return;
0231     }
0232     FileDescriptor fd = m_gpu->createNonMasterFd();
0233     send_drm_fd(resource->handle, fd.get());
0234     for (const auto &[output, connector] : m_connectors) {
0235         if (!connector->withdrawn()) {
0236             auto connectorResource = connector->add(resource->client(), 0, s_version);
0237             send_connector(resource->handle, connectorResource->handle);
0238             connector->send(connectorResource->handle);
0239         }
0240     }
0241     send_done(resource->handle);
0242 }
0243 
0244 void DrmLeaseDeviceV1Interface::wp_drm_lease_device_v1_destroy_global()
0245 {
0246     delete this;
0247 }
0248 
0249 DrmLeaseConnectorV1Interface::DrmLeaseConnectorV1Interface(DrmLeaseDeviceV1Interface *leaseDevice, DrmOutput *output)
0250     : wp_drm_lease_connector_v1()
0251     , m_device(leaseDevice)
0252     , m_output(output)
0253 {
0254 }
0255 
0256 uint32_t DrmLeaseConnectorV1Interface::id() const
0257 {
0258     return m_output->connector()->id();
0259 }
0260 
0261 DrmLeaseDeviceV1Interface *DrmLeaseConnectorV1Interface::device() const
0262 {
0263     return m_device;
0264 }
0265 
0266 DrmOutput *DrmLeaseConnectorV1Interface::output() const
0267 {
0268     return m_output;
0269 }
0270 
0271 bool DrmLeaseConnectorV1Interface::withdrawn() const
0272 {
0273     return m_withdrawn;
0274 }
0275 
0276 void DrmLeaseConnectorV1Interface::send(wl_resource *resource)
0277 {
0278     m_withdrawn = false;
0279     send_connector_id(resource, m_output->connector()->id());
0280     send_name(resource, m_output->name());
0281     send_description(resource, m_output->description());
0282     send_done(resource);
0283 }
0284 
0285 void DrmLeaseConnectorV1Interface::withdraw()
0286 {
0287     if (!m_withdrawn) {
0288         m_withdrawn = true;
0289         for (const auto &resource : resourceMap()) {
0290             send_withdrawn(resource->handle);
0291         }
0292     }
0293 }
0294 
0295 void DrmLeaseConnectorV1Interface::wp_drm_lease_connector_v1_destroy(Resource *resource)
0296 {
0297     wl_resource_destroy(resource->handle);
0298 }
0299 
0300 DrmLeaseRequestV1Interface::DrmLeaseRequestV1Interface(DrmLeaseDeviceV1Interface *device, wl_resource *resource)
0301     : wp_drm_lease_request_v1(resource)
0302     , m_device(device)
0303 {
0304 }
0305 
0306 DrmLeaseRequestV1Interface::~DrmLeaseRequestV1Interface()
0307 {
0308     m_device->removeLeaseRequest(this);
0309 }
0310 
0311 QList<DrmLeaseConnectorV1Interface *> DrmLeaseRequestV1Interface::connectors() const
0312 {
0313     return m_connectors;
0314 }
0315 
0316 void DrmLeaseRequestV1Interface::invalidate()
0317 {
0318     m_connectors.clear();
0319     m_invalid = true;
0320 }
0321 
0322 void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_request_connector(Resource *resource, struct ::wl_resource *connector_handle)
0323 {
0324     if (auto connector = resource_cast<DrmLeaseConnectorV1Interface *>(connector_handle)) {
0325         if (connector->device() != m_device) {
0326             wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_WRONG_DEVICE, "Requested connector from invalid lease device");
0327         } else if (connector->withdrawn()) {
0328             qCWarning(KWIN_CORE) << "DrmLease: withdrawn connector requested";
0329             invalidate();
0330         } else if (m_connectors.contains(connector)) {
0331             wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "Requested connector twice");
0332         } else if (!m_invalid) {
0333             m_connectors << connector;
0334         }
0335     } else {
0336         qCWarning(KWIN_CORE, "DrmLease: Invalid connector requested");
0337         invalidate();
0338     }
0339 }
0340 
0341 void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_submit(Resource *resource, uint32_t id)
0342 {
0343     wl_resource *leaseResource = wl_resource_create(resource->client(), &wp_drm_lease_v1_interface, s_version, id);
0344     if (!leaseResource) {
0345         wl_resource_post_no_memory(resource->handle);
0346         return;
0347     }
0348     DrmLeaseV1Interface *lease = new DrmLeaseV1Interface(m_device, m_connectors, leaseResource);
0349     m_device->addLease(lease);
0350     if (!m_device->hasDrmMaster()) {
0351         qCWarning(KWIN_CORE) << "DrmLease: rejecting lease request without drm master";
0352         lease->deny();
0353     } else if (m_invalid) {
0354         qCWarning(KWIN_CORE) << "DrmLease: rejecting lease request with a withdrawn connector";
0355         lease->deny();
0356     } else if (m_connectors.isEmpty()) {
0357         wl_resource_post_error(resource->handle, WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "Requested lease without connectors");
0358     } else {
0359         QList<DrmOutput *> outputs;
0360         for (const auto &connector : m_connectors) {
0361             outputs.push_back(connector->output());
0362         }
0363         auto drmLease = m_device->gpu()->leaseOutputs(outputs);
0364         if (drmLease) {
0365             lease->grant(std::move(drmLease));
0366         } else {
0367             lease->deny();
0368         }
0369     }
0370     wl_resource_destroy(resource->handle);
0371 }
0372 
0373 void DrmLeaseRequestV1Interface::wp_drm_lease_request_v1_destroy_resource(Resource *resource)
0374 {
0375     delete this;
0376 }
0377 
0378 DrmLeaseV1Interface::DrmLeaseV1Interface(DrmLeaseDeviceV1Interface *device, const QList<DrmLeaseConnectorV1Interface *> &connectors, wl_resource *resource)
0379     : wp_drm_lease_v1(resource)
0380     , m_device(device)
0381     , m_connectors(connectors)
0382 {
0383 }
0384 
0385 DrmLeaseV1Interface::~DrmLeaseV1Interface()
0386 {
0387     if (m_lease) {
0388         revoke();
0389     } else {
0390         deny();
0391     }
0392     m_device->removeLease(this);
0393 }
0394 
0395 void DrmLeaseV1Interface::grant(std::unique_ptr<DrmLease> &&lease)
0396 {
0397     FileDescriptor tmp = std::move(lease->fd());
0398     send_lease_fd(tmp.get());
0399     m_lease = std::move(lease);
0400     connect(m_lease.get(), &DrmLease::revokeRequested, this, &DrmLeaseV1Interface::revoke);
0401     for (const auto &connector : std::as_const(m_connectors)) {
0402         connector->withdraw();
0403     }
0404     m_device->done();
0405 }
0406 
0407 void DrmLeaseV1Interface::deny()
0408 {
0409     Q_ASSERT(!m_lease);
0410     if (!m_finished) {
0411         m_finished = true;
0412         send_finished();
0413     }
0414 }
0415 
0416 void DrmLeaseV1Interface::revoke()
0417 {
0418     Q_ASSERT(m_lease);
0419     if (!m_finished) {
0420         m_finished = true;
0421         send_finished();
0422     }
0423     m_lease.reset();
0424     // check if we should offer connectors again
0425     if (m_device->hasDrmMaster()) {
0426         for (const auto &connector : std::as_const(m_connectors)) {
0427             m_device->offerConnector(connector);
0428         }
0429         m_device->done();
0430     }
0431 }
0432 
0433 void DrmLeaseV1Interface::wp_drm_lease_v1_destroy(Resource *resource)
0434 {
0435     wl_resource_destroy(resource->handle);
0436 }
0437 
0438 void DrmLeaseV1Interface::wp_drm_lease_v1_destroy_resource(Resource *resource)
0439 {
0440     delete this;
0441 }
0442 
0443 QList<DrmLeaseConnectorV1Interface *> DrmLeaseV1Interface::connectors() const
0444 {
0445     return m_connectors;
0446 }
0447 
0448 }
0449 
0450 #include "moc_drmlease_v1.cpp"
0451 #include "moc_drmlease_v1_p.cpp"