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 MythDRMPtr MythDRMDevice::Create(QScreen *qScreen, const QString &Device)
18 {
19  // note - cannot use make_shared with a protected constructor
20  return std::shared_ptr<MythDRMDevice>(new MythDRMDevice(qScreen, Device));
21 }
22 
23 MythDRMDevice::MythDRMDevice(QScreen *qScreen, const QString& Device)
24  : m_screen(qScreen),
25  m_deviceName(Device),
26  m_verbose(Device.isEmpty() ? LOG_INFO : LOG_DEBUG)
27 {
28  if (!Open())
29  {
30  LOG(VB_GENERAL, m_verbose, LOC + "Failed to open");
31  return;
32  }
33 
34  m_valid = true;
35  Authenticate();
36  if (m_authenticated)
37  LOG(VB_GENERAL, m_verbose, LOC + "Authenticated");
38  else
39  LOG(VB_GENERAL, m_verbose, LOC + "Not authenticated - mode switching not available");
40 }
41 
43 {
44  Close();
45 }
46 
48 {
49  if (m_deviceName.isEmpty())
51  if (m_deviceName.isEmpty())
52  return false;
53  m_fd = open(m_deviceName.toLocal8Bit().constData(), O_RDWR);
54 
55  if (m_fd < 0)
56  return false;
57 
58  if (!Initialise())
59  {
60  Close();
61  return false;
62  }
63  return true;
64 }
65 
67 {
68  if (m_fd)
69  {
70  if (m_connector)
71  drmModeFreeConnector(m_connector);
72  if (m_crtc)
73  drmModeFreeCrtc(m_crtc);
74  if (m_resources)
75  drmModeFreeResources(m_resources);
76  m_resources = nullptr;
77  m_connector = nullptr;
78  m_crtc = nullptr;
79  close(m_fd);
80  LOG(VB_GENERAL, m_verbose, LOC + "Closed");
81  }
82  m_fd = 0;
83 }
84 
86 {
87  return m_valid;
88 }
89 
91 {
92  return m_serialNumber;
93 }
94 
95 QScreen* MythDRMDevice::GetScreen() const
96 {
97  return m_screen;
98 }
99 
101 {
102  return m_resolution;
103 }
104 
106 {
107  return m_physicalSize;
108 }
109 
111 {
112  return m_refreshRate;
113 }
114 
116 {
117  return m_authenticated;
118 }
119 
121 {
122  return m_edid;
123 }
124 
125 static QString GetConnectorName(drmModeConnector *Connector)
126 {
127  if (!Connector)
128  return "Unknown";
129  static const std::array<const std::string,DRM_MODE_CONNECTOR_DPI + 1> connectorNames
130  { "None", "VGA", "DVI", "DVI", "DVI", "Composite", "TV", "LVDS",
131  "CTV", "DIN", "DP", "HDMI", "HDMI", "TV", "eDP", "Virtual", "DSI", "DPI"
132  };
133  uint32_t type = qMin(Connector->connector_type, static_cast<uint32_t>(DRM_MODE_CONNECTOR_DPI));
134  return QString("%1%2").arg(QString::fromStdString(connectorNames[type])).arg(Connector->connector_type_id);
135 }
136 
138 {
139  if (!m_fd || m_authenticated)
140  return;
141 
142  int ret = drmSetMaster(m_fd);
143  m_authenticated = ret >= 0;
144 
145  if (!m_authenticated)
146  {
147  drm_magic_t magic = 0;
148  m_authenticated = drmGetMagic(m_fd, &magic) == 0 && drmAuthMagic(m_fd, magic) == 0;
149  }
150 }
151 
153 {
154  if (!m_fd)
155  return false;
156 
157  // Find the serial number of the display we are connected to
158  auto serial = m_screen->serialNumber();
159  if (serial.isEmpty())
160  {
161  // No serial number either means an older version of Qt or the EDID
162  // is not available for some reason - in which case there is no point
163  // in trying to use it anyway.
164  LOG(VB_GENERAL, m_verbose, LOC + "QScreen has no serial number.");
165  LOG(VB_GENERAL, m_verbose, LOC + "Will use first suitable connected device");
166  }
167 
168  // Retrieve full details for the device
169  m_resources = drmModeGetResources(m_fd);
170  if (!m_resources)
171  {
172  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Failed to retrieve device detail");
173  return false;
174  }
175 
176  // Find the right connector
177  drmModeConnectorPtr connector = nullptr;
178  for (int i = 0; i < m_resources->count_connectors; ++i)
179  {
180  connector = drmModeGetConnector(m_fd, m_resources->connectors[i]);
181  if (!connector)
182  continue;
183  if (connector->connection == DRM_MODE_CONNECTED)
184  {
185  if (serial.isEmpty())
186  {
187  m_connector = connector;
188  break;
189  }
190 
191  // Does the connected display have the serial number we are looking for?
192  drmModePropertyBlobPtr edidblob = GetBlobProperty(connector, "EDID");
193  if (edidblob)
194  {
195  MythEDID edid(reinterpret_cast<const char *>(edidblob->data),
196  static_cast<int>(edidblob->length));
197  drmModeFreePropertyBlob(edidblob);
198  if (edid.Valid() && edid.SerialNumbers().contains(serial))
199  {
200  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Matched connector with serial '%1'")
201  .arg(serial));
202  m_connector = connector;
203  m_physicalSize = QSize(static_cast<int>(connector->mmWidth),
204  static_cast<int>(connector->mmHeight));
205  m_serialNumber = serial;
206  m_edid = edid;
207  break;
208  }
209  if (!edid.Valid())
210  LOG(VB_GENERAL, m_verbose, LOC + "Connected device has invalid EDID");
211 
212  if (m_connector && !m_serialNumber.isEmpty())
213  break;
214  }
215  else
216  {
217  LOG(VB_GENERAL, m_verbose, LOC + "Connected device has no EDID");
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), static_cast<int>(m_crtc->height));
283  if (m_crtc->mode_valid)
284  {
285  drmModeModeInfo mode = m_crtc->mode;
286  m_refreshRate = (mode.clock * 1000.0) / (mode.htotal * mode.vtotal);
287  if (mode.flags & DRM_MODE_FLAG_INTERLACE)
288  m_refreshRate *= 2.0;
289  }
290 
291  LOG(VB_GENERAL, LOG_DEBUG, LOC + "Initialised");
292  return true;
293 }
294 
296 {
297  if (!m_screen)
298  return QString();
299 
300  // Iterate over /dev/dri/card*
301  const QString root(QString(DRM_DIR_NAME) + "/");
302  QDir dir(root);
303  QStringList namefilters;
304 #ifdef __OpenBSD__
305  namefilters.append("drm*");
306 #else
307  namefilters.append("card*");
308 #endif
309  auto devices = dir.entryList(namefilters, QDir::Files | QDir::System);
310 
311  // Nothing to see
312  if (devices.isEmpty())
313  {
314  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("No DRM devices found using '%1'")
315  .arg(root + namefilters.first()));
316  return QString();
317  }
318 
319  // Only one device - return it
320  if (devices.size() == 1)
321  return root + devices.first();
322 
323  // Use the serial number from the current QScreen to select a suitable device
324  auto serial = m_screen->serialNumber();
325  if (serial.isEmpty())
326  {
327  LOG(VB_GENERAL, m_verbose, LOC + "No serial number to search for");
328  return QString();
329  }
330 
331  for (const auto& dev : qAsConst(devices))
332  {
333  QString device = root + dev;
334  if (!ConfirmDevice(device))
335  {
336  LOG(VB_GENERAL, m_verbose, LOC + "Failed to confirm device");
337  continue;
338  }
339  MythDRMDevice drmdevice(m_screen, device);
340  if (drmdevice.GetSerialNumber() == serial)
341  return device;
342  }
343  return QString();
344 }
345 
347 {
348  bool result = false;
349  int fd = open(Device.toLocal8Bit().constData(), O_RDWR);
350  if (fd < 0)
351  return result;
352  drmVersionPtr version = drmGetVersion(fd);
353  if (version)
354  {
355  drmFreeVersion(version);
356  result = true;
357  }
358  close(fd);
359  return result;
360 }
361 
362 bool MythDRMDevice::SetEnumProperty(const QString &Property, uint64_t Value)
363 {
364  bool result = false;
365  if (m_connector && !Property.isEmpty())
366  {
367  for (int i = 0; i < m_connector->count_props; ++i)
368  {
369  drmModePropertyPtr prop = drmModeGetProperty(m_fd, m_connector->props[i]);
370  if ((prop->flags & DRM_MODE_PROP_ENUM) && prop->name == Property)
371  {
372  int ret = drmModeConnectorSetProperty(m_fd, m_connector->connector_id, prop->prop_id, Value);
373  result = ret == 0;
374  if (ret != 0)
375  LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("drmModeConnectorSetProperty error: %1").arg(strerror(-ret)));
376  }
377  drmModeFreeProperty(prop);
378  }
379  }
380  if (!result)
381  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Failed to set property '%1'").arg(Property));
382  return result;
383 }
384 
386 {
387  DRMEnum result{0};
388  if (!m_connector || Property.isEmpty())
389  return result;
390 
391  for (int i = 0; i < m_connector->count_props; ++i)
392  {
393  drmModePropertyPtr prop = drmModeGetProperty(m_fd, m_connector->props[i]);
394  if ((prop->flags & DRM_MODE_PROP_ENUM) && prop->name == Property)
395  {
396  result.m_value = m_connector->prop_values[prop->prop_id];
397  for (int j = 0; j < prop->count_enums; ++j)
398  result.m_enums.insert({prop->enums[i].value, prop->enums[i].name});
399  }
400  drmModeFreeProperty(prop);
401  }
402  return result;
403 }
404 
405 drmModePropertyBlobPtr MythDRMDevice::GetBlobProperty(drmModeConnectorPtr Connector, const QString& Property) const
406 {
407  if (!Connector || Property.isEmpty())
408  return nullptr;
409 
410  for (int i = 0; i < Connector->count_props; ++i)
411  {
412  drmModePropertyPtr propid = drmModeGetProperty(m_fd, Connector->props[i]);
413  if ((propid->flags & DRM_MODE_PROP_BLOB) && propid->name == Property)
414  {
415  auto blobid = static_cast<uint32_t>(Connector->prop_values[i]);
416  return drmModeGetPropertyBlob(m_fd, blobid);
417  }
418  drmModeFreeProperty(propid);
419  }
420  return nullptr;
421 }
MythDRMDevice::GetEnumProperty
DRMEnum GetEnumProperty(const QString &Property)
Definition: mythdrmdevice.cpp:385
MythDRMDevice::m_authenticated
bool m_authenticated
Definition: mythdrmdevice.h:65
MythDRMDevice::Authenticate
void Authenticate()
Definition: mythdrmdevice.cpp:137
MythDRMDevice::m_screen
QScreen * m_screen
Definition: mythdrmdevice.h:62
root
QDomElement root
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:656
MythDRMDevice::GetResolution
QSize GetResolution() const
Definition: mythdrmdevice.cpp:100
MythDRMDevice::GetScreen
QScreen * GetScreen() const
Definition: mythdrmdevice.cpp:95
MythDRMDevice::m_resources
drmModeRes * m_resources
Definition: mythdrmdevice.h:66
MythDRMDevice::~MythDRMDevice
~MythDRMDevice()
Definition: mythdrmdevice.cpp:42
MythDRMDevice::SetEnumProperty
bool SetEnumProperty(const QString &Property, uint64_t Value)
Definition: mythdrmdevice.cpp:362
MythDRMDevice::GetSerialNumber
QString GetSerialNumber() const
Definition: mythdrmdevice.cpp:90
arg
arg(title).arg(filename).arg(doDelete))
MythDRMDevice::Close
void Close()
Definition: mythdrmdevice.cpp:66
MythDRMDevice::m_resolution
QSize m_resolution
Definition: mythdrmdevice.h:68
MythDRMDevice::m_serialNumber
QString m_serialNumber
Definition: mythdrmdevice.h:71
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MythDRMDevice::DRMEnum
Definition: mythdrmdevice.h:29
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::Initialise
bool Initialise()
Definition: mythdrmdevice.cpp:152
MythDRMDevice::m_valid
bool m_valid
Definition: mythdrmdevice.h:61
DRM_MODE_FLAG_INTERLACE
#define DRM_MODE_FLAG_INTERLACE
Definition: mythdisplaymutter.cpp:12
close
#define close
Definition: compat.h:17
MythDRMDevice::GetBlobProperty
drmModePropertyBlobPtr GetBlobProperty(drmModeConnectorPtr Connector, const QString &Property) const
Definition: mythdrmdevice.cpp:405
MythDRMDevice::GetPhysicalSize
QSize GetPhysicalSize() const
Definition: mythdrmdevice.cpp:105
MythDRMDevice::m_verbose
LogLevel_t m_verbose
Definition: mythdrmdevice.h:74
GetConnectorName
static QString GetConnectorName(drmModeConnector *Connector)
Definition: mythdrmdevice.cpp:125
MythDRMDevice::MythDRMDevice
MythDRMDevice(QScreen *qScreen, const QString &Device=QString())
Definition: mythdrmdevice.cpp:23
MythDRMPtr
std::shared_ptr< class MythDRMDevice > MythDRMPtr
Definition: mythdrmdevice.h:20
MythDRMDevice::m_connector
drmModeConnector * m_connector
Definition: mythdrmdevice.h:67
MythDRMDevice
Definition: mythdrmdevice.h:23
MythDRMDevice::Authenticated
bool Authenticated() const
Definition: mythdrmdevice.cpp:115
MythDRMDevice::m_crtc
drmModeCrtc * m_crtc
Definition: mythdrmdevice.h:72
MythDRMDevice::FindBestDevice
QString FindBestDevice()
Definition: mythdrmdevice.cpp:295
MythDRMDevice::m_edid
MythEDID m_edid
Definition: mythdrmdevice.h:75
MythDRMDevice::m_deviceName
QString m_deviceName
Definition: mythdrmdevice.h:63
LOC
#define LOC
Definition: mythdrmdevice.cpp:15
MythDRMDevice::m_refreshRate
double m_refreshRate
Definition: mythdrmdevice.h:70
mythedid.h
dir
QDir dir
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1174
MythDRMDevice::IsValid
bool IsValid() const
Definition: mythdrmdevice.cpp:85
MythDRMDevice::GetRefreshRate
double GetRefreshRate() const
Definition: mythdrmdevice.cpp:110
MythDRMDevice::m_crtcIdx
int m_crtcIdx
Definition: mythdrmdevice.h:73
MythDRMDevice::Create
static MythDRMPtr Create(QScreen *qScreen, const QString &Device=QString())
Definition: mythdrmdevice.cpp:17
mythdrmdevice.h
MythDRMDevice::GetEDID
MythEDID GetEDID()
Definition: mythdrmdevice.cpp:120
MythDRMDevice::m_physicalSize
QSize m_physicalSize
Definition: mythdrmdevice.h:69
MythDRMDevice::m_fd
int m_fd
Definition: mythdrmdevice.h:64
nv_python_libs.bbciplayer.bbciplayer_api.version
string version
Definition: bbciplayer_api.py:81
MythEDID::SerialNumbers
QStringList SerialNumbers(void) const
Definition: mythedid.cpp:37
MythDRMDevice::Open
bool Open()
Definition: mythdrmdevice.cpp:47
MythEDID
Definition: mythedid.h:18
MythDRMDevice::ConfirmDevice
static bool ConfirmDevice(const QString &Device)
Definition: mythdrmdevice.cpp:346