1/* 2 * Copyright © 2020 Collabora, Ltd. 3 * Author: Antonio Caggiano <antonio.caggiano@collabora.com> 4 * Author: Rohan Garg <rohan.garg@collabora.com> 5 * Author: Robert Beckett <bob.beckett@collabora.com> 6 * 7 * SPDX-License-Identifier: MIT 8 */ 9 10#include "pps_device.h" 11 12#include <cassert> 13#include <fcntl.h> 14#include <memory> 15#include <unistd.h> 16#include <xf86drm.h> 17 18namespace pps 19{ 20#define MAX_DRM_DEVICES 64 21 22uint32_t DrmDevice::device_count() 23{ 24 drmDevicePtr devices[MAX_DRM_DEVICES] = {}; 25 int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); 26 drmFreeDevices(devices, num_devices); 27 return static_cast<uint32_t>(num_devices); 28} 29 30/// @return The name of a DRM device, empty string in case of error 31std::string query_drm_name(const int fd) 32{ 33 assert(fd && "Failed to query DrmDevice: invalid fd"); 34 35 std::string name = ""; 36 37 if (drmVersionPtr version = drmGetVersion(fd)) { 38 name = std::string(version->name, version->name_len); 39 drmFreeVersion(version); 40 } 41 42 return name; 43} 44 45/// @return A DRM device, nullopt in case of error 46std::optional<DrmDevice> create_drm_device(int fd, int32_t gpu_num) 47{ 48 if (fd < 0 || gpu_num < 0) { 49 return std::nullopt; 50 } 51 52 // Try getting the name 53 std::string name = query_drm_name(fd); 54 if (name.empty()) { 55 return std::nullopt; 56 } 57 58 auto ret = DrmDevice(); 59 ret.fd = fd; 60 ret.gpu_num = gpu_num; 61 ret.name = name; 62 return ret; 63} 64 65std::vector<DrmDevice> DrmDevice::create_all() 66{ 67 std::vector<DrmDevice> ret = {}; 68 69 drmDevicePtr devices[MAX_DRM_DEVICES] = {}; 70 int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); 71 if (num_devices <= 0) { 72 return ret; 73 } 74 75 for (int32_t gpu_num = 0; gpu_num < num_devices; gpu_num++) { 76 drmDevicePtr device = devices[gpu_num]; 77 if ((device->available_nodes & (1 << DRM_NODE_RENDER))) { 78 int fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR); 79 80 // If it can create a device, push it into the vector 81 if (auto drm_device = create_drm_device(fd, gpu_num)) { 82 ret.emplace_back(std::move(drm_device.value())); 83 } 84 } 85 } 86 87 drmFreeDevices(devices, num_devices); 88 return ret; 89} 90 91std::optional<DrmDevice> DrmDevice::create(int32_t gpu_num) 92{ 93 std::optional<DrmDevice> ret = std::nullopt; 94 95 if (gpu_num < 0) { 96 return ret; 97 } 98 99 drmDevicePtr devices[MAX_DRM_DEVICES] = {}; 100 int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); 101 102 if (num_devices > 0 && gpu_num < num_devices) { 103 drmDevicePtr device = devices[gpu_num]; 104 int fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR); 105 ret = create_drm_device(fd, gpu_num); 106 } 107 108 drmFreeDevices(devices, num_devices); 109 return ret; 110} 111 112DrmDevice::DrmDevice(DrmDevice &&other) 113 : fd {other.fd} 114 , gpu_num {other.gpu_num} 115 , name {std::move(other.name)} 116{ 117 other.fd = -1; 118 other.gpu_num = -1; 119} 120 121DrmDevice &DrmDevice::operator=(DrmDevice &&other) 122{ 123 std::swap(fd, other.fd); 124 std::swap(gpu_num, other.gpu_num); 125 std::swap(name, other.name); 126 return *this; 127} 128 129DrmDevice::~DrmDevice() 130{ 131 if (fd >= 0) { 132 close(fd); 133 } 134} 135 136DrmDevice::operator bool() const 137{ 138 return !name.empty(); 139} 140 141} // namespace pps 142