MythTV  master
mythcdrom-linux.cpp
Go to the documentation of this file.
1 #include <cerrno>
2 #include <climits>
3 #include <cstdint>
4 #include <fcntl.h>
5 #include <linux/cdrom.h> // old ioctls for cdrom
6 #include <linux/fs.h> // BLKRRPART
7 #include <linux/iso_fs.h>
8 #include <scsi/scsi.h>
9 #include <scsi/sg.h>
10 #include <sys/ioctl.h> // ioctls
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 
15 #include <QDateTime>
16 
17 #include "mythcdrom.h"
18 #include "mythcdrom-linux.h"
19 #include "mythconfig.h" // for HAVE_BIGENDIAN
20 #include "mythlogging.h"
21 #include "mythdate.h"
22 
23 #define LOC QString("MythCDROMLinux:")
24 
25 // On a mixed-mode disc (audio+data), set this to 0 to mount the data portion:
26 #ifndef ASSUME_WANT_AUDIO
27 #define ASSUME_WANT_AUDIO 1
28 #endif
29 
30 
31 // Some features cannot be detected (reliably) using the standard
32 // Linux ioctl()s, so we use some direct low-level device queries.
33 
34 using CDROMgenericCmd = struct cdrom_generic_command;
35 
36 // Some structures stolen from the __KERNEL__ section of linux/cdrom.h.
37 
38 // Prevent clang-tidy modernize-avoid-c-arrays warnings in these
39 // kernel structures
40 extern "C" {
41 
42 // This contains the result of a GPCMD_GET_EVENT_STATUS_NOTIFICATION.
43 // It is the joining of a struct event_header and a struct media_event_desc
45 {
47 #if HAVE_BIGENDIAN
48  uint8_t m_nea : 1;
49  uint8_t m_reserved1 : 4;
50  uint8_t m_notificationClass : 3;
51 #else
52  uint8_t m_notificationClass : 3;
53  uint8_t m_reserved1 : 4;
54  uint8_t m_nea : 1;
55 #endif
57 #if HAVE_BIGENDIAN
58  uint8_t m_reserved2 : 4;
59  uint8_t m_mediaEventCode : 4;
60  uint8_t m_reserved3 : 6;
61  uint8_t m_mediaPresent : 1;
62  uint8_t m_doorOpen : 1;
63 #else
64  uint8_t m_mediaEventCode : 4;
65  uint8_t m_reserved2 : 4;
66  uint8_t m_doorOpen : 1;
67  uint8_t m_mediaPresent : 1;
68  uint8_t m_reserved3 : 6;
69 #endif
70  uint8_t m_startSlot;
71  uint8_t m_endSlot;
72 };
73 
74 // and this is returned by GPCMD_READ_DISC_INFO
75 struct CDROMdiscInfo {
77 #if HAVE_BIGENDIAN
78  uint8_t m_reserved1 : 3;
79  uint8_t m_erasable : 1;
80  uint8_t m_borderStatus : 2;
81  uint8_t m_discStatus : 2;
82 #else
83  uint8_t m_discStatus : 2;
84  uint8_t m_borderStatus : 2;
85  uint8_t m_erasable : 1;
86  uint8_t m_reserved1 : 3;
87 #endif
88  uint8_t m_nFirstTrack;
89  uint8_t m_nSessionsLsb;
90  uint8_t m_firstTrackLsb;
91  uint8_t m_lastTrackLsb;
92 #if HAVE_BIGENDIAN
93  uint8_t m_didV : 1;
94  uint8_t m_dbcV : 1;
95  uint8_t m_uru : 1;
96  uint8_t m_reserved2 : 5;
97 #else
98  uint8_t m_reserved2 : 5;
99  uint8_t m_uru : 1;
100  uint8_t m_dbcV : 1;
101  uint8_t m_didV : 1;
102 #endif
103  uint8_t m_discType;
104  uint8_t m_nSessionsMsb;
106  uint8_t m_lastTrackMsb;
107  uint32_t m_discId;
108  uint32_t m_leadIn;
109  uint32_t m_leadOut;
110  uint8_t m_discBarCode[8];
111  uint8_t m_reserved3;
112  uint8_t m_nOpc;
113 };
114 
115 // end of kernel structures.
116 };
117 
119 {
124 };
125 
126 
134 {
135 public:
136  MythCDROMLinux(QObject* par, const QString& DevicePath, bool SuperMount,
137  bool AllowEject):
138  MythCDROM(par, DevicePath, SuperMount, AllowEject) {
139  }
140 
141  MythMediaError testMedia(void) override; // MythMediaDevice
142  bool mediaChanged(void) override; // MythCDROM
143  bool checkOK(void) override; // MythCDROM
144  MythMediaStatus checkMedia(void) override; // MythMediaDevice
145  MythMediaError eject(bool open_close = true) override; // MythMediaDevice
146  void setDeviceSpeed(const char *device, int speed) override; // MythMediaDevice
147  bool isSameDevice(const QString &path) override; // MythMediaDevice
148  MythMediaError lock(void) override; // MythMediaDevice
149  MythMediaError unlock(void) override; // MythMediaDevice
150 
151 protected:
152  MythMediaError ejectCDROM(bool open_close);
154 
155 private:
156  int driveStatus(void);
157  bool hasWritableMedia(void);
158  int SCSIstatus(void);
159 };
160 
161 MythCDROM *GetMythCDROMLinux(QObject* par, const QString& devicePath,
162  bool SuperMount, bool AllowEject)
163 {
164  return new MythCDROMLinux(par, devicePath, SuperMount, AllowEject);
165 }
166 
167 
177 {
178  int drive_status = ioctl(m_deviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT);
179 
180  if (drive_status == -1) // Very unlikely, but we should check
181  {
182  LOG(VB_MEDIA, LOG_ERR, LOC + ":driveStatus() - ioctl failed: " + ENO);
183  return CDS_NO_INFO;
184  }
185 
186  if (drive_status == CDS_TRAY_OPEN && m_devicePath.contains("/dev/scd"))
187  return SCSIstatus();
188 
189  return drive_status;
190 }
191 
196 {
197  CDROMdiscInfo di {};
198  CDROMgenericCmd cgc {};
199 
200  cgc.cmd[0] = GPCMD_READ_DISC_INFO;
201  cgc.cmd[8] = sizeof(CDROMdiscInfo);
202  cgc.quiet = 1;
203  cgc.buffer = reinterpret_cast<uint8_t*>(&di);
204  cgc.buflen = sizeof(CDROMdiscInfo);
205  cgc.data_direction = CGC_DATA_READ;
206 
207  if (ioctl(m_deviceHandle, CDROM_SEND_PACKET, &cgc) < 0)
208  {
209  LOG(VB_MEDIA, LOG_ERR, LOC +
210  ":hasWritableMedia() - failed to send packet to " + m_devicePath + ENO);
211  return false;
212  }
213 
214  switch (di.m_discStatus)
215  {
216  case MEDIA_IS_EMPTY:
217  return true;
218 
219  case MEDIA_IS_APPENDABLE:
220  // It is unlikely that any plugins will support multi-session
221  // writing, so we treat it just like a finished disc:
222 
223  case MEDIA_IS_COMPLETE:
224  return di.m_erasable;
225 
226  case MEDIA_IS_OTHER:
227  ;
228  }
229 
230  return false;
231 }
232 
242 {
243  CDROMeventStatus es {};
244  CDROMgenericCmd cgc {};
245 
246  cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
247  cgc.cmd[1] = 1; // Tell us immediately
248  cgc.cmd[4] = 1 << 4; // notification class of media
249  cgc.cmd[8] = sizeof(CDROMeventStatus);
250  cgc.quiet = 1;
251  cgc.buffer = reinterpret_cast<uint8_t*>(&es);
252  cgc.buflen = sizeof(CDROMeventStatus);
253  cgc.data_direction = CGC_DATA_READ;
254 
255  if ((ioctl(m_deviceHandle, CDROM_SEND_PACKET, &cgc) < 0)
256  || es.m_nea // drive does not support request
257  || (es.m_notificationClass != 0x4)) // notification class mismatch
258  {
259  LOG(VB_MEDIA, LOG_ERR, LOC +
260  ":SCSIstatus() - failed to send SCSI packet to " + m_devicePath + ENO);
261  return CDS_TRAY_OPEN;
262  }
263 
264  if (es.m_mediaPresent)
265  {
266  LOG(VB_MEDIA, LOG_DEBUG, LOC +
267  ":SCSIstatus() - ioctl said tray was open, "
268  "but drive is actually closed with a disc");
269  return CDS_DISC_OK;
270  }
271  if (es.m_doorOpen)
272  {
273  LOG(VB_MEDIA, LOG_DEBUG, LOC +
274  ":SCSIstatus() - tray is definitely open");
275  return CDS_TRAY_OPEN;
276  }
277 
278  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":SCSIstatus() - ioctl said tray was open, "
279  "but drive is actually closed with no disc");
280  return CDS_NO_DISC;
281 }
282 
283 
285 {
286  if (!isDeviceOpen())
287  {
288  if (!openDevice())
289  return MEDIAERR_FAILED;
290  }
291 
292  MythMediaError err = ejectCDROM(open_close);
293  if (MEDIAERR_OK != err && open_close)
294  err = ejectSCSI();
295 
296  return err;
297 }
298 
300 {
301  if (open_close)
302  {
303  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":eject - Ejecting CDROM");
304  int res = ioctl(m_deviceHandle, CDROMEJECT);
305 
306  if (res < 0)
307  LOG(VB_MEDIA, LOG_DEBUG, "CDROMEJECT ioctl failed" + ENO);
308 
309  return (res == 0) ? MEDIAERR_OK : MEDIAERR_FAILED;
310  }
311 
312  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":eject - Loading CDROM");
313  // If the tray is empty, this will fail (Input/Output error)
314  int res = ioctl(m_deviceHandle, CDROMCLOSETRAY);
315 
316  if (res < 0)
317  LOG(VB_MEDIA, LOG_DEBUG, "CDROMCLOSETRAY ioctl failed" + ENO);
318 
319  // This allows us to catch any drives that the OS has problems
320  // detecting the status of (some always report OPEN when empty)
321  if (driveStatus() == CDS_TRAY_OPEN)
322  return MEDIAERR_FAILED;
323  return MEDIAERR_OK;
324 }
325 
326 struct StHandle {
327  const int m_fd;
328  explicit StHandle(const char *dev) : m_fd(open(dev, O_RDWR | O_NONBLOCK)) { }
330  operator int() const { return m_fd; } // NOLINT(google-explicit-constructor)
331 };
332 
333 // This is copied from eject.c by Jeff Tranter (tranter@pobox.com)
335 {
336  int k = 0;
337  sg_io_hdr_t io_hdr;
338  std::array<uint8_t,6> allowRmBlk {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
339  std::array<uint8_t,6> startStop1Blk {START_STOP, 0, 0, 0, 1, 0}; // start
340  std::array<uint8_t,6> startStop2Blk {START_STOP, 0, 0, 0, 2, 0}; // load eject
341  std::array<uint8_t,16> sense_buffer {};
342  const unsigned DID_OK = 0;
343  const unsigned DRIVER_OK = 0;
344 
345  // ALLOW_MEDIUM_REMOVAL requires r/w access so re-open the device
346  struct StHandle fd(qPrintable(m_devicePath));
347 
348  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":ejectSCSI");
349  if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
350  {
351  // not an sg device, or old sg driver
352  LOG(VB_MEDIA, LOG_DEBUG, "SG_GET_VERSION_NUM ioctl failed" + ENO);
353  return MEDIAERR_FAILED;
354  }
355 
356  memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
357  io_hdr.interface_id = 'S';
358  io_hdr.cmd_len = 6;
359  io_hdr.mx_sb_len = sense_buffer.size();
360  io_hdr.dxfer_direction = SG_DXFER_NONE;
361  io_hdr.sbp = sense_buffer.data();
362  io_hdr.timeout = 10000; // millisecs
363 
364  io_hdr.cmdp = allowRmBlk.data();
365  if (ioctl(fd, SG_IO, &io_hdr) < 0)
366  {
367  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO allowRmBlk ioctl failed" + ENO);
368  return MEDIAERR_FAILED;
369  }
370  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
371  {
372  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO allowRmBlk failed");
373  return MEDIAERR_FAILED;
374  }
375 
376  io_hdr.cmdp = startStop1Blk.data();
377  if (ioctl(fd, SG_IO, &io_hdr) < 0)
378  {
379  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(start) ioctl failed" + ENO);
380  return MEDIAERR_FAILED;
381  }
382  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
383  {
384  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(start) failed");
385  return MEDIAERR_FAILED;
386  }
387 
388  io_hdr.cmdp = startStop2Blk.data();
389  if (ioctl(fd, SG_IO, &io_hdr) < 0)
390  {
391  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(eject) ioctl failed" + ENO);
392  return MEDIAERR_FAILED;
393  }
394  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
395  {
396  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(eject) failed");
397  return MEDIAERR_FAILED;
398  }
399 
400  /* force kernel to reread partition table when new disc inserted */
401  (void)ioctl(fd, BLKRRPART);
402  return MEDIAERR_OK;
403 }
404 
405 
407 {
408  return (ioctl(m_deviceHandle, CDROM_MEDIA_CHANGED, CDSL_CURRENT) > 0);
409 }
410 
412 {
413  return (ioctl(m_deviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT) ==
414  CDS_DISC_OK);
415 }
416 
417 // Helper function, perform a sanity check on the device
419 {
420  bool OpenedHere = false;
421  if (!isDeviceOpen())
422  {
423  if (!openDevice())
424  {
425  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":testMedia - failed to open '" +
426  m_devicePath + "' : " +ENO);
427  if (errno == EBUSY)
429  return MEDIAERR_FAILED;
430  }
431  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":testMedia - Opened device");
432  OpenedHere = true;
433  }
434 
435  // Since the device was is/was open we can get it's status...
436  int Stat = driveStatus();
437 
438  // Be nice and close the device if we opened it,
439  // otherwise it might be locked when the user doesn't want it to be.
440  if (OpenedHere)
441  closeDevice();
442 
443  if (Stat == -1)
444  {
445  LOG(VB_MEDIA, LOG_DEBUG, LOC +
446  ":testMedia - Failed to get drive status of '" + m_devicePath +
447  "' : " + ENO);
448  return MEDIAERR_FAILED;
449  }
450 
451  return MEDIAERR_OK;
452 }
453 
455 {
456  bool OpenedHere = false;
457 
458  // If it's not already open we need to at least
459  // TRY to open it for most of these checks to work.
460  if (!isDeviceOpen())
461  {
462  OpenedHere = openDevice();
463 
464  if (!OpenedHere)
465  {
466  LOG(VB_MEDIA, LOG_ERR, LOC +
467  ":checkMedia() - cannot open device '" + m_devicePath + "' : " +
468  ENO + "- returning UNKNOWN");
470  return setStatus(MEDIASTAT_UNKNOWN, false);
471  }
472  }
473 
474  switch (driveStatus())
475  {
476  case CDS_DISC_OK:
477  LOG(VB_MEDIA, LOG_DEBUG, m_devicePath + " Disk OK, type = " +
479  // further checking is required
480  break;
481  case CDS_TRAY_OPEN:
482  LOG(VB_MEDIA, LOG_DEBUG, m_devicePath + " Tray open or no disc");
483  // First, send a message to the
484  // plugins to forget the current media type
485  setStatus(MEDIASTAT_OPEN, OpenedHere);
486  // then "clear out" this device
488  return MEDIASTAT_OPEN;
489  break;
490  case CDS_NO_DISC:
491  LOG(VB_MEDIA, LOG_DEBUG, m_devicePath + " No disc");
493  return setStatus(MEDIASTAT_NODISK, OpenedHere);
494  break;
495  case CDS_NO_INFO:
496  case CDS_DRIVE_NOT_READY:
497  LOG(VB_MEDIA, LOG_DEBUG, m_devicePath +
498  " No info or drive not ready");
500  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
501  default:
502  LOG(VB_GENERAL, LOG_ERR, "Failed to get drive status of " +
503  m_devicePath + " : " + ENO);
505  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
506  }
507 
508  // NB must call mediaChanged before testing m_status otherwise will get
509  // an unwanted mediaChanged on next pass
511  {
512  LOG(VB_MEDIA, LOG_INFO, m_devicePath + " Media changed");
513  // Regardless of the actual status lie here and say
514  // it's open for now, so we can cover the case of a missed open.
515  return setStatus(MEDIASTAT_OPEN, OpenedHere);
516  }
517 
518 
519  if (isUsable())
520  {
521  LOG(VB_MEDIA, LOG_DEBUG, "Disc useable, media unchanged. All good!");
522  if (OpenedHere)
523  closeDevice();
524  return MEDIASTAT_USEABLE;
525  }
526 
527  // If we have tried to mount and failed, don't keep trying
528  if (m_status == MEDIASTAT_ERROR)
529  {
530  // Check if an external agent (like Gnome/KDE) mounted the disk
531  if (isMounted())
532  {
533  onDeviceMounted();
534  // pretend we're NOTMOUNTED so setStatus emits a signal
536  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
537  }
538 
539  LOG(VB_MEDIA, LOG_DEBUG, "Disc is unmountable?");
540  if (OpenedHere)
541  closeDevice();
542  return m_status;
543  }
544 
545  if ((m_status == MEDIASTAT_OPEN) ||
547  {
548  LOG(VB_MEDIA, LOG_INFO, m_devicePath + " Current status " +
550  int type = ioctl(m_deviceHandle, CDROM_DISC_STATUS, CDSL_CURRENT);
551  switch (type)
552  {
553  case CDS_DATA_1:
554  case CDS_DATA_2:
555  {
557  LOG(VB_MEDIA, LOG_INFO, "Found a data disk");
558 
559  //grab information from iso9660 (& udf)
561  (off_t) 2048*16, SEEK_SET);
562 
563  struct iso_primary_descriptor buf {};
564  ssize_t readin = 0;
565  while ((sr != (off_t) -1) && (readin < 2048))
566  {
567  ssize_t rr = read(
568  m_deviceHandle, ((char*)&buf) + readin, 2048 - readin);
569  if ((rr < 0) && ((EAGAIN == errno) || (EINTR == errno)))
570  continue;
571  if (rr < 0)
572  break;
573  readin += rr;
574  }
575 
576  if (readin == 2048)
577  {
578  m_volumeID = QString(buf.volume_id).trimmed();
579  m_keyID = QString("%1%2")
580  .arg(m_volumeID,
581  QString(reinterpret_cast<char*>(buf.creation_date)).left(16));
582  }
583  else
584  {
585  m_volumeID = "UNKNOWN";
587  }
588 
589  LOG(VB_MEDIA, LOG_INFO,
590  QString("Volume ID: %1").arg(m_volumeID));
591  {
593 /*
594  if( imageType == MythCDROM::kBluray )
595  m_mediaType = MEDIATYPE_BD;
596  else
597 */
598  if( imageType == MythCDROM::kDVD )
600 
602  {
603  // pretend we're NOTMOUNTED so setStatus emits a signal
605  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
606  }
607  }
608 
609  // the base class's onDeviceMounted will do fine
610  // grained detection of the type of data on this disc
611  if (isMounted())
612  onDeviceMounted();
613  else if (!mount()) // onDeviceMounted() called as side-effect
614  return setStatus(MEDIASTAT_NOTMOUNTED, OpenedHere);
615 
616  if (isMounted())
617  {
618  // pretend we're NOTMOUNTED so setStatus emits a signal
620  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
621  }
622  if (m_mediaType == MEDIATYPE_DVD)
623  {
624  // pretend we're NOTMOUNTED so setStatus emits a signal
626  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
627  }
628  return setStatus(MEDIASTAT_NOTMOUNTED, OpenedHere);
629  }
630  case CDS_AUDIO:
631  LOG(VB_MEDIA, LOG_DEBUG, "found an audio disk");
632  // pretend we're NOTMOUNTED so setStatus emits a signal
635  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
636  case CDS_MIXED:
637  LOG(VB_MEDIA, LOG_DEBUG, "found a mixed CD");
638  // Note: Mixed mode CDs require an explixit mount call
639  // since we'll usually want the audio portion.
640  // undefine ASSUME_WANT_AUDIO to change this behavior.
641  #if ASSUME_WANT_AUDIO
642  // pretend we're NOTMOUNTED so setStatus emits a signal
645  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
646  #else
648  mount();
649  if (isMounted())
650  {
651  // pretend we're NOTMOUNTED so setStatus
652  // emits a signal
654  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
655  }
656  else
657  {
658  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
659  }
660  #endif
661  break;
662  case CDS_NO_INFO:
663  case CDS_NO_DISC:
664  if (hasWritableMedia())
665  {
666  LOG(VB_MEDIA, LOG_DEBUG, "found a blank or writable disk");
667  return setStatus(MEDIASTAT_UNFORMATTED, OpenedHere);
668  }
669 
670  LOG(VB_MEDIA, LOG_DEBUG, "found no disk");
672  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
673  break;
674  default:
675  LOG(VB_MEDIA, LOG_DEBUG, "found unknown disk type: " +
676  QString::number(type));
678  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
679  }
680  }
681 
682  if (m_allowEject)
683  unlock();
684  else
685  lock();
686 
687  if (OpenedHere)
688  closeDevice();
689 
690  LOG(VB_MEDIA, LOG_DEBUG, QString("Returning %1")
692  return m_status;
693 }
694 
696 {
698  if (ret == MEDIAERR_OK)
699  {
700  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":lock - Locking CDROM door");
701  int res = ioctl(m_deviceHandle, CDROM_LOCKDOOR, 1);
702 
703  if (res < 0)
704  LOG(VB_MEDIA, LOG_WARNING, "lock() - CDROM_LOCKDOOR ioctl failed" + ENO);
705  }
706 
707  return ret;
708 }
709 
711 {
712  if (isDeviceOpen() || openDevice())
713  {
714  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":unlock - Unlocking CDROM door");
715  int res = ioctl(m_deviceHandle, CDROM_LOCKDOOR, 0);
716 
717  if (res < 0)
718  LOG(VB_MEDIA, LOG_WARNING, "unlock() - CDROM_LOCKDOOR ioctl failed" + ENO);
719  }
720  else
721  {
722  LOG(VB_GENERAL, LOG_INFO, "Failed to open device, CDROM tray will "
723  "remain locked.");
724  }
725 
726  return MythMediaDevice::unlock();
727 }
728 
729 bool MythCDROMLinux::isSameDevice(const QString &path)
730 {
731  struct stat sb {};
732 
733  if (stat(path.toLocal8Bit().constData(), &sb) < 0)
734  {
735  LOG(VB_GENERAL, LOG_ERR, LOC + ":isSameDevice() -- " +
736  QString("Failed to stat '%1'").arg(path) + ENO);
737  return false;
738  }
739  dev_t new_rdev = sb.st_rdev;
740 
741  // Check against m_devicePath...
742  if (stat(m_devicePath.toLocal8Bit().constData(), &sb) < 0)
743  {
744  LOG(VB_GENERAL, LOG_ERR, LOC + ":isSameDevice() -- " +
745  QString("Failed to stat '%1'").arg(m_devicePath) + ENO);
746  return false;
747  }
748  return (sb.st_rdev == new_rdev);
749 }
750 
751 #if defined(SG_IO) && defined(GPCMD_SET_STREAMING)
752 /*
753  * \brief obtained from the mplayer project
754  */
755 void MythCDROMLinux::setDeviceSpeed(const char *device, int speed)
756 {
757  std::array<uint8_t,28> buffer {};
758  std::array<uint8_t,16> cmd {};
759  std::array<uint8_t,16> sense {};
760  struct sg_io_hdr sghdr {};
761  struct stat st {};
762  int rate = 0;
763 
764  int fd = open(device, O_RDWR | O_NONBLOCK);
765  if (fd == -1)
766  {
767  LOG(VB_MEDIA, LOG_ERR, LOC +
768  " Changing CD/DVD speed needs write access");
769  return;
770  }
771 
772  if (fstat(fd, &st) == -1)
773  {
774  close(fd);
775  LOG(VB_MEDIA, LOG_ERR, LOC +
776  QString(":setDeviceSpeed() Failed. device %1 not found")
777  .arg(device));
778  return;
779  }
780 
781  if (!S_ISBLK(st.st_mode))
782  {
783  close(fd);
784  LOG(VB_MEDIA, LOG_ERR, LOC +
785  ":setDeviceSpeed() Failed. Not a block device");
786  return;
787  }
788 
789  if (speed < 0)
790  speed = -1;
791 
792  switch(speed)
793  {
794  case 0: // don't touch speed setting
795  close(fd);
796  return;
797  case -1: // restore default value
798  {
799  rate = 0;
800  buffer[0] = 4;
801  LOG(VB_MEDIA, LOG_INFO, LOC +
802  ":setDeviceSpeed() - Restored CD/DVD Speed");
803  break;
804  }
805  default:
806  {
807  // Speed in Kilobyte/Second. 177KB/s is the maximum data rate
808  // for standard Audio CD's.
809 
810  rate = (speed > 0 && speed < 100) ? speed * 177 : speed;
811 
812  LOG(VB_MEDIA, LOG_INFO, LOC +
813  QString(":setDeviceSpeed() - Limiting CD/DVD Speed to %1KB/s")
814  .arg(rate));
815  break;
816  }
817  }
818 
819  sghdr.interface_id = 'S';
820  sghdr.timeout = 5000;
821  sghdr.dxfer_direction = SG_DXFER_TO_DEV;
822  sghdr.mx_sb_len = sense.size();
823  sghdr.dxfer_len = buffer.size();
824  sghdr.cmd_len = cmd.size();
825  sghdr.sbp = sense.data();
826  sghdr.dxferp = buffer.data();
827  sghdr.cmdp = cmd.data();
828 
829  cmd[0] = GPCMD_SET_STREAMING;
830  cmd[10] = buffer.size();
831 
832  buffer[8] = 0xff;
833  buffer[9] = 0xff;
834  buffer[10] = 0xff;
835  buffer[11] = 0xff;
836 
837  buffer[12] = buffer[20] = (rate >> 24) & 0xff;
838  buffer[13] = buffer[21] = (rate >> 16) & 0xff;
839  buffer[14] = buffer[22] = (rate >> 8) & 0xff;
840  buffer[15] = buffer[23] = rate & 0xff;
841 
842  // Note: 0x3e8 == 1000, hence speed = data amount per 1000 milliseconds.
843  buffer[18] = buffer[26] = 0x03;
844  buffer[19] = buffer[27] = 0xe8;
845 
846  if (ioctl(fd, SG_IO, &sghdr) < 0)
847  {
848  LOG(VB_MEDIA, LOG_ERR, LOC + " Limit CD/DVD Speed Failed" + ENO);
849  }
850  else
851  {
852  // On my system (2.6.18+ide-cd), SG_IO succeeds without doing anything,
853  // while CDROM_SELECT_SPEED works...
854  if (ioctl(fd, CDROM_SELECT_SPEED, speed) < 0)
855  {
856  LOG(VB_MEDIA, LOG_ERR, LOC +
857  " Limit CD/DVD CDROM_SELECT_SPEED Failed" + ENO);
858  }
859  LOG(VB_MEDIA, LOG_INFO, LOC +
860  ":setDeviceSpeed() - CD/DVD Speed Set Successful");
861  }
862 
863  close(fd);
864 }
865 #endif
MythMediaDevice::isUsable
bool isUsable() const
Is this device "ready", for a plugin to access?
Definition: mythmedia.h:84
CDROMeventStatus::m_nea
uint8_t m_nea
Definition: mythcdrom-linux.cpp:54
MEDIATYPE_MIXED
@ MEDIATYPE_MIXED
Definition: mythmedia.h:27
MythCDROMLinux::unlock
MythMediaError unlock(void) override
Definition: mythcdrom-linux.cpp:710
CDROMdiscInfo::m_reserved1
uint8_t m_reserved1
Definition: mythcdrom-linux.cpp:86
CDROMdiscInfo::m_lastTrackLsb
uint8_t m_lastTrackLsb
Definition: mythcdrom-linux.cpp:91
MythMediaDevice::mount
bool mount()
Definition: mythmedia.h:107
MEDIATYPE_DVD
@ MEDIATYPE_DVD
Definition: mythmedia.h:29
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:72
MythMediaDevice::m_deviceHandle
int m_deviceHandle
A file handle for opening and closing the device, ioctls(), et c.
Definition: mythmedia.h:175
MythCDROMLinux::testMedia
MythMediaError testMedia(void) override
Definition: mythcdrom-linux.cpp:418
MEDIATYPE_UNKNOWN
@ MEDIATYPE_UNKNOWN
Definition: mythmedia.h:25
MythCDROMLinux::eject
MythMediaError eject(bool open_close=true) override
Definition: mythcdrom-linux.cpp:284
MEDIA_IS_APPENDABLE
@ MEDIA_IS_APPENDABLE
Definition: mythcdrom-linux.cpp:121
CDROMdiscInfo
Definition: mythcdrom-linux.cpp:75
MEDIA_IS_EMPTY
@ MEDIA_IS_EMPTY
Definition: mythcdrom-linux.cpp:120
MythMediaDevice::isDeviceOpen
bool isDeviceOpen() const
Definition: mythmedia.cpp:107
MythCDROMLinux::setDeviceSpeed
void setDeviceSpeed(const char *device, int speed) override
MythCDROMLinux::mediaChanged
bool mediaChanged(void) override
Definition: mythcdrom-linux.cpp:406
MythMediaDevice::m_keyID
QString m_keyID
KeyID of the media.
Definition: mythmedia.h:151
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
CDROMdiscStatus
CDROMdiscStatus
Definition: mythcdrom-linux.cpp:118
mythcdrom.h
CDROMeventStatus::m_mediaPresent
uint8_t m_mediaPresent
Definition: mythcdrom-linux.cpp:67
CDROMdiscInfo::m_nFirstTrack
uint8_t m_nFirstTrack
Definition: mythcdrom-linux.cpp:88
MEDIASTAT_ERROR
@ MEDIASTAT_ERROR
Unable to mount, but could be usable.
Definition: mythmedia.h:13
MythMediaDevice::openDevice
virtual bool openDevice()
Definition: mythmedia.cpp:83
LOC
#define LOC
Definition: mythcdrom-linux.cpp:23
MEDIASTAT_USEABLE
@ MEDIASTAT_USEABLE
Definition: mythmedia.h:19
StHandle::StHandle
StHandle(const char *dev)
Definition: mythcdrom-linux.cpp:328
CDROMdiscInfo::m_didV
uint8_t m_didV
Definition: mythcdrom-linux.cpp:101
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
CDROMdiscInfo::m_erasable
uint8_t m_erasable
Definition: mythcdrom-linux.cpp:85
CDROMdiscInfo::m_reserved3
uint8_t m_reserved3
Definition: mythcdrom-linux.cpp:111
CDROMdiscInfo::m_discInformationLength
uint16_t m_discInformationLength
Definition: mythcdrom-linux.cpp:76
MythCDROMLinux::ejectCDROM
MythMediaError ejectCDROM(bool open_close)
Definition: mythcdrom-linux.cpp:299
MEDIASTAT_MOUNTED
@ MEDIASTAT_MOUNTED
Definition: mythmedia.h:21
CDROMdiscInfo::m_firstTrackLsb
uint8_t m_firstTrackLsb
Definition: mythcdrom-linux.cpp:90
CDROMdiscInfo::m_uru
uint8_t m_uru
Definition: mythcdrom-linux.cpp:99
MythCDROMLinux::SCSIstatus
int SCSIstatus(void)
Use a SCSI query packet to see if the drive is really open.
Definition: mythcdrom-linux.cpp:241
CDROMeventStatus::m_startSlot
uint8_t m_startSlot
Definition: mythcdrom-linux.cpp:70
close
#define close
Definition: compat.h:17
MythCDROMLinux::MythCDROMLinux
MythCDROMLinux(QObject *par, const QString &DevicePath, bool SuperMount, bool AllowEject)
Definition: mythcdrom-linux.cpp:136
x4
static int x4
Definition: mythsocket.cpp:60
lseek
#define lseek
Definition: mythiowrapper.cpp:238
MythCDROMLinux::isSameDevice
bool isSameDevice(const QString &path) override
Definition: mythcdrom-linux.cpp:729
MythMediaDevice::isMounted
bool isMounted(bool bVerify=true)
Tells us if m_devicePath is a mounted device.
Definition: mythmedia.cpp:357
CDROMdiscInfo::m_discType
uint8_t m_discType
Definition: mythcdrom-linux.cpp:103
mythdate.h
MythCDROMLinux::checkMedia
MythMediaStatus checkMedia(void) override
Definition: mythcdrom-linux.cpp:454
MythCDROM::kDVD
@ kDVD
Definition: mythcdrom.h:30
CDROMdiscInfo::m_nSessionsLsb
uint8_t m_nSessionsLsb
Definition: mythcdrom-linux.cpp:89
mythlogging.h
CDROMdiscInfo::m_leadIn
uint32_t m_leadIn
Definition: mythcdrom-linux.cpp:108
CDROMdiscInfo::m_firstTrackMsb
uint8_t m_firstTrackMsb
Definition: mythcdrom-linux.cpp:105
CDROMdiscInfo::m_nOpc
uint8_t m_nOpc
Definition: mythcdrom-linux.cpp:112
O_NONBLOCK
#define O_NONBLOCK
Definition: mythmedia.cpp:25
CDROMeventStatus::m_suppEventClass
uint8_t m_suppEventClass
Definition: mythcdrom-linux.cpp:56
MythCDROMLinux::driveStatus
int driveStatus(void)
Exhaustively determine the status.
Definition: mythcdrom-linux.cpp:176
CDROMdiscInfo::m_dbcV
uint8_t m_dbcV
Definition: mythcdrom-linux.cpp:100
GetMythCDROMLinux
MythCDROM * GetMythCDROMLinux(QObject *par, const QString &devicePath, bool SuperMount, bool AllowEject)
Definition: mythcdrom-linux.cpp:161
MythCDROM::ImageType
ImageType
Definition: mythcdrom.h:26
MythMediaDevice::unlock
virtual MythMediaError unlock()
Definition: mythmedia.cpp:349
MythCDROM
Definition: mythcdrom.h:6
MEDIATYPE_AUDIO
@ MEDIATYPE_AUDIO
Definition: mythmedia.h:28
MythMediaDevice::setStatus
MythMediaStatus setStatus(MythMediaStatus newStat, bool CloseIt=false)
Definition: mythmedia.cpp:462
CDROMdiscInfo::m_leadOut
uint32_t m_leadOut
Definition: mythcdrom-linux.cpp:109
MythMediaDevice::lock
virtual MythMediaError lock()
Definition: mythmedia.cpp:336
MEDIA_IS_COMPLETE
@ MEDIA_IS_COMPLETE
Definition: mythcdrom-linux.cpp:122
off_t
#define off_t
Definition: mythiowrapper.cpp:240
MythCDROM::inspectImage
static ImageType inspectImage(const QString &path)
Definition: mythcdrom.cpp:179
MythCDROMLinux
Definition: mythcdrom-linux.cpp:133
CDROMeventStatus::m_reserved2
uint8_t m_reserved2
Definition: mythcdrom-linux.cpp:65
MythCDROMLinux::ejectSCSI
MythMediaError ejectSCSI()
Definition: mythcdrom-linux.cpp:334
MEDIASTAT_NODISK
@ MEDIASTAT_NODISK
CD/DVD tray closed but empty, device unusable.
Definition: mythmedia.h:17
CDROMdiscInfo::m_reserved2
uint8_t m_reserved2
Definition: mythcdrom-linux.cpp:98
CDROMeventStatus::m_endSlot
uint8_t m_endSlot
Definition: mythcdrom-linux.cpp:71
CDROMeventStatus::m_notificationClass
uint8_t m_notificationClass
Definition: mythcdrom-linux.cpp:52
CDROMdiscInfo::m_lastTrackMsb
uint8_t m_lastTrackMsb
Definition: mythcdrom-linux.cpp:106
CDROMdiscInfo::m_discBarCode
uint8_t m_discBarCode[8]
Definition: mythcdrom-linux.cpp:110
MythMediaDevice::m_devicePath
QString m_devicePath
The path to this media's device.
Definition: mythmedia.h:149
MythMediaDevice::m_allowEject
bool m_allowEject
Allow the user to eject the media?. Read only.
Definition: mythmedia.h:165
mythcdrom-linux.h
MythMediaDevice::kMediaStatusStrings
static const std::array< const QString, 9 > kMediaStatusStrings
Definition: mythmedia.h:117
CDROMeventStatus
Definition: mythcdrom-linux.cpp:44
CDROMdiscInfo::m_nSessionsMsb
uint8_t m_nSessionsMsb
Definition: mythcdrom-linux.cpp:104
MythCDROM::onDeviceMounted
void onDeviceMounted() override
Override this to perform any post mount logic.
Definition: mythcdrom.cpp:55
CDROMeventStatus::m_dataLen
uint16_t m_dataLen[2]
Definition: mythcdrom-linux.cpp:46
MEDIASTAT_OPEN
@ MEDIASTAT_OPEN
CD/DVD tray open (meaningless for non-CDs?)
Definition: mythmedia.h:16
CDROMdiscInfo::m_discId
uint32_t m_discId
Definition: mythcdrom-linux.cpp:107
MythDate::current_iso_string
QString current_iso_string(bool stripped)
Returns current Date and Time in UTC as a string.
Definition: mythdate.cpp:18
StHandle::m_fd
const int m_fd
Definition: mythcdrom-linux.cpp:327
MythCDROMLinux::checkOK
bool checkOK(void) override
Definition: mythcdrom-linux.cpp:411
MEDIAERR_FAILED
@ MEDIAERR_FAILED
Definition: mythmedia.h:41
MythMediaError
MythMediaError
Definition: mythmedia.h:39
uint16_t
unsigned short uint16_t
Definition: iso6937tables.h:3
MythMediaDevice::m_status
MythMediaStatus m_status
The status of the media as of the last call to checkMedia.
Definition: mythmedia.h:159
CDROMeventStatus::m_reserved3
uint8_t m_reserved3
Definition: mythcdrom-linux.cpp:68
CDROMgenericCmd
struct cdrom_generic_command CDROMgenericCmd
Definition: mythcdrom-linux.cpp:34
MEDIASTAT_UNFORMATTED
@ MEDIASTAT_UNFORMATTED
For devices/media a plugin might erase/format.
Definition: mythmedia.h:18
MythCDROMLinux::lock
MythMediaError lock(void) override
Definition: mythcdrom-linux.cpp:695
MEDIA_IS_OTHER
@ MEDIA_IS_OTHER
Definition: mythcdrom-linux.cpp:123
MEDIAERR_OK
@ MEDIAERR_OK
Definition: mythmedia.h:40
MythCDROMLinux::hasWritableMedia
bool hasWritableMedia(void)
Is there blank or eraseable media in the drive?
Definition: mythcdrom-linux.cpp:195
MEDIASTAT_UNKNOWN
@ MEDIASTAT_UNKNOWN
Definition: mythmedia.h:14
MythMediaDevice::closeDevice
virtual bool closeDevice()
Definition: mythmedia.cpp:95
MythMediaStatus
MythMediaStatus
Definition: mythmedia.h:12
CDROMeventStatus::m_doorOpen
uint8_t m_doorOpen
Definition: mythcdrom-linux.cpp:66
MythMediaDevice::m_mediaType
MythMediaType m_mediaType
The type of media. Read only.
Definition: mythmedia.h:162
CDROMeventStatus::m_mediaEventCode
uint8_t m_mediaEventCode
Definition: mythcdrom-linux.cpp:64
CDROMeventStatus::m_reserved1
uint8_t m_reserved1
Definition: mythcdrom-linux.cpp:53
StHandle
Definition: mythcdrom-linux.cpp:326
CDROMdiscInfo::m_discStatus
uint8_t m_discStatus
Definition: mythcdrom-linux.cpp:83
MEDIATYPE_DATA
@ MEDIATYPE_DATA
Definition: mythmedia.h:26
MEDIASTAT_NOTMOUNTED
@ MEDIASTAT_NOTMOUNTED
Definition: mythmedia.h:20
MythMediaDevice::m_volumeID
QString m_volumeID
The volume ID of the media. Read/write.
Definition: mythmedia.h:157
MythMediaDevice::MediaTypeString
QString MediaTypeString()
Definition: mythmedia.cpp:515
StHandle::~StHandle
~StHandle()
Definition: mythcdrom-linux.cpp:329
CDROMdiscInfo::m_borderStatus
uint8_t m_borderStatus
Definition: mythcdrom-linux.cpp:84