1 /*
2 * Copyright © 2020 Collabora, Ltd.
3 * Author: Antonio Caggiano <[email protected]>
4 * Author: Rohan Garg <[email protected]>
5 * Author: Robert Beckett <[email protected]>
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 const char *dri_prime = getenv("DRI_PRIME");
59 if (dri_prime != NULL) {
60 drmDevicePtr drm_device;
61 uint16_t vendor_id, device_id;
62 bool prime_is_vid_did =
63 sscanf(dri_prime, "%hx:%hx", &vendor_id, &device_id) == 2;
64
65 if (prime_is_vid_did && drmGetDevice2(fd, 0, &drm_device) == 0) {
66 if (drm_device->bustype == DRM_BUS_PCI &&
67 (drm_device->deviceinfo.pci->vendor_id != vendor_id ||
68 drm_device->deviceinfo.pci->device_id != device_id))
69 return std::nullopt;
70 }
71 }
72
73 auto ret = DrmDevice();
74 ret.fd = fd;
75 ret.gpu_num = gpu_num;
76 ret.name = name;
77 return ret;
78 }
79
create_all()80 std::vector<DrmDevice> DrmDevice::create_all()
81 {
82 std::vector<DrmDevice> ret = {};
83
84 drmDevicePtr devices[MAX_DRM_DEVICES] = {};
85 int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
86 if (num_devices <= 0) {
87 return ret;
88 }
89
90 for (int32_t gpu_num = 0; gpu_num < num_devices; gpu_num++) {
91 drmDevicePtr device = devices[gpu_num];
92 if ((device->available_nodes & (1 << DRM_NODE_RENDER))) {
93 int fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR);
94
95 // If it can create a device, push it into the vector
96 if (auto drm_device = create_drm_device(fd, gpu_num)) {
97 ret.emplace_back(std::move(drm_device.value()));
98 }
99 }
100 }
101
102 drmFreeDevices(devices, num_devices);
103 return ret;
104 }
105
create(int32_t gpu_num)106 std::optional<DrmDevice> DrmDevice::create(int32_t gpu_num)
107 {
108 std::optional<DrmDevice> ret = std::nullopt;
109
110 if (gpu_num < 0) {
111 return ret;
112 }
113
114 drmDevicePtr devices[MAX_DRM_DEVICES] = {};
115 int num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES);
116
117 if (num_devices > 0 && gpu_num < num_devices) {
118 drmDevicePtr device = devices[gpu_num];
119 int fd = open(device->nodes[DRM_NODE_RENDER], O_RDWR);
120 ret = create_drm_device(fd, gpu_num);
121 }
122
123 drmFreeDevices(devices, num_devices);
124 return ret;
125 }
126
DrmDevice(DrmDevice && other)127 DrmDevice::DrmDevice(DrmDevice &&other)
128 : fd {other.fd}
129 , gpu_num {other.gpu_num}
130 , name {std::move(other.name)}
131 {
132 other.fd = -1;
133 other.gpu_num = -1;
134 }
135
operator =(DrmDevice && other)136 DrmDevice &DrmDevice::operator=(DrmDevice &&other)
137 {
138 std::swap(fd, other.fd);
139 std::swap(gpu_num, other.gpu_num);
140 std::swap(name, other.name);
141 return *this;
142 }
143
~DrmDevice()144 DrmDevice::~DrmDevice()
145 {
146 if (fd >= 0) {
147 close(fd);
148 }
149 }
150
operator bool() const151 DrmDevice::operator bool() const
152 {
153 return !name.empty();
154 }
155
156 } // namespace pps
157