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