GCC Code Coverage Report


Directory: ./
File: mediaoutput/DrmCards.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 218 237 92.0%
Functions: 20 20 100.0%
Branches: 208 356 58.4%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2026 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include "DrmCards.h"
16
17 #include <atomic>
18 #include <chrono>
19 #include <condition_variable>
20 #include <map>
21 #include <mutex>
22 #include <set>
23
24 #include "../log.h"
25 #include "mediaoutput.h"
26
27 #if __has_include(<xf86drm.h>)
28 #include <cerrno>
29 #include <cstring>
30 #include <fcntl.h>
31 #include <sys/ioctl.h>
32 #include <unistd.h>
33 #include <xf86drm.h>
34 #include <xf86drmMode.h>
35
36 namespace DrmCards {
37
38 namespace {
39 struct Card {
40 int fd = -1;
41 int refCount = 0;
42 bool master = false;
43 bool masterWarned = false;
44 std::set<uint32_t> claimed; // driven by a framebuffer output
45 // Held by a live lease or a lease-less holder, counted per holder: the
46 // fallback in AcquireLease() hands a second holder the shared fd for a
47 // connector someone already holds, and with a plain set the first
48 // holder's release cleared the mark the second was still relying on (its
49 // framebuffer output then drew over the video).
50 std::map<uint32_t, int> leased;
51 // Connectors whose lease a teardown still has to release (RetireLease()).
52 std::map<uint32_t, int> retiring;
53 };
54 // Guards the table and the per-card sets. Never held across a blocking DRM
55 // call that waits on the display.
56 std::mutex s_lock;
57 std::map<std::string, Card> s_cards;
58 // Notified, under s_lock, by every ReleaseLease() of a retiring lease.
59 std::condition_variable s_retired;
60 std::atomic<void (*)(int, uint32_t)> s_mediaHoldListener{ nullptr };
61
62 // Caller holds drmMasterLock, and not s_lock: the listener asks HeldByMedia().
63 25 void NotifyMediaHold(int fd, uint32_t connectorId) {
64
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 10 times.
25 if (auto listener = s_mediaHoldListener.load()) {
65 15 listener(fd, connectorId);
66 }
67 25 }
68
69 2219 Card* CardForFdLocked(int fd) {
70
1/2
✓ Branch 0 taken 2841 times.
✗ Branch 1 not taken.
2841 for (auto& [path, card] : s_cards) {
71
2/2
✓ Branch 0 taken 2219 times.
✓ Branch 1 taken 622 times.
2841 if (card.fd == fd) {
72 2219 return &card;
73 }
74 }
75 ✗ return nullptr;
76 }
77
78 1949 bool EnsureMasterLocked(const std::string& cardPath, Card& card) {
79
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 1923 times.
1949 if (!card.master) {
80 26 card.master = ioctl(card.fd, DRM_IOCTL_SET_MASTER, 0) == 0;
81
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
26 if (!card.master && !card.masterWarned) {
82 ✗ LogWarn(VB_MEDIAOUT, "DRM: no DRM master on %s (%s); another process holds it\n",
83 cardPath.c_str(), strerror(errno));
84 ✗ card.masterWarned = true;
85
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 } else if (card.master) {
86 26 card.masterWarned = false;
87 }
88 }
89 1949 return card.master;
90 }
91
92 // Master is held only while something here needs it: a framebuffer output
93 // driving the card, or a live lease, whose lessee is master only while its
94 // lessor is. Kept on a card fppd merely opened, it locked out every other
95 // DRM client there: X or a kiosk started after fppd stayed dark, and fbcon
96 // could no longer relight a monitor plugged in later.
97 95 void DropMasterIfIdleLocked(Card& card) {
98
7/8
✓ Branch 0 taken 95 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 51 times.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 50 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 50 times.
✓ Branch 7 taken 45 times.
95 if (card.master && card.claimed.empty() && card.leased.empty()) {
99 50 ioctl(card.fd, DRM_IOCTL_DROP_MASTER, 0);
100 50 card.master = false;
101 }
102 95 }
103
104 100 int PlaneType(int fd, uint32_t planeId) {
105 100 int type = -1;
106 100 drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(fd, planeId, DRM_MODE_OBJECT_PLANE);
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 if (!props) {
108 ✗ return type;
109 }
110
4/4
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 100 times.
✓ Branch 3 taken 72 times.
200 for (uint32_t i = 0; i < props->count_props && type < 0; i++) {
111 100 drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[i]);
112
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 if (prop) {
113
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 if (!strcmp(prop->name, "type")) {
114 100 type = (int)props->prop_values[i];
115 }
116 100 drmModeFreeProperty(prop);
117 }
118 }
119 100 drmModeFreeObjectProperties(props);
120 100 return type;
121 }
122 } // namespace
123
124 730 int Acquire(const std::string& cardPath) {
125
1/2
✓ Branch 0 taken 730 times.
✗ Branch 1 not taken.
730 std::lock_guard<std::mutex> lock(s_lock);
126
1/2
✓ Branch 0 taken 730 times.
✗ Branch 1 not taken.
730 auto it = s_cards.find(cardPath);
127
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 721 times.
730 if (it != s_cards.end()) {
128 9 it->second.refCount++;
129 9 return it->second.fd;
130 }
131
1/2
✓ Branch 0 taken 721 times.
✗ Branch 1 not taken.
721 int fd = open(cardPath.c_str(), O_RDWR | O_CLOEXEC);
132
2/2
✓ Branch 0 taken 692 times.
✓ Branch 1 taken 29 times.
721 if (fd < 0) {
133 692 return -1;
134 }
135
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
136 29 Card card;
137 29 card.fd = fd;
138 29 card.refCount = 1;
139 // A new fd on a card with no master becomes master by itself. Nothing
140 // is claimed or leased yet, so give it back; EnsureMaster() takes it
141 // when there is something to drive.
142 29 card.master = true;
143 29 DropMasterIfIdleLocked(card);
144
2/4
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
29 s_cards[cardPath] = card;
145
1/2
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
29 LogDebug(VB_MEDIAOUT, "DRM: opened %s fd=%d\n", cardPath.c_str(), fd);
146 29 return fd;
147 730 }
148
149 30 void Release(const std::string& cardPath) {
150
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 std::lock_guard<std::mutex> lock(s_lock);
151
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 auto it = s_cards.find(cardPath);
152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (it == s_cards.end()) {
153 ✗ LogWarn(VB_MEDIAOUT, "DRM: release of %s, which was never acquired\n", cardPath.c_str());
154 ✗ return;
155 }
156
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 21 times.
30 if (--it->second.refCount > 0) {
157 9 return;
158 }
159 // Closing it drops master, revokes any lease left over, and frees every
160 // handle and framebuffer still open on it.
161
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 close(it->second.fd);
162
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 LogDebug(VB_MEDIAOUT, "DRM: closed %s\n", cardPath.c_str());
163
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 s_cards.erase(it);
164
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 9 times.
30 }
165
166 1936 bool EnsureMaster(int fd) {
167
1/2
✓ Branch 0 taken 1936 times.
✗ Branch 1 not taken.
1936 std::lock_guard<std::mutex> lock(s_lock);
168
1/2
✓ Branch 0 taken 2547 times.
✗ Branch 1 not taken.
2547 for (auto& [path, card] : s_cards) {
169
2/2
✓ Branch 0 taken 1936 times.
✓ Branch 1 taken 611 times.
2547 if (card.fd == fd) {
170
1/2
✓ Branch 0 taken 1936 times.
✗ Branch 1 not taken.
1936 return EnsureMasterLocked(path, card);
171 }
172 }
173 ✗ return ioctl(fd, DRM_IOCTL_SET_MASTER, 0) == 0;
174 1936 }
175
176 66 void Claim(int fd, uint32_t objectId) {
177
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 std::lock_guard<std::mutex> lock(s_lock);
178
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 if (Card* card = CardForFdLocked(fd)) {
179
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 card->claimed.insert(objectId);
180 }
181 66 }
182
183 54 void Unclaim(int fd, uint32_t objectId) {
184
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 std::lock_guard<std::mutex> lock(s_lock);
185
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 if (Card* card = CardForFdLocked(fd)) {
186
1/2
✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
54 card->claimed.erase(objectId);
187 54 DropMasterIfIdleLocked(*card);
188 }
189 54 }
190
191 27 bool InUse(int fd, uint32_t objectId) {
192
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 std::lock_guard<std::mutex> lock(s_lock);
193 27 Card* card = CardForFdLocked(fd);
194
6/10
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 27 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 25 times.
54 return card && (card->claimed.count(objectId) || card->leased.count(objectId));
195 27 }
196
197 2072 bool HeldByMedia(int fd, uint32_t objectId) {
198
1/2
✓ Branch 0 taken 2072 times.
✗ Branch 1 not taken.
2072 std::lock_guard<std::mutex> lock(s_lock);
199 2072 Card* card = CardForFdLocked(fd);
200
4/6
✓ Branch 0 taken 2072 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2072 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 180 times.
✓ Branch 5 taken 1892 times.
4144 return card && card->leased.count(objectId);
201 2072 }
202
203 23 void SetMediaHoldListener(void (*listener)(int fd, uint32_t connectorId)) {
204 23 s_mediaHoldListener = listener;
205 23 }
206
207 705 Lease AcquireLease(const std::string& cardPath, int connectorId) {
208 705 Lease lease;
209
1/2
✓ Branch 0 taken 705 times.
✗ Branch 1 not taken.
705 lease.cardPath = cardPath;
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 705 times.
705 if (connectorId <= 0) {
211 ✗ return lease;
212 }
213 705 lease.connectorId = connectorId;
214 {
215 // A consumer restarted after its pipeline died asks for the connector
216 // within milliseconds of the stop, while the old pipeline's teardown
217 // thread still holds its lease. Found held, the connector got no
218 // CRTC and the new kmssink the card's shared fd, the one the
219 // framebuffer outputs flip on. Wait for that release instead; a
220 // teardown wedged past this still falls back as before. Before
221 // drmMasterLock, which the framebuffer outputs' flips take.
222
1/2
✓ Branch 0 taken 705 times.
✗ Branch 1 not taken.
705 std::unique_lock<std::mutex> lock(s_lock);
223 709 auto retiring = [&cardPath, connectorId]() {
224
1/2
✓ Branch 0 taken 709 times.
✗ Branch 1 not taken.
709 auto card = s_cards.find(cardPath);
225
5/6
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 697 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 7 times.
709 return card != s_cards.end() && card->second.retiring.count((uint32_t)connectorId);
226 705 };
227
8/10
✓ Branch 0 taken 705 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 703 times.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 704 times.
709 if (retiring() && !s_retired.wait_for(lock, std::chrono::seconds(2), [&retiring]() { return !retiring(); })) {
228
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 LogWarn(VB_MEDIAOUT, "DRM: %s connector %d is still held by a teardown after 2 s\n",
229 cardPath.c_str(), connectorId);
230 }
231 705 }
232
1/2
✓ Branch 0 taken 705 times.
✗ Branch 1 not taken.
705 int fd = Acquire(cardPath);
233
2/2
✓ Branch 0 taken 692 times.
✓ Branch 1 taken 13 times.
705 if (fd < 0) {
234
1/2
✓ Branch 0 taken 692 times.
✗ Branch 1 not taken.
692 LogErr(VB_MEDIAOUT, "DRM: cannot open %s: %s\n", cardPath.c_str(), strerror(errno));
235 692 return lease;
236 }
237 // drmModeCreateLease, and keeping the framebuffer outputs off the card
238 // while it is chosen.
239
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 std::unique_lock<std::mutex> masterLock(drmMasterLock);
240
241 // Master is taken under the same lock that records the lease, so an
242 // Unclaim() in between can't see an idle card and drop it again.
243
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 std::unique_lock<std::mutex> lock(s_lock);
244
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 Card& card = s_cards[cardPath];
245
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 EnsureMasterLocked(cardPath, card);
246 35 auto leasable = [&card](uint32_t id) { return !card.leased.count(id); };
247
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 auto unused = [&card](uint32_t id) { return !card.leased.count(id) && !card.claimed.count(id); };
248
249 // The cached state: a real probe would read the EDID over DDC.
250
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 drmModeConnectorPtr conn = drmModeGetConnectorCurrent(fd, connectorId);
251
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 drmModeResPtr res = drmModeGetResources(fd);
252 13 uint32_t crtcId = 0;
253 13 int crtcIndex = -1;
254
7/10
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 11 times.
✓ Branch 9 taken 2 times.
13 if (conn && res && leasable(connectorId)) {
255 // The CRTC already driving the connector, even one a framebuffer
256 // output on this connector uses (it stands aside while media plays).
257
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (conn->encoder_id) {
258
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 if (drmModeEncoderPtr enc = drmModeGetEncoder(fd, conn->encoder_id)) {
259
4/8
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
11 if (enc->crtc_id && leasable(enc->crtc_id)) {
260 11 crtcId = enc->crtc_id;
261 }
262
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 drmModeFreeEncoder(enc);
263 }
264 }
265 // Otherwise one that can drive it and nothing else is using.
266
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
11 for (int e = 0; !crtcId && e < conn->count_encoders; e++) {
267 ✗ drmModeEncoderPtr enc = drmModeGetEncoder(fd, conn->encoders[e]);
268 ✗ if (!enc) {
269 ✗ continue;
270 }
271 ✗ for (int i = 0; i < res->count_crtcs && !crtcId; i++) {
272 ✗ if ((enc->possible_crtcs & (1u << i)) && unused(res->crtcs[i])) {
273 ✗ crtcId = res->crtcs[i];
274 }
275 }
276 ✗ drmModeFreeEncoder(enc);
277 }
278
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 11 times.
34 for (int i = 0; i < res->count_crtcs; i++) {
279
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 12 times.
23 if (res->crtcs[i] == crtcId) {
280 11 crtcIndex = i;
281 }
282 }
283 }
284
285 13 uint32_t primary = 0, overlay = 0;
286
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13 if (crtcIndex >= 0) {
287
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 if (drmModePlaneResPtr planes = drmModeGetPlaneResources(fd)) {
288
2/2
✓ Branch 0 taken 252 times.
✓ Branch 1 taken 11 times.
263 for (uint32_t i = 0; i < planes->count_planes; i++) {
289 252 uint32_t pid = planes->planes[i];
290
1/2
✓ Branch 0 taken 252 times.
✗ Branch 1 not taken.
252 drmModePlanePtr plane = drmModeGetPlane(fd, pid);
291
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
252 if (!plane) {
292 ✗ continue;
293 }
294
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 152 times.
252 if (plane->possible_crtcs & (1u << crtcIndex)) {
295
1/2
✓ Branch 0 taken 100 times.
✗ Branch 1 not taken.
100 int type = PlaneType(fd, pid);
296 // The CRTC's own primary, which a modeset needs.
297
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (!primary && type == DRM_PLANE_TYPE_PRIMARY &&
298
7/10
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 89 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 11 times.
✓ Branch 9 taken 89 times.
111 plane->possible_crtcs == (1u << crtcIndex) && leasable(pid)) {
299 11 primary = pid;
300
7/10
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 11 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 11 times.
✓ Branch 9 taken 78 times.
89 } else if (!overlay && type == DRM_PLANE_TYPE_OVERLAY && unused(pid)) {
301 // An overlay, not the primary, for the picture:
302 // fbcon may be holding the primary.
303 11 overlay = pid;
304 }
305 }
306
1/2
✓ Branch 0 taken 252 times.
✗ Branch 1 not taken.
252 drmModeFreePlane(plane);
307 }
308
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 drmModeFreePlaneResources(planes);
309 }
310 }
311
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (res) {
312
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 drmModeFreeResources(res);
313 }
314
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (conn) {
315
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 drmModeFreeConnector(conn);
316 }
317
318
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13 lease.planeId = overlay ? (int)overlay : -1;
319
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
13 if (crtcId) {
320 11 lease.objects[lease.objectCount++] = (uint32_t)connectorId;
321 11 lease.objects[lease.objectCount++] = crtcId;
322
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (primary) {
323 11 lease.objects[lease.objectCount++] = primary;
324 }
325
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (overlay) {
326 11 lease.objects[lease.objectCount++] = overlay;
327 }
328 11 uint32_t lessee = 0;
329
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 int lfd = drmModeCreateLease(fd, lease.objects, lease.objectCount, O_CLOEXEC, &lessee);
330
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 if (lfd >= 0) {
331 11 lease.fd = lfd;
332 11 lease.isLease = true;
333 11 lease.lesseeId = lessee;
334
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 11 times.
55 for (int i = 0; i < lease.objectCount; i++) {
335
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 card.leased[lease.objects[i]]++;
336 }
337
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 LogDebug(VB_MEDIAOUT, "DRM: lease %u on %s: connector %d crtc %u primary %u overlay %u\n",
338 lessee, cardPath.c_str(), connectorId, crtcId, primary, overlay);
339
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 lock.unlock();
340
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 NotifyMediaHold(fd, (uint32_t)connectorId);
341 11 return lease; // the card's ref is held until ReleaseLease()
342 }
343 ✗ LogWarn(VB_MEDIAOUT, "DRM: lease on %s connector %d refused (%s); kmssink will share the card's fd\n",
344 cardPath.c_str(), connectorId, strerror(errno));
345 } else {
346
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 LogWarn(VB_MEDIAOUT, "DRM: no free CRTC for %s connector %d; kmssink will share the card's fd\n",
347 cardPath.c_str(), connectorId);
348 }
349 // No lease: kmssink on the shared, master fd, as before leases. Mark the
350 // connector and the overlay it gets as held all the same, so a
351 // framebuffer output there stands aside and no one else takes the plane.
352 // Each holder counts once, so a release drops only its own hold and a
353 // mark another holder of the connector still relies on stays.
354 2 lease.objectCount = 0;
355 2 lease.objects[lease.objectCount++] = (uint32_t)connectorId;
356
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 card.leased[(uint32_t)connectorId]++;
357
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (overlay) {
358 ✗ lease.objects[lease.objectCount++] = overlay;
359 ✗ card.leased[overlay]++;
360 }
361 2 lease.fd = fd;
362
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 lock.unlock();
363
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 NotifyMediaHold(fd, (uint32_t)connectorId);
364 2 return lease;
365 13 }
366
367 768 void RetireLease(Lease& lease) {
368
4/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 761 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
768 if (lease.fd < 0 || lease.connectorId <= 0 || lease.retired) {
369 761 return;
370 }
371
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 std::lock_guard<std::mutex> lock(s_lock);
372
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 s_cards[lease.cardPath].retiring[(uint32_t)lease.connectorId]++;
373 7 lease.retired = true;
374 7 }
375
376 771 void ReleaseLease(Lease& lease) {
377
2/2
✓ Branch 0 taken 759 times.
✓ Branch 1 taken 12 times.
771 if (lease.fd < 0) {
378 759 return;
379 }
380 {
381
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 std::lock_guard<std::mutex> lock(s_lock);
382
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 auto it = s_cards.find(lease.cardPath);
383
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (it != s_cards.end()) {
384
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 auto retiring = it->second.retiring.find((uint32_t)lease.connectorId);
385
5/6
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 5 times.
12 if (lease.retired && retiring != it->second.retiring.end()) {
386
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if (--retiring->second <= 0) {
387
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 it->second.retiring.erase(retiring);
388 }
389 7 s_retired.notify_all();
390 }
391
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 12 times.
54 for (int i = 0; i < lease.objectCount; i++) {
392
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 auto held = it->second.leased.find(lease.objects[i]);
393
5/6
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 40 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 40 times.
✓ Branch 5 taken 2 times.
42 if (held != it->second.leased.end() && --held->second <= 0) {
394
1/2
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
40 it->second.leased.erase(held);
395 }
396 }
397 12 DropMasterIfIdleLocked(it->second);
398 }
399 12 }
400 12 int cardFd = -1;
401
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 if (lease.isLease) {
402 // Revokes the lease and frees what kmssink left behind on it.
403 10 close(lease.fd);
404 } else {
405 2 cardFd = lease.fd;
406 }
407
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (lease.connectorId > 0) {
408 // The card's fd stays open on our ref until the Release() below.
409
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 if (cardFd < 0) {
410
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 std::lock_guard<std::mutex> lock(s_lock);
411
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 auto it = s_cards.find(lease.cardPath);
412
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 cardFd = it != s_cards.end() ? it->second.fd : -1;
413 10 }
414
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (cardFd >= 0) {
415
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 std::lock_guard<std::mutex> masterLock(drmMasterLock);
416
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 NotifyMediaHold(cardFd, (uint32_t)lease.connectorId);
417 12 }
418 }
419 12 Release(lease.cardPath);
420 12 lease = Lease();
421 }
422
423 } // namespace DrmCards
424
425 #else // no libdrm (macOS)
426
427 namespace DrmCards {
428 int Acquire(const std::string&) { return -1; }
429 void Release(const std::string&) {}
430 bool EnsureMaster(int) { return false; }
431 void Claim(int, uint32_t) {}
432 void Unclaim(int, uint32_t) {}
433 bool InUse(int, uint32_t) { return false; }
434 bool HeldByMedia(int, uint32_t) { return false; }
435 void SetMediaHoldListener(void (*)(int, uint32_t)) {}
436 Lease AcquireLease(const std::string& cardPath, int) {
437 Lease l;
438 l.cardPath = cardPath;
439 return l;
440 }
441 void RetireLease(Lease&) {}
442 void ReleaseLease(Lease& lease) { lease = Lease(); }
443 } // namespace DrmCards
444
445 #endif
446