5#include <linux/cdrom.h>
7#include <linux/iso_fs.h>
23#define LOC QString("MythCDROMLinux:")
26#ifndef ASSUME_WANT_AUDIO
27#define ASSUME_WANT_AUDIO 1
47#if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
57#if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
77#if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
92#if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
138 MythCDROM(par, DevicePath, SuperMount, AllowEject) {
162 bool SuperMount,
bool AllowEject)
164 return new MythCDROMLinux(par, devicePath, SuperMount, AllowEject);
178 int drive_status = ioctl(
m_deviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT);
180 if (drive_status == -1)
182 LOG(VB_MEDIA, LOG_ERR,
LOC +
":driveStatus() - ioctl failed: " +
ENO);
186 if (drive_status == CDS_TRAY_OPEN &&
m_devicePath.contains(
"/dev/scd"))
200 cgc.cmd[0] = GPCMD_READ_DISC_INFO;
203 cgc.buffer =
reinterpret_cast<uint8_t*
>(&di);
205 cgc.data_direction = CGC_DATA_READ;
209 LOG(VB_MEDIA, LOG_ERR,
LOC +
210 ":hasWritableMedia() - failed to send packet to " +
m_devicePath +
ENO);
214 switch (di.m_discStatus)
224 return di.m_erasable;
246 cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
251 cgc.buffer =
reinterpret_cast<uint8_t*
>(&es);
253 cgc.data_direction = CGC_DATA_READ;
257 || (es.m_notificationClass != 0x4))
259 LOG(VB_MEDIA, LOG_ERR,
LOC +
260 ":SCSIstatus() - failed to send SCSI packet to " +
m_devicePath +
ENO);
261 return CDS_TRAY_OPEN;
264 if (es.m_mediaPresent)
266 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
267 ":SCSIstatus() - ioctl said tray was open, "
268 "but drive is actually closed with a disc");
273 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
274 ":SCSIstatus() - tray is definitely open");
275 return CDS_TRAY_OPEN;
278 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":SCSIstatus() - ioctl said tray was open, "
279 "but drive is actually closed with no disc");
303 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":eject - Ejecting CDROM");
307 LOG(VB_MEDIA, LOG_DEBUG,
"CDROMEJECT ioctl failed" +
ENO);
312 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":eject - Loading CDROM");
317 LOG(VB_MEDIA, LOG_DEBUG,
"CDROMCLOSETRAY ioctl failed" +
ENO);
330 operator int()
const {
return m_fd; }
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};
340 std::array<uint8_t,6> startStop2Blk {START_STOP, 0, 0, 0, 2, 0};
341 std::array<uint8_t,16> sense_buffer {};
342 const unsigned DID_OK = 0;
343 const unsigned DRIVER_OK = 0;
348 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":ejectSCSI");
349 if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
352 LOG(VB_MEDIA, LOG_DEBUG,
"SG_GET_VERSION_NUM ioctl failed" +
ENO);
356 memset(&io_hdr, 0,
sizeof(sg_io_hdr_t));
357 io_hdr.interface_id =
'S';
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;
364 io_hdr.cmdp = allowRmBlk.data();
365 if (ioctl(fd, SG_IO, &io_hdr) < 0)
367 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO allowRmBlk ioctl failed" +
ENO);
370 if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
372 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO allowRmBlk failed");
376 io_hdr.cmdp = startStop1Blk.data();
377 if (ioctl(fd, SG_IO, &io_hdr) < 0)
379 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO START_STOP(start) ioctl failed" +
ENO);
382 if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
384 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO START_STOP(start) failed");
388 io_hdr.cmdp = startStop2Blk.data();
389 if (ioctl(fd, SG_IO, &io_hdr) < 0)
391 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO START_STOP(eject) ioctl failed" +
ENO);
394 if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
396 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO START_STOP(eject) failed");
401 (void)ioctl(fd, BLKRRPART);
408 return (ioctl(
m_deviceHandle, CDROM_MEDIA_CHANGED, CDSL_CURRENT) > 0);
413 return (ioctl(
m_deviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT) ==
420 bool OpenedHere =
false;
425 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":testMedia - failed to open '" +
431 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":testMedia - Opened device");
445 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
446 ":testMedia - Failed to get drive status of '" +
m_devicePath +
456 bool OpenedHere =
false;
466 LOG(VB_MEDIA, LOG_ERR,
LOC +
467 ":checkMedia() - cannot open device '" +
m_devicePath +
"' : " +
468 ENO +
"- returning UNKNOWN");
496 case CDS_DRIVE_NOT_READY:
498 " No info or drive not ready");
502 LOG(VB_GENERAL, LOG_ERR,
"Failed to get drive status of " +
521 LOG(VB_MEDIA, LOG_DEBUG,
"Disc useable, media unchanged. All good!");
539 LOG(VB_MEDIA, LOG_DEBUG,
"Disc is unmountable?");
557 LOG(VB_MEDIA, LOG_INFO,
"Found a data disk");
561 (
off_t) 2048*16, SEEK_SET);
563 struct iso_primary_descriptor buf {};
565 while ((sr != (
off_t) -1) && (readin < 2048))
569 if ((rr < 0) && ((EAGAIN == errno) || (EINTR == errno)))
578 m_volumeID = QString(buf.volume_id).trimmed();
581 QString(
reinterpret_cast<char*
>(buf.creation_date)).left(16));
589 LOG(VB_MEDIA, LOG_INFO,
631 LOG(VB_MEDIA, LOG_DEBUG,
"found an audio disk");
637 LOG(VB_MEDIA, LOG_DEBUG,
"found a mixed CD");
641 #if ASSUME_WANT_AUDIO
666 LOG(VB_MEDIA, LOG_DEBUG,
"found a blank or writable disk");
670 LOG(VB_MEDIA, LOG_DEBUG,
"found no disk");
675 LOG(VB_MEDIA, LOG_DEBUG,
"found unknown disk type: " +
676 QString::number(
type));
690 LOG(VB_MEDIA, LOG_DEBUG, QString(
"Returning %1")
700 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":lock - Locking CDROM door");
704 LOG(VB_MEDIA, LOG_WARNING,
"lock() - CDROM_LOCKDOOR ioctl failed" +
ENO);
714 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":unlock - Unlocking CDROM door");
718 LOG(VB_MEDIA, LOG_WARNING,
"unlock() - CDROM_LOCKDOOR ioctl failed" +
ENO);
722 LOG(VB_GENERAL, LOG_INFO,
"Failed to open device, CDROM tray will "
733 if (stat(path.toLocal8Bit().constData(), &sb) < 0)
735 LOG(VB_GENERAL, LOG_ERR,
LOC +
":isSameDevice() -- " +
736 QString(
"Failed to stat '%1'").arg(path) +
ENO);
739 dev_t new_rdev = sb.st_rdev;
742 if (stat(
m_devicePath.toLocal8Bit().constData(), &sb) < 0)
744 LOG(VB_GENERAL, LOG_ERR,
LOC +
":isSameDevice() -- " +
748 return (sb.st_rdev == new_rdev);
751#if defined(SG_IO) && defined(GPCMD_SET_STREAMING)
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 {};
767 LOG(VB_MEDIA, LOG_ERR,
LOC +
768 " Changing CD/DVD speed needs write access");
772 if (fstat(fd, &st) == -1)
775 LOG(VB_MEDIA, LOG_ERR,
LOC +
776 QString(
":setDeviceSpeed() Failed. device %1 not found")
781 if (!S_ISBLK(st.st_mode))
784 LOG(VB_MEDIA, LOG_ERR,
LOC +
785 ":setDeviceSpeed() Failed. Not a block device");
801 LOG(VB_MEDIA, LOG_INFO,
LOC +
802 ":setDeviceSpeed() - Restored CD/DVD Speed");
810 rate = (speed > 0 && speed < 100) ? speed * 177 : speed;
812 LOG(VB_MEDIA, LOG_INFO,
LOC +
813 QString(
":setDeviceSpeed() - Limiting CD/DVD Speed to %1KB/s")
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();
829 cmd[0] = GPCMD_SET_STREAMING;
830 cmd[10] = buffer.size();
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;
843 buffer[18] = buffer[26] = 0x03;
844 buffer[19] = buffer[27] = 0xe8;
846 if (ioctl(fd, SG_IO, &sghdr) < 0)
848 LOG(VB_MEDIA, LOG_ERR,
LOC +
" Limit CD/DVD Speed Failed" +
ENO);
854 if (ioctl(fd, CDROM_SELECT_SPEED, speed) < 0)
856 LOG(VB_MEDIA, LOG_ERR,
LOC +
857 " Limit CD/DVD CDROM_SELECT_SPEED Failed" +
ENO);
859 LOG(VB_MEDIA, LOG_INFO,
LOC +
860 ":setDeviceSpeed() - CD/DVD Speed Set Successful");
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)
void onDeviceMounted() override
Override this to perform any post mount logic.
cdrom_generic_command CDROMgenericCmd
MythCDROM * GetMythCDROMLinux(QObject *par, const QString &devicePath, bool SuperMount, bool AllowEject)
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QString current_iso_string(bool stripped)
Returns current Date and Time in UTC as a string.
def read(device=None, features=[])
uint16_t m_discInformationLength
uint8_t m_notificationClass
StHandle(const char *dev)