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 QString connectorNames[DRM_MODE_CONNECTOR_DPI + 1] =
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(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 = QString();
154 #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)
155  serial = m_screen->serialNumber();
156 #endif
157  if (serial.isEmpty())
158  {
159  // No serial number either means an older version of Qt or the EDID
160  // is not available for some reason - in which case there is no point
161  // in trying to use it anyway.
162  LOG(VB_GENERAL, m_verbose, LOC + "QScreen has no serial number.");
163  LOG(VB_GENERAL, m_verbose, LOC + "Will use first suitable connected device");
164  }
165 
166  // Retrieve full details for the device
167  m_resources = drmModeGetResources(m_fd);
168  if (!m_resources)
169  {
170  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Failed to retrieve device detail");
171  return false;
172  }
173 
174  // Find the right connector
175  drmModeConnectorPtr connector = nullptr;
176  for (int i = 0; i < m_resources->count_connectors; ++i)
177  {
178  connector = drmModeGetConnector(m_fd, m_resources->connectors[i]);
179  if (!connector)
180  continue;
181  if (connector->connection == DRM_MODE_CONNECTED)
182  {
183  if (serial.isEmpty())
184  {
185  m_connector = connector;
186  break;
187  }
188  else
189  {
190  // Does the connected display have the serial number we are looking for?
191  drmModePropertyBlobPtr edidblob = GetBlobProperty(connector, "EDID");
192  if (edidblob)
193  {
194  MythEDID edid(reinterpret_cast<const char *>(edidblob->data),
195  static_cast<int>(edidblob->length));
196  drmModeFreePropertyBlob(edidblob);
197  if (edid.Valid() && edid.SerialNumbers().contains(serial))
198  {
199  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Matched connector with serial '%1'")
200  .arg(serial));
201  m_connector = connector;
202  m_physicalSize = QSize(static_cast<int>(connector->mmWidth),
203  static_cast<int>(connector->mmHeight));
204  m_serialNumber = serial;
205  m_edid = edid;
206  break;
207  }
208  if (!edid.Valid())
209  LOG(VB_GENERAL, m_verbose, LOC + "Connected device has invalid EDID");
210 
211  if (m_connector && !m_serialNumber.isEmpty())
212  break;
213  }
214  else
215  {
216  LOG(VB_GENERAL, m_verbose, LOC + "Connected device has no EDID");
217  }
218  }
219  }
220  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Ignoring disconnected connector %1")
221  .arg(GetConnectorName(connector)));
222  drmModeFreeConnector(connector);
223  connector = nullptr;
224  }
225 
226  if (!m_connector)
227  {
228  LOG(VB_GENERAL, LOG_DEBUG, LOC + "No connected connectors");
229  return false;
230  }
231 
232  QString cname = GetConnectorName(m_connector);
233  LOG(VB_GENERAL, m_verbose, LOC + QString("Selected connector %1").arg(cname));
234 
235  // Find the encoder for the connector
236  drmModeEncoder* encoder = nullptr;
237  for (int i = 0; i < m_resources->count_encoders; ++i)
238  {
239  encoder = drmModeGetEncoder(m_fd, m_resources->encoders[i]);
240  if (!encoder)
241  continue;
242  if (encoder->encoder_id == m_connector->encoder_id)
243  break;
244  drmModeFreeEncoder(encoder);
245  encoder = nullptr;
246  }
247 
248  if (!encoder)
249  {
250  LOG(VB_GENERAL, m_verbose, LOC + QString("Failed to find encoder for %1").arg(cname));
251  return false;
252  }
253 
254  // Find the CRTC for the encoder...
255  drmModeCrtc* crtc = nullptr;
256  for (int i = 0; i < m_resources->count_crtcs; ++i)
257  {
258  const uint32_t crtcmask = 1 << i;
259  if (!(encoder->possible_crtcs & crtcmask))
260  continue;
261  crtc = drmModeGetCrtc(m_fd, m_resources->crtcs[i]);
262  if (!crtc)
263  continue;
264  if (crtc->crtc_id == encoder->crtc_id)
265  {
266  m_crtcIdx = i;
267  break;
268  }
269  drmModeFreeCrtc(crtc);
270  crtc = nullptr;
271  }
272 
273  drmModeFreeEncoder(encoder);
274 
275  if (!crtc)
276  {
277  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Failed to find crtc for encoder");
278  return true;
279  }
280 
281  m_crtc = crtc;
282  m_resolution = QSize(static_cast<int>(m_crtc->width),
283  static_cast<int>(m_crtc->height));
284  if (m_crtc->mode_valid)
285  {
286  drmModeModeInfo mode = m_crtc->mode;
287  m_refreshRate = (mode.clock * 1000.0) / (mode.htotal * mode.vtotal);
288  if (mode.flags & DRM_MODE_FLAG_INTERLACE)
289  m_refreshRate *= 2.0;
290  }
291 
292  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Initialised");
293  return true;
294 }
295 
297 {
298  if (!m_screen)
299  return QString();
300 
301  // Iterate over /dev/dri/card*
302  const QString root(QString(DRM_DIR_NAME) + "/");
303  QDir dir(root);
304  QStringList namefilters;
305 #ifdef __OpenBSD__
306  namefilters.append("drm*");
307 #else
308  namefilters.append("card*");
309 #endif
310  auto devices = dir.entryList(namefilters, QDir::Files | QDir::System);
311 
312  // Nothing to see
313  if (devices.isEmpty())
314  {
315  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("No DRM devices found using '%1'")
316  .arg(root + namefilters.first()));
317  return QString();
318  }
319 
320  // Only one device - return it
321  if (devices.size() == 1)
322  return root + devices.first();
323 
324  // Use the serial number from the current QScreen to select a suitable device
325  auto serial = QString();
326 #if QT_VERSION >= QT_VERSION_CHECK(5,9,0)
327  serial = m_screen->serialNumber();
328 #endif
329  if (serial.isEmpty())
330  {
331  LOG(VB_GENERAL, m_verbose, LOC + "No serial number to search for");
332  return QString();
333  }
334 
335  foreach (const auto & dev, devices)
336  {
337  QString device = root + dev;
338  if (!ConfirmDevice(device))
339  {
340  LOG(VB_GENERAL, m_verbose, LOC + "Failed to confirm device");
341  continue;
342  }
343  MythDRMDevice drmdevice(m_screen, device);
344  if (drmdevice.GetSerialNumber() == serial)
345  return device;
346  }
347  return QString();
348 }
349 
351 {
352  bool result = false;
353  int fd = open(Device.toLocal8Bit().constData(), O_RDWR);
354  if (fd < 0)
355  return result;
356  drmVersionPtr version = drmGetVersion(fd);
357  if (version)
358  {
359  drmFreeVersion(version);
360  result = true;
361  }
362  close(fd);
363  return result;
364 }
365 
366 drmModePropertyBlobPtr MythDRMDevice::GetBlobProperty(drmModeConnectorPtr Connector, const QString& Property)
367 {
368  drmModePropertyBlobPtr result = nullptr;
369  if (!Connector || Property.isEmpty())
370  return result;
371 
372  for (int i = 0; i < Connector->count_props; ++i)
373  {
374  drmModePropertyPtr propid = drmModeGetProperty(m_fd, Connector->props[i]);
375  if ((propid->flags & DRM_MODE_PROP_BLOB) && propid->name == Property)
376  {
377  auto blobid = static_cast<uint32_t>(Connector->prop_values[i]);
378  result = drmModeGetPropertyBlob(m_fd, blobid);
379  }
380  drmModeFreeProperty(propid);
381  if (result)
382  break;
383  }
384  return result;
385 }
MythEDID GetEDID(void)
bool Open(void)
bool IsValid(void) const
General purpose reference counter.
drmModeRes * m_resources
Definition: mythdrmdevice.h:51
QSize m_physicalSize
Definition: mythdrmdevice.h:54
void Authenticate(void)
static QString GetConnectorName(drmModeConnector *Connector)
QSize GetPhysicalSize(void) const
LogLevel_t m_verbose
Definition: mythdrmdevice.h:59
QString m_serialNumber
Definition: mythdrmdevice.h:56
QScreen * GetScreen(void) const
A device containing images (ie. USB stick, CD, storage group etc)
MythDRMDevice(QScreen *qScreen, const QString &Device=QString())
drmModeConnector * m_connector
Definition: mythdrmdevice.h:52
#define close
Definition: compat.h:16
MythEDID m_edid
Definition: mythdrmdevice.h:60
drmModeCrtc * m_crtc
Definition: mythdrmdevice.h:57
QString GetSerialNumber(void) const
QString m_deviceName
Definition: mythdrmdevice.h:48
bool Authenticated(void) const
QString FindBestDevice(void)
static bool ConfirmDevice(const QString &Device)
#define LOC
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
double m_refreshRate
Definition: mythdrmdevice.h:55
double GetRefreshRate(void) const
bool Valid(void) const
Definition: mythedid.cpp:32
QSize m_resolution
Definition: mythdrmdevice.h:53
~MythDRMDevice() override
bool m_authenticated
Definition: mythdrmdevice.h:50
void Close(void)
QSize GetResolution(void) const
QStringList SerialNumbers(void) const
Definition: mythedid.cpp:37
QScreen * m_screen
Definition: mythdrmdevice.h:47
bool Initialise(void)
drmModePropertyBlobPtr GetBlobProperty(drmModeConnectorPtr Connector, const QString &Property)