MythTV  master
mythdrmdevice.cpp
Go to the documentation of this file.
1 // Qt
2 #include <QDir>
3 #include <QMutex>
4 #include <QtGlobal>
5 #include <QScreen>
6 
7 // MythTV
8 #include "mythedid.h"
9 #include "mythdrmdevice.h"
10 
11 // Std
12 #include <unistd.h>
13 #include <fcntl.h>
14 
15 #define LOC (QString("%1: ").arg(m_deviceName))
16 
17 MythDRMDevice::MythDRMDevice(QScreen *qScreen, const QString& Device)
18  : ReferenceCounter("DRMDev"),
19  m_screen(qScreen),
20  m_deviceName(Device),
21  m_verbose(Device.isEmpty() ? LOG_INFO : LOG_DEBUG)
22 {
23  if (!Open())
24  {
25  LOG(VB_GENERAL, m_verbose, LOC + "Failed to open");
26  return;
27  }
28 
29  m_valid = true;
30  Authenticate();
31  if (m_authenticated)
32  LOG(VB_GENERAL, m_verbose, LOC + "Authenticated");
33  else
34  LOG(VB_GENERAL, m_verbose, LOC + "Not authenticated - mode switching not available");
35 }
36 
38 {
39  Close();
40 }
41 
43 {
44  if (m_deviceName.isEmpty())
46  if (m_deviceName.isEmpty())
47  return false;
48  m_fd = open(m_deviceName.toLocal8Bit().constData(), O_RDWR);
49 
50  if (m_fd < 0)
51  return false;
52 
53  if (!Initialise())
54  {
55  Close();
56  return false;
57  }
58  return true;
59 }
60 
62 {
63  if (m_fd)
64  {
65  if (m_connector)
66  drmModeFreeConnector(m_connector);
67  if (m_crtc)
68  drmModeFreeCrtc(m_crtc);
69  if (m_resources)
70  drmModeFreeResources(m_resources);
71  m_resources = nullptr;
72  m_connector = nullptr;
73  m_crtc = nullptr;
74  close(m_fd);
75  LOG(VB_GENERAL, m_verbose, LOC + "Closed");
76  }
77  m_fd = 0;
78 }
79 
80 bool MythDRMDevice::IsValid(void) const
81 {
82  return m_valid;
83 }
84 
85 QString MythDRMDevice::GetSerialNumber(void) const
86 {
87  return m_serialNumber;
88 }
89 
90 QScreen* MythDRMDevice::GetScreen(void) const
91 {
92  return m_screen;
93 }
94 
96 {
97  return m_resolution;
98 }
99 
101 {
102  return m_physicalSize;
103 }
104 
106 {
107  return m_refreshRate;
108 }
109 
111 {
112  return m_authenticated;
113 }
114 
116 {
117  return m_edid;
118 }
119 
120 static QString GetConnectorName(drmModeConnector *Connector)
121 {
122  if (!Connector)
123  return "Unknown";
124  static const std::array<const std::string,DRM_MODE_CONNECTOR_DPI + 1> connectorNames
125  { "None", "VGA", "DVI", "DVI", "DVI", "Composite", "TV", "LVDS",
126  "CTV", "DIN", "DP", "HDMI", "HDMI", "TV", "eDP", "Virtual", "DSI", "DPI"
127  };
128  uint32_t type = qMin(Connector->connector_type, static_cast<uint32_t>(DRM_MODE_CONNECTOR_DPI));
129  return QString("%1%2").arg(QString::fromStdString(connectorNames[type])).arg(Connector->connector_type_id);
130 }
131 
133 {
134  if (!m_fd || m_authenticated)
135  return;
136 
137  int ret = drmSetMaster(m_fd);
138  m_authenticated = ret >= 0;
139 
140  if (!m_authenticated)
141  {
142  drm_magic_t magic = 0;
143  m_authenticated = drmGetMagic(m_fd, &magic) == 0 && drmAuthMagic(m_fd, magic) == 0;
144  }
145 }
146 
148 {
149  if (!m_fd)
150  return false;
151 
152  // Find the serial number of the display we are connected to
153  auto serial = m_screen->serialNumber();
154  if (serial.isEmpty())
155  {
156  // No serial number either means an older version of Qt or the EDID
157  // is not available for some reason - in which case there is no point
158  // in trying to use it anyway.
159  LOG(VB_GENERAL, m_verbose, LOC + "QScreen has no serial number.");
160  LOG(VB_GENERAL, m_verbose, LOC + "Will use first suitable connected device");
161  }
162 
163  // Retrieve full details for the device
164  m_resources = drmModeGetResources(m_fd);
165  if (!m_resources)
166  {
167  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Failed to retrieve device detail");
168  return false;
169  }
170 
171  // Find the right connector
172  drmModeConnectorPtr connector = nullptr;
173  for (int i = 0; i < m_resources->count_connectors; ++i)
174  {
175  connector = drmModeGetConnector(m_fd, m_resources->connectors[i]);
176  if (!connector)
177  continue;
178  if (connector->connection == DRM_MODE_CONNECTED)
179  {
180  if (serial.isEmpty())
181  {
182  m_connector = connector;
183  break;
184  }
185 
186  // Does the connected display have the serial number we are looking for?
187  drmModePropertyBlobPtr edidblob = GetBlobProperty(connector, "EDID");
188  if (edidblob)
189  {
190  MythEDID edid(reinterpret_cast<const char *>(edidblob->data),
191  static_cast<int>(edidblob->length));
192  drmModeFreePropertyBlob(edidblob);
193  if (edid.Valid() && edid.SerialNumbers().contains(serial))
194  {
195  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Matched connector with serial '%1'")
196  .arg(serial));
197  m_connector = connector;
198  m_physicalSize = QSize(static_cast<int>(connector->mmWidth),
199  static_cast<int>(connector->mmHeight));
200  m_serialNumber = serial;
201  m_edid = edid;
202  break;
203  }
204  if (!edid.Valid())
205  LOG(VB_GENERAL, m_verbose, LOC + "Connected device has invalid EDID");
206 
207  if (m_connector && !m_serialNumber.isEmpty())
208  break;
209  }
210  else
211  {
212  LOG(VB_GENERAL, m_verbose, LOC + "Connected device has no EDID");
213  }
214  }
215  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Ignoring disconnected connector %1")
216  .arg(GetConnectorName(connector)));
217  drmModeFreeConnector(connector);
218  connector = nullptr;
219  }
220 
221  if (!m_connector)
222  {
223  LOG(VB_GENERAL, LOG_DEBUG, LOC + "No connected connectors");
224  return false;
225  }
226 
227  QString cname = GetConnectorName(m_connector);
228  LOG(VB_GENERAL, m_verbose, LOC + QString("Selected connector %1").arg(cname));
229 
230  // Find the encoder for the connector
231  drmModeEncoder* encoder = nullptr;
232  for (int i = 0; i < m_resources->count_encoders; ++i)
233  {
234  encoder = drmModeGetEncoder(m_fd, m_resources->encoders[i]);
235  if (!encoder)
236  continue;
237  if (encoder->encoder_id == m_connector->encoder_id)
238  break;
239  drmModeFreeEncoder(encoder);
240  encoder = nullptr;
241  }
242 
243  if (!encoder)
244  {
245  LOG(VB_GENERAL, m_verbose, LOC + QString("Failed to find encoder for %1").arg(cname));
246  return false;
247  }
248 
249  // Find the CRTC for the encoder...
250  drmModeCrtc* crtc = nullptr;
251  for (int i = 0; i < m_resources->count_crtcs; ++i)
252  {
253  const uint32_t crtcmask = 1 << i;
254  if (!(encoder->possible_crtcs & crtcmask))
255  continue;
256  crtc = drmModeGetCrtc(m_fd, m_resources->crtcs[i]);
257  if (!crtc)
258  continue;
259  if (crtc->crtc_id == encoder->crtc_id)
260  {
261  m_crtcIdx = i;
262  break;
263  }
264  drmModeFreeCrtc(crtc);
265  crtc = nullptr;
266  }
267 
268  drmModeFreeEncoder(encoder);
269 
270  if (!crtc)
271  {
272  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Failed to find crtc for encoder");
273  return true;
274  }
275 
276  m_crtc = crtc;
277  m_resolution = QSize(static_cast<int>(m_crtc->width),
278  static_cast<int>(m_crtc->height));
279  if (m_crtc->mode_valid)
280  {
281  drmModeModeInfo mode = m_crtc->mode;
282  m_refreshRate = (mode.clock * 1000.0) / (mode.htotal * mode.vtotal);
283  if (mode.flags & DRM_MODE_FLAG_INTERLACE)
284  m_refreshRate *= 2.0;
285  }
286 
287  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Initialised");
288  return true;
289 }
290 
292 {
293  if (!m_screen)
294  return QString();
295 
296  // Iterate over /dev/dri/card*
297  const QString root(QString(DRM_DIR_NAME) + "/");
298  QDir dir(root);
299  QStringList namefilters;
300 #ifdef __OpenBSD__
301  namefilters.append("drm*");
302 #else
303  namefilters.append("card*");
304 #endif
305  auto devices = dir.entryList(namefilters, QDir::Files | QDir::System);
306 
307  // Nothing to see
308  if (devices.isEmpty())
309  {
310  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("No DRM devices found using '%1'")
311  .arg(root + namefilters.first()));
312  return QString();
313  }
314 
315  // Only one device - return it
316  if (devices.size() == 1)
317  return root + devices.first();
318 
319  // Use the serial number from the current QScreen to select a suitable device
320  auto serial = m_screen->serialNumber();
321  if (serial.isEmpty())
322  {
323  LOG(VB_GENERAL, m_verbose, LOC + "No serial number to search for");
324  return QString();
325  }
326 
327  for (const auto& dev : qAsConst(devices))
328  {
329  QString device = root + dev;
330  if (!ConfirmDevice(device))
331  {
332  LOG(VB_GENERAL, m_verbose, LOC + "Failed to confirm device");
333  continue;
334  }
335  MythDRMDevice drmdevice(m_screen, device);
336  if (drmdevice.GetSerialNumber() == serial)
337  return device;
338  }
339  return QString();
340 }
341 
343 {
344  bool result = false;
345  int fd = open(Device.toLocal8Bit().constData(), O_RDWR);
346  if (fd < 0)
347  return result;
348  drmVersionPtr version = drmGetVersion(fd);
349  if (version)
350  {
351  drmFreeVersion(version);
352  result = true;
353  }
354  close(fd);
355  return result;
356 }
357 
358 drmModePropertyBlobPtr MythDRMDevice::GetBlobProperty(drmModeConnectorPtr Connector, const QString& Property) const
359 {
360  drmModePropertyBlobPtr result = nullptr;
361  if (!Connector || Property.isEmpty())
362  return result;
363 
364  for (int i = 0; i < Connector->count_props; ++i)
365  {
366  drmModePropertyPtr propid = drmModeGetProperty(m_fd, Connector->props[i]);
367  if ((propid->flags & DRM_MODE_PROP_BLOB) && propid->name == Property)
368  {
369  auto blobid = static_cast<uint32_t>(Connector->prop_values[i]);
370  result = drmModeGetPropertyBlob(m_fd, blobid);
371  }
372  drmModeFreeProperty(propid);
373  if (result)
374  break;
375  }
376  return result;
377 }
MythDRMDevice::m_authenticated
bool m_authenticated
Definition: mythdrmdevice.h:50
MythDRMDevice::m_screen
QScreen * m_screen
Definition: mythdrmdevice.h:47
MythDRMDevice::Authenticate
void Authenticate(void)
Definition: mythdrmdevice.cpp:132
root
QDomElement root
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:659
MythDRMDevice::Authenticated
bool Authenticated(void) const
Definition: mythdrmdevice.cpp:110
MythDRMDevice::Open
bool Open(void)
Definition: mythdrmdevice.cpp:42
MythDRMDevice::IsValid
bool IsValid(void) const
Definition: mythdrmdevice.cpp:80
MythDRMDevice::m_resources
drmModeRes * m_resources
Definition: mythdrmdevice.h:51
MythDRMDevice::GetSerialNumber
QString GetSerialNumber(void) const
Definition: mythdrmdevice.cpp:85
arg
arg(title).arg(filename).arg(doDelete))
MythDRMDevice::Close
void Close(void)
Definition: mythdrmdevice.cpp:61
MythDRMDevice::m_resolution
QSize m_resolution
Definition: mythdrmdevice.h:53
MythDRMDevice::m_serialNumber
QString m_serialNumber
Definition: mythdrmdevice.h:56
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
Device
A device containing images (ie. USB stick, CD, storage group etc)
Definition: imagemanager.cpp:34
MythEDID::Valid
bool Valid(void) const
Definition: mythedid.cpp:32
Property
Definition: upnpcdsobjects.h:45
MythDRMDevice::m_valid
bool m_valid
Definition: mythdrmdevice.h:46
DRM_MODE_FLAG_INTERLACE
#define DRM_MODE_FLAG_INTERLACE
Definition: mythdisplaymutter.cpp:10
close
#define close
Definition: compat.h:16
MythDRMDevice::GetBlobProperty
drmModePropertyBlobPtr GetBlobProperty(drmModeConnectorPtr Connector, const QString &Property) const
Definition: mythdrmdevice.cpp:358
MythDRMDevice::m_verbose
LogLevel_t m_verbose
Definition: mythdrmdevice.h:59
GetConnectorName
static QString GetConnectorName(drmModeConnector *Connector)
Definition: mythdrmdevice.cpp:120
MythDRMDevice::MythDRMDevice
MythDRMDevice(QScreen *qScreen, const QString &Device=QString())
Definition: mythdrmdevice.cpp:17
MythDRMDevice::GetResolution
QSize GetResolution(void) const
Definition: mythdrmdevice.cpp:95
MythDRMDevice::m_connector
drmModeConnector * m_connector
Definition: mythdrmdevice.h:52
MythDRMDevice
Definition: mythdrmdevice.h:19
MythDRMDevice::m_crtc
drmModeCrtc * m_crtc
Definition: mythdrmdevice.h:57
MythDRMDevice::m_edid
MythEDID m_edid
Definition: mythdrmdevice.h:60
MythDRMDevice::m_deviceName
QString m_deviceName
Definition: mythdrmdevice.h:48
LOC
#define LOC
Definition: mythdrmdevice.cpp:15
MythDRMDevice::GetEDID
MythEDID GetEDID(void)
Definition: mythdrmdevice.cpp:115
MythDRMDevice::m_refreshRate
double m_refreshRate
Definition: mythdrmdevice.h:55
MythDRMDevice::GetScreen
QScreen * GetScreen(void) const
Definition: mythdrmdevice.cpp:90
mythedid.h
dir
QDir dir
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1177
MythDRMDevice::Initialise
bool Initialise(void)
Definition: mythdrmdevice.cpp:147
MythDRMDevice::m_crtcIdx
int m_crtcIdx
Definition: mythdrmdevice.h:58
mythdrmdevice.h
MythDRMDevice::~MythDRMDevice
~MythDRMDevice() override
Definition: mythdrmdevice.cpp:37
MythDRMDevice::GetRefreshRate
double GetRefreshRate(void) const
Definition: mythdrmdevice.cpp:105
MythDRMDevice::GetPhysicalSize
QSize GetPhysicalSize(void) const
Definition: mythdrmdevice.cpp:100
MythDRMDevice::m_physicalSize
QSize m_physicalSize
Definition: mythdrmdevice.h:54
MythDRMDevice::m_fd
int m_fd
Definition: mythdrmdevice.h:49
nv_python_libs.bbciplayer.bbciplayer_api.version
string version
Definition: bbciplayer_api.py:81
MythDRMDevice::FindBestDevice
QString FindBestDevice(void)
Definition: mythdrmdevice.cpp:291
MythEDID::SerialNumbers
QStringList SerialNumbers(void) const
Definition: mythedid.cpp:37
ReferenceCounter
General purpose reference counter.
Definition: referencecounter.h:27
MythEDID
Definition: mythedid.h:18
MythDRMDevice::ConfirmDevice
static bool ConfirmDevice(const QString &Device)
Definition: mythdrmdevice.cpp:342