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  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 
319  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":eject - Loading CDROM");
320  // If the tray is empty, this will fail (Input/Output error)
321  int res = ioctl(m_DeviceHandle, CDROMCLOSETRAY);
322 
323  if (res < 0)
324  LOG(VB_MEDIA, LOG_DEBUG, "CDROMCLOSETRAY ioctl failed" + ENO);
325 
326  // This allows us to catch any drives that the OS has problems
327  // detecting the status of (some always report OPEN when empty)
328  if (driveStatus() == CDS_TRAY_OPEN)
329  return MEDIAERR_FAILED;
330  return MEDIAERR_OK;
331 }
332 
333 struct StHandle {
334  const int m_fd;
335  explicit StHandle(const char *dev) : m_fd(open(dev, O_RDWR | O_NONBLOCK)) { }
337  operator int() const { return m_fd; }
338 };
339 
340 // This is copied from eject.c by Jeff Tranter (tranter@pobox.com)
342 {
343  int k;
344  sg_io_hdr_t io_hdr;
345  unsigned char allowRmBlk[6] = {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
346  unsigned char startStop1Blk[6] = {START_STOP, 0, 0, 0, 1, 0}; // start
347  unsigned char startStop2Blk[6] = {START_STOP, 0, 0, 0, 2, 0}; // load eject
348  unsigned char sense_buffer[16];
349  const unsigned DID_OK = 0;
350  const unsigned DRIVER_OK = 0;
351 
352  // ALLOW_MEDIUM_REMOVAL requires r/w access so re-open the device
353  struct StHandle fd(qPrintable(m_DevicePath));
354 
355  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":ejectSCSI");
356  if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
357  {
358  // not an sg device, or old sg driver
359  LOG(VB_MEDIA, LOG_DEBUG, "SG_GET_VERSION_NUM ioctl failed" + ENO);
360  return MEDIAERR_FAILED;
361  }
362 
363  memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
364  io_hdr.interface_id = 'S';
365  io_hdr.cmd_len = 6;
366  io_hdr.mx_sb_len = sizeof(sense_buffer);
367  io_hdr.dxfer_direction = SG_DXFER_NONE;
368  io_hdr.sbp = sense_buffer;
369  io_hdr.timeout = 10000; // millisecs
370 
371  io_hdr.cmdp = allowRmBlk;
372  if (ioctl(fd, SG_IO, &io_hdr) < 0)
373  {
374  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO allowRmBlk ioctl failed" + ENO);
375  return MEDIAERR_FAILED;
376  }
377  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
378  {
379  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO allowRmBlk failed");
380  return MEDIAERR_FAILED;
381  }
382 
383  io_hdr.cmdp = startStop1Blk;
384  if (ioctl(fd, SG_IO, &io_hdr) < 0)
385  {
386  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(start) ioctl failed" + ENO);
387  return MEDIAERR_FAILED;
388  }
389  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
390  {
391  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(start) failed");
392  return MEDIAERR_FAILED;
393  }
394 
395  io_hdr.cmdp = startStop2Blk;
396  if (ioctl(fd, SG_IO, &io_hdr) < 0)
397  {
398  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(eject) ioctl failed" + ENO);
399  return MEDIAERR_FAILED;
400  }
401  if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
402  {
403  LOG(VB_MEDIA, LOG_DEBUG, "SG_IO START_STOP(eject) failed");
404  return MEDIAERR_FAILED;
405  }
406 
407  /* force kernel to reread partition table when new disc inserted */
408  (void)ioctl(fd, BLKRRPART);
409  return MEDIAERR_OK;
410 }
411 
412 
414 {
415  return (ioctl(m_DeviceHandle, CDROM_MEDIA_CHANGED, CDSL_CURRENT) > 0);
416 }
417 
419 {
420  return (ioctl(m_DeviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT) ==
421  CDS_DISC_OK);
422 }
423 
424 // Helper function, perform a sanity check on the device
426 {
427  bool OpenedHere = false;
428  if (!isDeviceOpen())
429  {
430  if (!openDevice())
431  {
432  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":testMedia - failed to open '" +
433  m_DevicePath + "' : " +ENO);
434  if (errno == EBUSY)
436  return MEDIAERR_FAILED;
437  }
438  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":testMedia - Opened device");
439  OpenedHere = true;
440  }
441 
442  // Since the device was is/was open we can get it's status...
443  int Stat = driveStatus();
444 
445  // Be nice and close the device if we opened it,
446  // otherwise it might be locked when the user doesn't want it to be.
447  if (OpenedHere)
448  closeDevice();
449 
450  if (Stat == -1)
451  {
452  LOG(VB_MEDIA, LOG_DEBUG, LOC +
453  ":testMedia - Failed to get drive status of '" + m_DevicePath +
454  "' : " + ENO);
455  return MEDIAERR_FAILED;
456  }
457 
458  return MEDIAERR_OK;
459 }
460 
462 {
463  bool OpenedHere = false;
464 
465  // If it's not already open we need to at least
466  // TRY to open it for most of these checks to work.
467  if (!isDeviceOpen())
468  {
469  OpenedHere = openDevice();
470 
471  if (!OpenedHere)
472  {
473  LOG(VB_MEDIA, LOG_ERR, LOC +
474  ":checkMedia() - cannot open device '" + m_DevicePath + "' : " +
475  ENO + "- returning UNKNOWN");
477  return setStatus(MEDIASTAT_UNKNOWN, false);
478  }
479  }
480 
481  switch (driveStatus())
482  {
483  case CDS_DISC_OK:
484  LOG(VB_MEDIA, LOG_DEBUG, m_DevicePath + " Disk OK, type = " +
486  // further checking is required
487  break;
488  case CDS_TRAY_OPEN:
489  LOG(VB_MEDIA, LOG_DEBUG, m_DevicePath + " Tray open or no disc");
490  // First, send a message to the
491  // plugins to forget the current media type
492  setStatus(MEDIASTAT_OPEN, OpenedHere);
493  // then "clear out" this device
495  return MEDIASTAT_OPEN;
496  break;
497  case CDS_NO_DISC:
498  LOG(VB_MEDIA, LOG_DEBUG, m_DevicePath + " No disc");
500  return setStatus(MEDIASTAT_NODISK, OpenedHere);
501  break;
502  case CDS_NO_INFO:
503  case CDS_DRIVE_NOT_READY:
504  LOG(VB_MEDIA, LOG_DEBUG, m_DevicePath +
505  " No info or drive not ready");
507  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
508  default:
509  LOG(VB_GENERAL, LOG_ERR, "Failed to get drive status of " +
510  m_DevicePath + " : " + ENO);
512  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
513  }
514 
515  // NB must call mediaChanged before testing m_Status otherwise will get
516  // an unwanted mediaChanged on next pass
518  {
519  LOG(VB_MEDIA, LOG_INFO, m_DevicePath + " Media changed");
520  // Regardless of the actual status lie here and say
521  // it's open for now, so we can cover the case of a missed open.
522  return setStatus(MEDIASTAT_OPEN, OpenedHere);
523  }
524 
525 
526  if (isUsable())
527  {
528  LOG(VB_MEDIA, LOG_DEBUG, "Disc useable, media unchanged. All good!");
529  if (OpenedHere)
530  closeDevice();
531  return MEDIASTAT_USEABLE;
532  }
533 
534  // If we have tried to mount and failed, don't keep trying
535  if (m_Status == MEDIASTAT_ERROR)
536  {
537  // Check if an external agent (like Gnome/KDE) mounted the disk
538  if (isMounted())
539  {
540  onDeviceMounted();
541  // pretend we're NOTMOUNTED so setStatus emits a signal
543  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
544  }
545 
546  LOG(VB_MEDIA, LOG_DEBUG, "Disc is unmountable?");
547  if (OpenedHere)
548  closeDevice();
549  return m_Status;
550  }
551 
552  if ((m_Status == MEDIASTAT_OPEN) ||
554  {
555  LOG(VB_MEDIA, LOG_INFO, m_DevicePath + " Current status " +
557  int type = ioctl(m_DeviceHandle, CDROM_DISC_STATUS, CDSL_CURRENT);
558  switch (type)
559  {
560  case CDS_DATA_1:
561  case CDS_DATA_2:
562  {
564  LOG(VB_MEDIA, LOG_INFO, "Found a data disk");
565 
566  //grab information from iso9660 (& udf)
568  (off_t) 2048*16, SEEK_SET);
569 
570  struct iso_primary_descriptor buf;
571  ssize_t readin = 0;
572  while ((sr != (off_t) -1) && (readin < 2048))
573  {
574  ssize_t rr = read(
575  m_DeviceHandle, ((char*)&buf) + readin, 2048 - readin);
576  if ((rr < 0) && ((EAGAIN == errno) || (EINTR == errno)))
577  continue;
578  if (rr < 0)
579  break;
580  readin += rr;
581  }
582 
583  if (readin == 2048)
584  {
585  m_VolumeID = QString(buf.volume_id).trimmed();
586  m_KeyID = QString("%1%2")
587  .arg(m_VolumeID)
588  .arg(QString(reinterpret_cast<char*>(buf.creation_date)).left(16));
589  }
590  else
591  {
592  m_VolumeID = "UNKNOWN";
594  }
595 
596  LOG(VB_MEDIA, LOG_INFO,
597  QString("Volume ID: %1").arg(m_VolumeID));
598  {
600 /*
601  if( imageType == MythCDROM::kBluray )
602  m_MediaType = MEDIATYPE_BD;
603  else
604 */
605  if( imageType == MythCDROM::kDVD )
607 
609  {
610  // pretend we're NOTMOUNTED so setStatus emits a signal
612  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
613  }
614  }
615 
616  // the base class's onDeviceMounted will do fine
617  // grained detection of the type of data on this disc
618  if (isMounted())
619  onDeviceMounted();
620  else if (!mount()) // onDeviceMounted() called as side-effect
621  return setStatus(MEDIASTAT_NOTMOUNTED, OpenedHere);
622 
623  if (isMounted())
624  {
625  // pretend we're NOTMOUNTED so setStatus emits a signal
627  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
628  }
629  if (m_MediaType == MEDIATYPE_DVD)
630  {
631  // pretend we're NOTMOUNTED so setStatus emits a signal
633  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
634  }
635  return setStatus(MEDIASTAT_NOTMOUNTED, OpenedHere);
636  }
637  case CDS_AUDIO:
638  LOG(VB_MEDIA, LOG_DEBUG, "found an audio disk");
639  // pretend we're NOTMOUNTED so setStatus emits a signal
642  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
643  case CDS_MIXED:
644  LOG(VB_MEDIA, LOG_DEBUG, "found a mixed CD");
645  // Note: Mixed mode CDs require an explixit mount call
646  // since we'll usually want the audio portion.
647  // undefine ASSUME_WANT_AUDIO to change this behavior.
648  #if ASSUME_WANT_AUDIO
649  // pretend we're NOTMOUNTED so setStatus emits a signal
652  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
653  #else
655  mount();
656  if (isMounted())
657  {
658  // pretend we're NOTMOUNTED so setStatus
659  // emits a signal
661  return setStatus(MEDIASTAT_MOUNTED, OpenedHere);
662  }
663  else
664  {
665  return setStatus(MEDIASTAT_USEABLE, OpenedHere);
666  }
667  #endif
668  break;
669  case CDS_NO_INFO:
670  case CDS_NO_DISC:
671  if (hasWritableMedia())
672  {
673  LOG(VB_MEDIA, LOG_DEBUG, "found a blank or writable disk");
674  return setStatus(MEDIASTAT_UNFORMATTED, OpenedHere);
675  }
676 
677  LOG(VB_MEDIA, LOG_DEBUG, "found no disk");
679  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
680  break;
681  default:
682  LOG(VB_MEDIA, LOG_DEBUG, "found unknown disk type: " +
683  QString::number(type));
685  return setStatus(MEDIASTAT_UNKNOWN, OpenedHere);
686  }
687  }
688 
689  if (m_AllowEject)
690  unlock();
691  else
692  lock();
693 
694  if (OpenedHere)
695  closeDevice();
696 
697  LOG(VB_MEDIA, LOG_DEBUG, QString("Returning %1")
699  return m_Status;
700 }
701 
703 {
705  if (ret == MEDIAERR_OK)
706  {
707  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":lock - Locking CDROM door");
708  int res = ioctl(m_DeviceHandle, CDROM_LOCKDOOR, 1);
709 
710  if (res < 0)
711  LOG(VB_MEDIA, LOG_WARNING, "lock() - CDROM_LOCKDOOR ioctl failed" + ENO);
712  }
713 
714  return ret;
715 }
716 
718 {
719  if (isDeviceOpen() || openDevice())
720  {
721  LOG(VB_MEDIA, LOG_DEBUG, LOC + ":unlock - Unlocking CDROM door");
722  int res = ioctl(m_DeviceHandle, CDROM_LOCKDOOR, 0);
723 
724  if (res < 0)
725  LOG(VB_MEDIA, LOG_WARNING, "unlock() - CDROM_LOCKDOOR ioctl failed" + ENO);
726  }
727  else
728  {
729  LOG(VB_GENERAL, LOG_INFO, "Failed to open device, CDROM tray will "
730  "remain locked.");
731  }
732 
733  return MythMediaDevice::unlock();
734 }
735 
736 bool MythCDROMLinux::isSameDevice(const QString &path)
737 {
738  dev_t new_rdev;
739  struct stat sb;
740 
741  if (stat(path.toLocal8Bit().constData(), &sb) < 0)
742  {
743  LOG(VB_GENERAL, LOG_ERR, LOC + ":isSameDevice() -- " +
744  QString("Failed to stat '%1'").arg(path) + ENO);
745  return false;
746  }
747  new_rdev = sb.st_rdev;
748 
749  // Check against m_DevicePath...
750  if (stat(m_DevicePath.toLocal8Bit().constData(), &sb) < 0)
751  {
752  LOG(VB_GENERAL, LOG_ERR, LOC + ":isSameDevice() -- " +
753  QString("Failed to stat '%1'").arg(m_DevicePath) + ENO);
754  return false;
755  }
756  return (sb.st_rdev == new_rdev);
757 }
758 
759 #if defined(SG_IO) && defined(GPCMD_SET_STREAMING)
760 /*
761  * \brief obtained from the mplayer project
762  */
763 void MythCDROMLinux::setDeviceSpeed(const char *device, int speed)
764 {
765  int fd;
766  unsigned char buffer[28];
767  unsigned char cmd[16];
768  unsigned char sense[16];
769  struct sg_io_hdr sghdr;
770  struct stat st;
771  int rate = 0;
772 
773  memset(&sghdr, 0, sizeof(sghdr));
774  memset(buffer, 0, sizeof(buffer));
775  memset(sense, 0, sizeof(sense));
776  memset(cmd, 0, sizeof(cmd));
777  memset(&st, 0, sizeof(st));
778 
779  if ((fd = open(device, O_RDWR | O_NONBLOCK)) == -1)
780  {
781  LOG(VB_MEDIA, LOG_ERR, LOC +
782  " Changing CD/DVD speed needs write access");
783  return;
784  }
785 
786  if (fstat(fd, &st) == -1)
787  {
788  close(fd);
789  LOG(VB_MEDIA, LOG_ERR, LOC +
790  QString(":setDeviceSpeed() Failed. device %1 not found")
791  .arg(device));
792  return;
793  }
794 
795  if (!S_ISBLK(st.st_mode))
796  {
797  close(fd);
798  LOG(VB_MEDIA, LOG_ERR, LOC +
799  ":setDeviceSpeed() Failed. Not a block device");
800  return;
801  }
802 
803  if (speed < 0)
804  speed = -1;
805 
806  switch(speed)
807  {
808  case 0: // don't touch speed setting
809  close(fd);
810  return;
811  case -1: // restore default value
812  {
813  rate = 0;
814  buffer[0] = 4;
815  LOG(VB_MEDIA, LOG_INFO, LOC +
816  ":setDeviceSpeed() - Restored CD/DVD Speed");
817  break;
818  }
819  default:
820  {
821  // Speed in Kilobyte/Second. 177KB/s is the maximum data rate
822  // for standard Audio CD's.
823 
824  rate = (speed > 0 && speed < 100) ? speed * 177 : speed;
825 
826  LOG(VB_MEDIA, LOG_INFO, LOC +
827  QString(":setDeviceSpeed() - Limiting CD/DVD Speed to %1KB/s")
828  .arg(rate));
829  break;
830  }
831  }
832 
833  sghdr.interface_id = 'S';
834  sghdr.timeout = 5000;
835  sghdr.dxfer_direction = SG_DXFER_TO_DEV;
836  sghdr.mx_sb_len = sizeof(sense);
837  sghdr.dxfer_len = sizeof(buffer);
838  sghdr.cmd_len = sizeof(cmd);
839  sghdr.sbp = sense;
840  sghdr.dxferp = buffer;
841  sghdr.cmdp = cmd;
842 
843  cmd[0] = GPCMD_SET_STREAMING;
844  cmd[10] = sizeof(buffer);
845 
846  buffer[8] = 0xff;
847  buffer[9] = 0xff;
848  buffer[10] = 0xff;
849  buffer[11] = 0xff;
850 
851  buffer[12] = buffer[20] = (rate >> 24) & 0xff;
852  buffer[13] = buffer[21] = (rate >> 16) & 0xff;
853  buffer[14] = buffer[22] = (rate >> 8) & 0xff;
854  buffer[15] = buffer[23] = rate & 0xff;
855 
856  // Note: 0x3e8 == 1000, hence speed = data amount per 1000 milliseconds.
857  buffer[18] = buffer[26] = 0x03;
858  buffer[19] = buffer[27] = 0xe8;
859 
860  if (ioctl(fd, SG_IO, &sghdr) < 0)
861  {
862  LOG(VB_MEDIA, LOG_ERR, LOC + " Limit CD/DVD Speed Failed" + ENO);
863  }
864  else
865  {
866  // On my system (2.6.18+ide-cd), SG_IO succeeds without doing anything,
867  // while CDROM_SELECT_SPEED works...
868  if (ioctl(fd, CDROM_SELECT_SPEED, speed) < 0)
869  {
870  LOG(VB_MEDIA, LOG_ERR, LOC +
871  " Limit CD/DVD CDROM_SELECT_SPEED Failed" + ENO);
872  }
873  LOG(VB_MEDIA, LOG_INFO, LOC +
874  ":setDeviceSpeed() - CD/DVD Speed Set Successful");
875  }
876 
877  close(fd);
878 }
879 #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?