| /* |
| * Copyright © 2020 Collabora, Ltd. |
| * Author: Antonio Caggiano <antonio.caggiano@collabora.com> |
| * Author: Rohan Garg <rohan.garg@collabora.com> |
| * Author: Robert Beckett <bob.beckett@collabora.com> |
| * |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| #include "pps_device.h" |
| |
| #include <cassert> |
| #include <fcntl.h> |
| #include <memory> |
| #include <unistd.h> |
| #include <xf86drm.h> |
| |
| namespace pps |
| { |
| #define MAX_DRM_DEVICES 64 |
| |
| uint32_t DrmDevice::device_count() |
| { |
| drmDevicePtr devices[MAX_DRM_DEVICES] = {}; |
| int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); |
| drmFreeDevices(devices, num_devices); |
| return static_cast<uint32_t>(num_devices); |
| } |
| |
| /// @return The name of a DRM device, empty string in case of error |
| std::string query_drm_name(const int fd) |
| { |
| assert(fd && "Failed to query DrmDevice: invalid fd"); |
| |
| std::string name = ""; |
| |
| if (drmVersionPtr version = drmGetVersion(fd)) { |
| name = std::string(version->name, version->name_len); |
| drmFreeVersion(version); |
| } |
| |
| return name; |
| } |
| |
| /// @return A DRM device, nullopt in case of error |
| std::optional<DrmDevice> create_drm_device(int fd, int32_t gpu_num) |
| { |
| if (fd < 0 || gpu_num < 0) { |
| return std::nullopt; |
| } |
| |
| // Try getting the name |
| std::string name = query_drm_name(fd); |
| if (name.empty()) { |
| return std::nullopt; |
| } |
| |
| auto ret = DrmDevice(); |
| ret.fd = fd; |
| ret.gpu_num = gpu_num; |
| ret.name = name; |
| return ret; |
| } |
| |
| std::vector<DrmDevice> DrmDevice::create_all() |
| { |
| std::vector<DrmDevice> ret = {}; |
| |
| drmDevicePtr devices[MAX_DRM_DEVICES] = {}; |
| int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); |
| if (num_devices <= 0) { |
| return ret; |
| } |
| |
| for (int32_t gpu_num = 0; gpu_num < num_devices; gpu_num++) { |
| drmDevicePtr device = devices[gpu_num]; |
| if ((device->available_nodes & (1 << DRM_NODE_RENDER))) { |
| int fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR); |
| |
| // If it can create a device, push it into the vector |
| if (auto drm_device = create_drm_device(fd, gpu_num)) { |
| ret.emplace_back(std::move(drm_device.value())); |
| } |
| } |
| } |
| |
| drmFreeDevices(devices, num_devices); |
| return ret; |
| } |
| |
| std::optional<DrmDevice> DrmDevice::create(int32_t gpu_num) |
| { |
| std::optional<DrmDevice> ret = std::nullopt; |
| |
| if (gpu_num < 0) { |
| return ret; |
| } |
| |
| drmDevicePtr devices[MAX_DRM_DEVICES] = {}; |
| int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); |
| |
| if (num_devices > 0 && gpu_num < num_devices) { |
| drmDevicePtr device = devices[gpu_num]; |
| int fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR); |
| ret = create_drm_device(fd, gpu_num); |
| } |
| |
| drmFreeDevices(devices, num_devices); |
| return ret; |
| } |
| |
| DrmDevice::DrmDevice(DrmDevice &&other) |
| : fd {other.fd} |
| , gpu_num {other.gpu_num} |
| , name {std::move(other.name)} |
| { |
| other.fd = -1; |
| other.gpu_num = -1; |
| } |
| |
| DrmDevice &DrmDevice::operator=(DrmDevice &&other) |
| { |
| std::swap(fd, other.fd); |
| std::swap(gpu_num, other.gpu_num); |
| std::swap(name, other.name); |
| return *this; |
| } |
| |
| DrmDevice::~DrmDevice() |
| { |
| if (fd >= 0) { |
| close(fd); |
| } |
| } |
| |
| DrmDevice::operator bool() const |
| { |
| return !name.empty(); |
| } |
| |
| } // namespace pps |