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