/* SPDX-License-Identifier: MIT */ #include "drm++/gem.hpp" #include "drm++/helper.hpp" #include "drm++/ioctl.hpp" #include #include #include #include #include #include #include #include #include #include using namespace drm; using namespace drm::gem; /* GEM object class */ Object::Object(ObjectManager& manager, int dmabuf_fd, bool close) : m_manager(&manager) { try { drm_prime_handle args{ .fd = dmabuf_fd }; ioctl::perform(this->m_manager->m_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, args); this->m_handle = args.handle; this->m_manager->m_refcounts[this->m_handle]++; } catch (...) { if (close) { ::close(dmabuf_fd); } throw; } if (close) { ::close(dmabuf_fd); } } int Object::exportFd(ExportFlags flags) const { drm_prime_handle args{ .handle = this->m_handle, .flags = static_cast(flags) }; ioctl::perform(this->m_manager->m_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, args); return args.fd; } void Object::changeHandle(u32 handle) { auto& refcounts{this->m_manager->m_refcounts}; if (refcounts[handle] > 0 || refcounts[this->m_handle] > 1) { throw std::logic_error("GEM handle is still in use"); } drm_gem_change_handle args{ .handle = this->m_handle, .new_handle = handle }; ioctl::perform(this->m_manager->m_fd, DRM_IOCTL_GEM_CHANGE_HANDLE, args); std::exchange(refcounts[handle], refcounts[this->m_handle]); this->m_handle = handle; } void Object::destruct() noexcept { if (this->m_manager == nullptr) { return; } auto& refcounts{this->m_manager->m_refcounts}; if (refcounts[this->m_handle] > 1) { refcounts[this->m_handle]--; this->m_manager = nullptr; return; } drm_gem_close args{ .handle = this->m_handle }; try { ioctl::perform(this->m_manager->m_fd, DRM_IOCTL_GEM_CLOSE, args); } catch(...) { assert(false && "GEM close failed, memory leak likely"); } this->m_manager = nullptr; } /* Dumb buffer class */ DumbBuffer::DumbBuffer(ObjectManager& manager, u32 width, u32 height, u32 bpp) : Object(manager), m_width(width), m_height(height), m_bpp(bpp) { drm_mode_create_dumb args{ .height = height, .width = width, .bpp = bpp }; ioctl::perform(this->m_manager->m_fd, DRM_IOCTL_MODE_CREATE_DUMB, args); this->m_handle = args.handle; this->m_manager->m_refcounts[this->m_handle]++; this->m_pitch = args.pitch; this->m_size = args.size; } void* DumbBuffer::map() { if (this->m_map != nullptr) { return this->m_map; } drm_mode_map_dumb args{ .handle = this->m_handle }; ioctl::perform(this->m_manager->m_fd, DRM_IOCTL_MODE_MAP_DUMB, args); void* map{::mmap(nullptr, this->m_size, PROT_READ | PROT_WRITE, MAP_SHARED, this->m_manager->m_fd, static_cast(args.offset))}; if (map == MAP_FAILED) { throw std::system_error(errno, std::generic_category(), "mmap failed"); } this->m_map = map; return this->m_map; } void DumbBuffer::destruct() noexcept { if (this->m_manager == nullptr) { return; } auto& refcounts{this->m_manager->m_refcounts}; if (refcounts[this->m_handle] > 1) { refcounts[this->m_handle]--; this->m_manager = nullptr; return; } if (this->m_map != nullptr) { ::munmap(this->m_map, this->m_size); this->m_map = nullptr; } drm_mode_destroy_dumb args{ .handle = this->m_handle }; try { ioctl::perform(this->m_manager->m_fd, DRM_IOCTL_MODE_DESTROY_DUMB, args); } catch(...) { assert(false && "Dumb buffer destroy failed, memory leak likely"); } this->m_manager = nullptr; }