| 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 |