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
18 namespace pps
19 {
20 #define MAX_DRM_DEVICES 64
21
device_count()22 uint32_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
query_drm_name(const int fd)31 std::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
create_drm_device(int fd,int32_t gpu_num)46 std::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
create_all()65 std::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
create(int32_t gpu_num)91 std::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_RDONLY);
105 ret = create_drm_device(fd, gpu_num);
106 }
107
108 drmFreeDevices(devices, num_devices);
109 return ret;
110 }
111
DrmDevice(DrmDevice && other)112 DrmDevice::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
operator =(DrmDevice && other)121 DrmDevice &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
~DrmDevice()129 DrmDevice::~DrmDevice()
130 {
131 if (fd >= 0) {
132 close(fd);
133 }
134 }
135
operator bool() const136 DrmDevice::operator bool() const
137 {
138 return !name.empty();
139 }
140
141 } // namespace pps
142