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