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 {
123  MEDIA_IS_OTHER = 0x3
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  std::array<uint8_t,32> buffer {};
198  CDROMgenericCmd cgc {};
199 
200  cgc.cmd[0] = GPCMD_READ_DISC_INFO;
201  cgc.cmd[8] = buffer.size();
202  cgc.quiet = 1;
203  cgc.buffer = buffer.data();
204  cgc.buflen = buffer.size();
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  auto *di = (CDROMdiscInfo *) buffer.data();
215  switch (di->m_discStatus)
216  {
217  case MEDIA_IS_EMPTY:
218  return true;
219 
220  case MEDIA_IS_APPENDABLE:
221  // It is unlikely that any plugins will support multi-session
222  // writing, so we treat it just like a finished disc:
223 
224  case MEDIA_IS_COMPLETE:
225  return di->m_erasable;
226 
227  case MEDIA_IS_OTHER:
228  ;
229  }
230 
231  return false;
232 }
233 
243 {
244  std::array<uint8_t,8> buffer {};
245  CDROMgenericCmd cgc {};
246 
247  cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
248  cgc.cmd[1] = 1; // Tell us immediately
249  cgc.cmd[4] = 1 << 4; // notification class of media
250  cgc.cmd[8] = buffer.size();
251  cgc.quiet = 1;
252  cgc.buffer = buffer.data();
253  cgc.buflen = buffer.size();
254  cgc.data_direction = CGC_DATA_READ;
255 
256  auto *es = (CDROMeventStatus *) buffer.data();
257 
258  if ((ioctl(m_deviceHandle, CDROM_SEND_PACKET, &cgc) < 0)
259  || es->m_nea // drive does not support request
260  || (es->m_notificationClass != 0x4)) // notification class mismatch
261  {
262  LOG(VB_MEDIA, LOG_ERR, LOC +
263  ":SCSIstatus() - failed to send SCSI packet to " + m_devicePath + ENO);
264  return CDS_TRAY_OPEN;
265  }
266 
267  if (es->m_mediaPresent)
268  {
269  LOG(VB_MEDIA, LOG_DEBUG, LOC +
270  ":SCSIstatus() - ioctl said tray was open, "
271  "but drive is actually closed with a disc");
272  return CDS_DISC_OK;
273  }
274  if (es->m_doorOpen)
275  {
276  LOG(VB_MEDIA, LOG_DEBUG, LOC +
277  ":SCSIstatus() - tray is definitely open");
278  return CDS_TRAY_OPEN;
279  }
280 
281  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":SCSIstatus() - ioctl said tray was open, "
282  "but drive is actually closed with no disc");
283  return CDS_NO_DISC;
284 }
285 
286 
288 {
289  if (!isDeviceOpen())
290  {
291  if (!openDevice())
292  return MEDIAERR_FAILED;
293  }
294 
295  MythMediaError err = ejectCDROM(open_close);
296  if (MEDIAERR_OK != err && open_close)
297  err = ejectSCSI();
298 
299  return err;
300 }
301 
303 {
304  if (open_close)
305  {
306  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":eject - Ejecting CDROM");
307  int res = ioctl(m_deviceHandle, CDROMEJECT);
308 
309  if (res < 0)
310  LOG(VB_MEDIA, LOG_DEBUG, "CDROMEJECT ioctl failed" + ENO);
311 
312  return (res == 0) ? MEDIAERR_OK : MEDIAERR_FAILED;
313  }
314 
315  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":eject - Loading CDROM");
316  // If the tray is empty, this will fail (Input/Output error)
317  int res = ioctl(m_deviceHandle, CDROMCLOSETRAY);
318 
319  if (res < 0)
320  LOG(VB_MEDIA, LOG_DEBUG, "CDROMCLOSETRAY ioctl failed" + ENO);
321 
322  // This allows us to catch any drives that the OS has problems
323  // detecting the status of (some always report OPEN when empty)
324  if (driveStatus() == CDS_TRAY_OPEN)
325  return MEDIAERR_FAILED;
326  return MEDIAERR_OK;
327 }
328 
329 struct StHandle {
330  const int m_fd;
331  explicit StHandle(const char *dev) : m_fd(open(dev, O_RDWR | O_NONBLOCK)) { }
333  operator int() const { return m_fd; } // NOLINT(google-explicit-constructor)
334 };
335 
336 // This is copied from eject.c by Jeff Tranter (tranter@pobox.com)
338 {
339  int k = 0;
340  sg_io_hdr_t io_hdr;
341  std::array<uint8_t,6> allowRmBlk {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
342  std::array<uint8_t,6> startStop1Blk {START_STOP, 0, 0, 0, 1, 0}; // start
343  std::array<uint8_t,6> startStop2Blk {START_STOP, 0, 0, 0, 2, 0}; // load eject
344  std::array<uint8_t,16> sense_buffer {};
345  const unsigned DID_OK = 0;
346  const unsigned DRIVER_OK = 0;
347 
348  // ALLOW_MEDIUM_REMOVAL requires r/w access so re-open the device
349  struct StHandle fd(qPrintable(m_devicePath));
350 
351  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":ejectSCSI");
352  if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
353  {
354  // not an sg device, or old sg driver
355  LOG(VB_MEDIA, LOG_DEBUG, "SG_GET_VERSION_NUM ioctl failed" + ENO);
356  return MEDIAERR_FAILED;
357  }
358 
359  memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
360  io_hdr.interface_id = 'S';
361  io_hdr.cmd_len = 6;
362  io_hdr.mx_sb_len = sense_buffer.size();
363  io_hdr.dxfer_direction = SG_DXFER_NONE;
364  io_hdr.sbp = sense_buffer.data();
365  io_hdr.timeout = 10000; // millisecs
366 
367  io_hdr.cmdp = allowRmBlk.data();
368  if (ioctl(fd, SG_IO, &io_hdr) < 0)
369  {
370  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO allowRmBlk ioctl failed" + ENO);
371  return MEDIAERR_FAILED;
372  }
373  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
374  {
375  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO allowRmBlk failed");
376  return MEDIAERR_FAILED;
377  }
378 
379  io_hdr.cmdp = startStop1Blk.data();
380  if (ioctl(fd, SG_IO, &io_hdr) < 0)
381  {
382  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(start) ioctl failed" + ENO);
383  return MEDIAERR_FAILED;
384  }
385  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
386  {
387  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(start) failed");
388  return MEDIAERR_FAILED;
389  }
390 
391  io_hdr.cmdp = startStop2Blk.data();
392  if (ioctl(fd, SG_IO, &io_hdr) < 0)
393  {
394  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(eject) ioctl failed" + ENO);
395  return MEDIAERR_FAILED;
396  }
397  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
398  {
399  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(eject) failed");
400  return MEDIAERR_FAILED;
401  }
402 
403  /* force kernel to reread partition table when new disc inserted */
404  (void)ioctl(fd, BLKRRPART);
405  return MEDIAERR_OK;
406 }
407 
408 
410 {
411  return (ioctl(m_deviceHandle, CDROM_MEDIA_CHANGED, CDSL_CURRENT) > 0);
412 }
413 
415 {
416  return (ioctl(m_deviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT) ==
417  CDS_DISC_OK);
418 }
419 
420 // Helper function, perform a sanity check on the device
422 {
423  bool OpenedHere = false;
424  if (!isDeviceOpen())
425  {
426  if (!openDevice())
427  {
428  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":testMedia - failed to open '" +
429  m_devicePath + "' : " +ENO);
430  if (errno == EBUSY)
432  return MEDIAERR_FAILED;
433  }
434  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":testMedia - Opened device");
435  OpenedHere = true;
436  }
437 
438  // Since the device was is/was open we can get it's status...
439  int Stat = driveStatus();
440 
441  // Be nice and close the device if we opened it,
442  // otherwise it might be locked when the user doesn't want it to be.
443  if (OpenedHere)
444  closeDevice();
445 
446  if (Stat == -1)
447  {
448  LOG(VB_MEDIA, LOG_DEBUG, LOC +
449  ":testMedia - Failed to get drive status of '" + m_devicePath +
450  "' : " + ENO);
451  return MEDIAERR_FAILED;
452  }
453 
454  return MEDIAERR_OK;
455 }
456 
458 {
459  bool OpenedHere = false;
460 
461  // If it's not already open we need to at least
462  // TRY to open it for most of these checks to work.
463  if (!isDeviceOpen())
464  {
465  OpenedHere = openDevice();
466 
467  if (!OpenedHere)
468  {
469  LOG(VB_MEDIA, LOG_ERR, LOC +
470  ":checkMedia() - cannot open device '" + m_devicePath + "' : " +
471  ENO + "- returning UNKNOWN");
473  return setStatus(MEDIASTAT_UNKNOWN, false);
474  }
475  }
476 
477  switch (driveStatus())
478  {
479  case CDS_DISC_OK:
480  LOG(VB_MEDIA, LOG_DEBUG, m_devicePath + " Disk OK, type = " +
482  // further checking is required
483  break;
484  case CDS_TRAY_OPEN:
485  LOG(VB_MEDIA, LOG_DEBUG, m_devicePath + " Tray open or no disc");
486  // First, send a message to the
487  // plugins to forget the current media type
488  setStatus(MEDIASTAT_OPEN, OpenedHere);
489  // then "clear out" this device
491  return MEDIASTAT_OPEN;
492  break;
493  case CDS_NO_DISC:
494  LOG(VB_MEDIA, LOG_DEBUG, m_devicePath + " No disc");
496  return setStatus(MEDIASTAT_NODISK, OpenedHere);
497  break;
498  case CDS_NO_INFO:
499  case CDS_DRIVE_NOT_READY:
500  LOG(VB_MEDIA, LOG_DEBUG, m_devicePath +
501  " No info or drive not ready");
503  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
504  default:
505  LOG(VB_GENERAL, LOG_ERR, "Failed to get drive status of " +
506  m_devicePath + " : " + ENO);
508  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
509  }
510 
511  // NB must call mediaChanged before testing m_status otherwise will get
512  // an unwanted mediaChanged on next pass
514  {
515  LOG(VB_MEDIA, LOG_INFO, m_devicePath + " Media changed");
516  // Regardless of the actual status lie here and say
517  // it's open for now, so we can cover the case of a missed open.
518  return setStatus(MEDIASTAT_OPEN, OpenedHere);
519  }
520 
521 
522  if (isUsable())
523  {
524  LOG(VB_MEDIA, LOG_DEBUG, "Disc useable, media unchanged. All good!");
525  if (OpenedHere)
526  closeDevice();
527  return MEDIASTAT_USEABLE;
528  }
529 
530  // If we have tried to mount and failed, don't keep trying
531  if (m_status == MEDIASTAT_ERROR)
532  {
533  // Check if an external agent (like Gnome/KDE) mounted the disk
534  if (isMounted())
535  {
536  onDeviceMounted();
537  // pretend we're NOTMOUNTED so setStatus emits a signal
539  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
540  }
541 
542  LOG(VB_MEDIA, LOG_DEBUG, "Disc is unmountable?");
543  if (OpenedHere)
544  closeDevice();
545  return m_status;
546  }
547 
548  if ((m_status == MEDIASTAT_OPEN) ||
550  {
551  LOG(VB_MEDIA, LOG_INFO, m_devicePath + " Current status " +
553  int type = ioctl(m_deviceHandle, CDROM_DISC_STATUS, CDSL_CURRENT);
554  switch (type)
555  {
556  case CDS_DATA_1:
557  case CDS_DATA_2:
558  {
560  LOG(VB_MEDIA, LOG_INFO, "Found a data disk");
561 
562  //grab information from iso9660 (& udf)
564  (off_t) 2048*16, SEEK_SET);
565 
566  struct iso_primary_descriptor buf {};
567  ssize_t readin = 0;
568  while ((sr != (off_t) -1) && (readin < 2048))
569  {
570  ssize_t rr = read(
571  m_deviceHandle, ((char*)&buf) + readin, 2048 - readin);
572  if ((rr < 0) && ((EAGAIN == errno) || (EINTR == errno)))
573  continue;
574  if (rr < 0)
575  break;
576  readin += rr;
577  }
578 
579  if (readin == 2048)
580  {
581  m_volumeID = QString(buf.volume_id).trimmed();
582  m_keyID = QString("%1%2")
583  .arg(m_volumeID)
584  .arg(QString(reinterpret_cast<char*>(buf.creation_date)).left(16));
585  }
586  else
587  {
588  m_volumeID = "UNKNOWN";
590  }
591 
592  LOG(VB_MEDIA, LOG_INFO,
593  QString("Volume ID: %1").arg(m_volumeID));
594  {
596 /*
597  if( imageType == MythCDROM::kBluray )
598  m_mediaType = MEDIATYPE_BD;
599  else
600 */
601  if( imageType == MythCDROM::kDVD )
603 
605  {
606  // pretend we're NOTMOUNTED so setStatus emits a signal
608  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
609  }
610  }
611 
612  // the base class's onDeviceMounted will do fine
613  // grained detection of the type of data on this disc
614  if (isMounted())
615  onDeviceMounted();
616  else if (!mount()) // onDeviceMounted() called as side-effect
617  return setStatus(MEDIASTAT_NOTMOUNTED, OpenedHere);
618 
619  if (isMounted())
620  {
621  // pretend we're NOTMOUNTED so setStatus emits a signal
623  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
624  }
625  if (m_mediaType == MEDIATYPE_DVD)
626  {
627  // pretend we're NOTMOUNTED so setStatus emits a signal
629  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
630  }
631  return setStatus(MEDIASTAT_NOTMOUNTED, OpenedHere);
632  }
633  case CDS_AUDIO:
634  LOG(VB_MEDIA, LOG_DEBUG, "found an audio disk");
635  // pretend we're NOTMOUNTED so setStatus emits a signal
638  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
639  case CDS_MIXED:
640  LOG(VB_MEDIA, LOG_DEBUG, "found a mixed CD");
641  // Note: Mixed mode CDs require an explixit mount call
642  // since we'll usually want the audio portion.
643  // undefine ASSUME_WANT_AUDIO to change this behavior.
644  #if ASSUME_WANT_AUDIO
645  // pretend we're NOTMOUNTED so setStatus emits a signal
648  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
649  #else
651  mount();
652  if (isMounted())
653  {
654  // pretend we're NOTMOUNTED so setStatus
655  // emits a signal
657  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
658  }
659  else
660  {
661  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
662  }
663  #endif
664  break;
665  case CDS_NO_INFO:
666  case CDS_NO_DISC:
667  if (hasWritableMedia())
668  {
669  LOG(VB_MEDIA, LOG_DEBUG, "found a blank or writable disk");
670  return setStatus(MEDIASTAT_UNFORMATTED, OpenedHere);
671  }
672 
673  LOG(VB_MEDIA, LOG_DEBUG, "found no disk");
675  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
676  break;
677  default:
678  LOG(VB_MEDIA, LOG_DEBUG, "found unknown disk type: " +
679  QString::number(type));
681  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
682  }
683  }
684 
685  if (m_allowEject)
686  unlock();
687  else
688  lock();
689 
690  if (OpenedHere)
691  closeDevice();
692 
693  LOG(VB_MEDIA, LOG_DEBUG, QString("Returning %1")
695  return m_status;
696 }
697 
699 {
701  if (ret == MEDIAERR_OK)
702  {
703  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":lock - Locking CDROM door");
704  int res = ioctl(m_deviceHandle, CDROM_LOCKDOOR, 1);
705 
706  if (res < 0)
707  LOG(VB_MEDIA, LOG_WARNING, "lock() - CDROM_LOCKDOOR ioctl failed" + ENO);
708  }
709 
710  return ret;
711 }
712 
714 {
715  if (isDeviceOpen() || openDevice())
716  {
717  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":unlock - Unlocking CDROM door");
718  int res = ioctl(m_deviceHandle, CDROM_LOCKDOOR, 0);
719 
720  if (res < 0)
721  LOG(VB_MEDIA, LOG_WARNING, "unlock() - CDROM_LOCKDOOR ioctl failed" + ENO);
722  }
723  else
724  {
725  LOG(VB_GENERAL, LOG_INFO, "Failed to open device, CDROM tray will "
726  "remain locked.");
727  }
728 
729  return MythMediaDevice::unlock();
730 }
731 
732 bool MythCDROMLinux::isSameDevice(const QString &path)
733 {
734  struct stat sb {};
735 
736  if (stat(path.toLocal8Bit().constData(), &sb) < 0)
737  {
738  LOG(VB_GENERAL, LOG_ERR, LOC + ":isSameDevice() -- " +
739  QString("Failed to stat '%1'").arg(path) + ENO);
740  return false;
741  }
742  dev_t new_rdev = sb.st_rdev;
743 
744  // Check against m_devicePath...
745  if (stat(m_devicePath.toLocal8Bit().constData(), &sb) < 0)
746  {
747  LOG(VB_GENERAL, LOG_ERR, LOC + ":isSameDevice() -- " +
748  QString("Failed to stat '%1'").arg(m_devicePath) + ENO);
749  return false;
750  }
751  return (sb.st_rdev == new_rdev);
752 }
753 
754 #if defined(SG_IO) && defined(GPCMD_SET_STREAMING)
755 /*
756  * \brief obtained from the mplayer project
757  */
758 void MythCDROMLinux::setDeviceSpeed(const char *device, int speed)
759 {
760  std::array<uint8_t,28> buffer {};
761  std::array<uint8_t,16> cmd {};
762  std::array<uint8_t,16> sense {};
763  struct sg_io_hdr sghdr {};
764  struct stat st {};
765  int rate = 0;
766 
767  int fd = open(device, O_RDWR | O_NONBLOCK);
768  if (fd == -1)
769  {
770  LOG(VB_MEDIA, LOG_ERR, LOC +
771  " Changing CD/DVD speed needs write access");
772  return;
773  }
774 
775  if (fstat(fd, &st) == -1)
776  {
777  close(fd);
778  LOG(VB_MEDIA, LOG_ERR, LOC +
779  QString(":setDeviceSpeed() Failed. device %1 not found")
780  .arg(device));
781  return;
782  }
783 
784  if (!S_ISBLK(st.st_mode))
785  {
786  close(fd);
787  LOG(VB_MEDIA, LOG_ERR, LOC +
788  ":setDeviceSpeed() Failed. Not a block device");
789  return;
790  }
791 
792  if (speed < 0)
793  speed = -1;
794 
795  switch(speed)
796  {
797  case 0: // don't touch speed setting
798  close(fd);
799  return;
800  case -1: // restore default value
801  {
802  rate = 0;
803  buffer[0] = 4;
804  LOG(VB_MEDIA, LOG_INFO, LOC +
805  ":setDeviceSpeed() - Restored CD/DVD Speed");
806  break;
807  }
808  default:
809  {
810  // Speed in Kilobyte/Second. 177KB/s is the maximum data rate
811  // for standard Audio CD's.
812 
813  rate = (speed > 0 && speed < 100) ? speed * 177 : speed;
814 
815  LOG(VB_MEDIA, LOG_INFO, LOC +
816  QString(":setDeviceSpeed() - Limiting CD/DVD Speed to %1KB/s")
817  .arg(rate));
818  break;
819  }
820  }
821 
822  sghdr.interface_id = 'S';
823  sghdr.timeout = 5000;
824  sghdr.dxfer_direction = SG_DXFER_TO_DEV;
825  sghdr.mx_sb_len = sense.size();
826  sghdr.dxfer_len = buffer.size();
827  sghdr.cmd_len = cmd.size();
828  sghdr.sbp = sense.data();
829  sghdr.dxferp = buffer.data();
830  sghdr.cmdp = cmd.data();
831 
832  cmd[0] = GPCMD_SET_STREAMING;
833  cmd[10] = buffer.size();
834 
835  buffer[8] = 0xff;
836  buffer[9] = 0xff;
837  buffer[10] = 0xff;
838  buffer[11] = 0xff;
839 
840  buffer[12] = buffer[20] = (rate >> 24) & 0xff;
841  buffer[13] = buffer[21] = (rate >> 16) & 0xff;
842  buffer[14] = buffer[22] = (rate >> 8) & 0xff;
843  buffer[15] = buffer[23] = rate & 0xff;
844 
845  // Note: 0x3e8 == 1000, hence speed = data amount per 1000 milliseconds.
846  buffer[18] = buffer[26] = 0x03;
847  buffer[19] = buffer[27] = 0xe8;
848 
849  if (ioctl(fd, SG_IO, &sghdr) < 0)
850  {
851  LOG(VB_MEDIA, LOG_ERR, LOC + " Limit CD/DVD Speed Failed" + ENO);
852  }
853  else
854  {
855  // On my system (2.6.18+ide-cd), SG_IO succeeds without doing anything,
856  // while CDROM_SELECT_SPEED works...
857  if (ioctl(fd, CDROM_SELECT_SPEED, speed) < 0)
858  {
859  LOG(VB_MEDIA, LOG_ERR, LOC +
860  " Limit CD/DVD CDROM_SELECT_SPEED Failed" + ENO);
861  }
862  LOG(VB_MEDIA, LOG_INFO, LOC +
863  ":setDeviceSpeed() - CD/DVD Speed Set Successful");
864  }
865 
866  close(fd);
867 }
868 #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:713
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:421
MEDIATYPE_UNKNOWN
@ MEDIATYPE_UNKNOWN
Definition: mythmedia.h:25
MythCDROMLinux::eject
MythMediaError eject(bool open_close=true) override
Definition: mythcdrom-linux.cpp:287
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:106
MythCDROMLinux::setDeviceSpeed
void setDeviceSpeed(const char *device, int speed) override
MythCDROMLinux::mediaChanged
bool mediaChanged(void) override
Definition: mythcdrom-linux.cpp:409
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:119
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:82
arg
arg(title).arg(filename).arg(doDelete))
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:331
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:302
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:242
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
lseek
#define lseek
Definition: mythiowrapper.cpp:236
MythCDROMLinux::isSameDevice
bool isSameDevice(const QString &path) override
Definition: mythcdrom-linux.cpp:732
MythMediaDevice::isMounted
bool isMounted(bool bVerify=true)
Tells us if m_devicePath is a mounted device.
Definition: mythmedia.cpp:358
CDROMdiscInfo::m_discType
uint8_t m_discType
Definition: mythcdrom-linux.cpp:103
mythdate.h
MythCDROMLinux::checkMedia
MythMediaStatus checkMedia(void) override
Definition: mythcdrom-linux.cpp:457
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:24
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:27
MythMediaDevice::unlock
virtual MythMediaError unlock()
Definition: mythmedia.cpp:350
MythCDROM
Definition: mythcdrom.h:7
MEDIATYPE_AUDIO
@ MEDIATYPE_AUDIO
Definition: mythmedia.h:28
MythMediaDevice::setStatus
MythMediaStatus setStatus(MythMediaStatus newStat, bool CloseIt=false)
Definition: mythmedia.cpp:463
CDROMdiscInfo::m_leadOut
uint32_t m_leadOut
Definition: mythcdrom-linux.cpp:109
MythMediaDevice::lock
virtual MythMediaError lock()
Definition: mythmedia.cpp:337
MEDIA_IS_COMPLETE
@ MEDIA_IS_COMPLETE
Definition: mythcdrom-linux.cpp:122
off_t
#define off_t
Definition: mythiowrapper.cpp:238
MythCDROM::inspectImage
static ImageType inspectImage(const QString &path)
Definition: mythcdrom.cpp:179
MythCDROMLinux
Use Linux-specific ioctl()s to detect Audio-CDs, changed media, open trays and blank writable media.
Definition: mythcdrom-linux.cpp:134
CDROMeventStatus::m_reserved2
uint8_t m_reserved2
Definition: mythcdrom-linux.cpp:65
MythCDROMLinux::ejectSCSI
MythMediaError ejectSCSI()
Definition: mythcdrom-linux.cpp:337
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:45
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:330
MythCDROMLinux::checkOK
bool checkOK(void) override
Definition: mythcdrom-linux.cpp:414
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:698
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:94
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:329
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:516
StHandle::~StHandle
~StHandle()
Definition: mythcdrom-linux.cpp:332
CDROMdiscInfo::m_borderStatus
uint8_t m_borderStatus
Definition: mythcdrom-linux.cpp:84