5#include <linux/cdrom.h>
7#include <linux/iso_fs.h>
24#define LOC QString("MythCDROMLinux:")
27#ifndef ASSUME_WANT_AUDIO
28#define ASSUME_WANT_AUDIO 1
48#if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
58#if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
78#if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
93#if (Q_BYTE_ORDER == Q_BIG_ENDIAN)
139 MythCDROM(par, DevicePath, SuperMount, AllowEject) {
163 bool SuperMount,
bool AllowEject)
165 return new MythCDROMLinux(par, devicePath, SuperMount, AllowEject);
179 int drive_status = ioctl(
m_deviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT);
181 if (drive_status == -1)
183 LOG(VB_MEDIA, LOG_ERR,
LOC +
":driveStatus() - ioctl failed: " +
ENO);
187 if (drive_status == CDS_TRAY_OPEN &&
m_devicePath.contains(
"/dev/scd"))
201 cgc.cmd[0] = GPCMD_READ_DISC_INFO;
204 cgc.buffer =
reinterpret_cast<uint8_t*
>(&di);
206 cgc.data_direction = CGC_DATA_READ;
210 LOG(VB_MEDIA, LOG_ERR,
LOC +
211 ":hasWritableMedia() - failed to send packet to " +
m_devicePath +
ENO);
215 switch (di.m_discStatus)
225 return di.m_erasable;
247 cgc.cmd[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
252 cgc.buffer =
reinterpret_cast<uint8_t*
>(&es);
254 cgc.data_direction = CGC_DATA_READ;
258 || (es.m_notificationClass != 0x4))
260 LOG(VB_MEDIA, LOG_ERR,
LOC +
261 ":SCSIstatus() - failed to send SCSI packet to " +
m_devicePath +
ENO);
262 return CDS_TRAY_OPEN;
265 if (es.m_mediaPresent)
267 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
268 ":SCSIstatus() - ioctl said tray was open, "
269 "but drive is actually closed with a disc");
274 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
275 ":SCSIstatus() - tray is definitely open");
276 return CDS_TRAY_OPEN;
279 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":SCSIstatus() - ioctl said tray was open, "
280 "but drive is actually closed with no disc");
304 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":eject - Ejecting CDROM");
308 LOG(VB_MEDIA, LOG_DEBUG,
"CDROMEJECT ioctl failed" +
ENO);
313 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":eject - Loading CDROM");
318 LOG(VB_MEDIA, LOG_DEBUG,
"CDROMCLOSETRAY ioctl failed" +
ENO);
331 operator int()
const {
return m_fd; }
339 std::array<uint8_t,6> allowRmBlk {ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0};
340 std::array<uint8_t,6> startStop1Blk {START_STOP, 0, 0, 0, 1, 0};
341 std::array<uint8_t,6> startStop2Blk {START_STOP, 0, 0, 0, 2, 0};
342 std::array<uint8_t,16> sense_buffer {};
343 const unsigned DID_OK = 0;
344 const unsigned DRIVER_OK = 0;
349 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":ejectSCSI");
350 if ((ioctl(fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
353 LOG(VB_MEDIA, LOG_DEBUG,
"SG_GET_VERSION_NUM ioctl failed" +
ENO);
357 memset(&io_hdr, 0,
sizeof(sg_io_hdr_t));
358 io_hdr.interface_id =
'S';
360 io_hdr.mx_sb_len = sense_buffer.size();
361 io_hdr.dxfer_direction = SG_DXFER_NONE;
362 io_hdr.sbp = sense_buffer.data();
363 io_hdr.timeout = 10000;
365 io_hdr.cmdp = allowRmBlk.data();
366 if (ioctl(fd, SG_IO, &io_hdr) < 0)
368 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO allowRmBlk ioctl failed" +
ENO);
371 if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
373 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO allowRmBlk failed");
377 io_hdr.cmdp = startStop1Blk.data();
378 if (ioctl(fd, SG_IO, &io_hdr) < 0)
380 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO START_STOP(start) ioctl failed" +
ENO);
383 if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
385 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO START_STOP(start) failed");
389 io_hdr.cmdp = startStop2Blk.data();
390 if (ioctl(fd, SG_IO, &io_hdr) < 0)
392 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO START_STOP(eject) ioctl failed" +
ENO);
395 if (io_hdr.host_status != DID_OK || io_hdr.driver_status != DRIVER_OK)
397 LOG(VB_MEDIA, LOG_DEBUG,
"SG_IO START_STOP(eject) failed");
402 (void)ioctl(fd, BLKRRPART);
409 return (ioctl(
m_deviceHandle, CDROM_MEDIA_CHANGED, CDSL_CURRENT) > 0);
414 return (ioctl(
m_deviceHandle, CDROM_DRIVE_STATUS, CDSL_CURRENT) ==
421 bool OpenedHere =
false;
426 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":testMedia - failed to open '" +
432 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":testMedia - Opened device");
446 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
447 ":testMedia - Failed to get drive status of '" +
m_devicePath +
457 bool OpenedHere =
false;
467 LOG(VB_MEDIA, LOG_ERR,
LOC +
468 ":checkMedia() - cannot open device '" +
m_devicePath +
"' : " +
469 ENO +
"- returning UNKNOWN");
497 case CDS_DRIVE_NOT_READY:
499 " No info or drive not ready");
503 LOG(VB_GENERAL, LOG_ERR,
"Failed to get drive status of " +
522 LOG(VB_MEDIA, LOG_DEBUG,
"Disc useable, media unchanged. All good!");
540 LOG(VB_MEDIA, LOG_DEBUG,
"Disc is unmountable?");
558 LOG(VB_MEDIA, LOG_INFO,
"Found a data disk");
562 (off_t) 2048*16, SEEK_SET);
564 struct iso_primary_descriptor buf {};
566 while ((sr != (off_t) -1) && (readin < 2048))
570 if ((rr < 0) && ((EAGAIN == errno) || (EINTR == errno)))
579 m_volumeID = QString(buf.volume_id).trimmed();
582 QString(
reinterpret_cast<char*
>(buf.creation_date)).left(16));
590 LOG(VB_MEDIA, LOG_INFO,
632 LOG(VB_MEDIA, LOG_DEBUG,
"found an audio disk");
638 LOG(VB_MEDIA, LOG_DEBUG,
"found a mixed CD");
642 #if ASSUME_WANT_AUDIO
667 LOG(VB_MEDIA, LOG_DEBUG,
"found a blank or writable disk");
671 LOG(VB_MEDIA, LOG_DEBUG,
"found no disk");
676 LOG(VB_MEDIA, LOG_DEBUG,
"found unknown disk type: " +
677 QString::number(
type));
691 LOG(VB_MEDIA, LOG_DEBUG, QString(
"Returning %1")
701 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":lock - Locking CDROM door");
705 LOG(VB_MEDIA, LOG_WARNING,
"lock() - CDROM_LOCKDOOR ioctl failed" +
ENO);
715 LOG(VB_MEDIA, LOG_DEBUG,
LOC +
":unlock - Unlocking CDROM door");
719 LOG(VB_MEDIA, LOG_WARNING,
"unlock() - CDROM_LOCKDOOR ioctl failed" +
ENO);
723 LOG(VB_GENERAL, LOG_INFO,
"Failed to open device, CDROM tray will "
734 if (stat(path.toLocal8Bit().constData(), &sb) < 0)
736 LOG(VB_GENERAL, LOG_ERR,
LOC +
":isSameDevice() -- " +
737 QString(
"Failed to stat '%1'").arg(path) +
ENO);
740 dev_t new_rdev = sb.st_rdev;
743 if (stat(
m_devicePath.toLocal8Bit().constData(), &sb) < 0)
745 LOG(VB_GENERAL, LOG_ERR,
LOC +
":isSameDevice() -- " +
749 return (sb.st_rdev == new_rdev);
752#if defined(SG_IO) && defined(GPCMD_SET_STREAMING)
758 std::array<uint8_t,28> buffer {};
759 std::array<uint8_t,16> cmd {};
760 std::array<uint8_t,16> sense {};
761 struct sg_io_hdr sghdr {};
768 LOG(VB_MEDIA, LOG_ERR,
LOC +
769 " Changing CD/DVD speed needs write access");
773 if (fstat(fd, &st) == -1)
776 LOG(VB_MEDIA, LOG_ERR,
LOC +
777 QString(
":setDeviceSpeed() Failed. device %1 not found")
782 if (!S_ISBLK(st.st_mode))
785 LOG(VB_MEDIA, LOG_ERR,
LOC +
786 ":setDeviceSpeed() Failed. Not a block device");
802 LOG(VB_MEDIA, LOG_INFO,
LOC +
803 ":setDeviceSpeed() - Restored CD/DVD Speed");
811 rate = (speed > 0 && speed < 100) ? speed * 177 : speed;
813 LOG(VB_MEDIA, LOG_INFO,
LOC +
814 QString(
":setDeviceSpeed() - Limiting CD/DVD Speed to %1KB/s")
820 sghdr.interface_id =
'S';
821 sghdr.timeout = 5000;
822 sghdr.dxfer_direction = SG_DXFER_TO_DEV;
823 sghdr.mx_sb_len = sense.size();
824 sghdr.dxfer_len = buffer.size();
825 sghdr.cmd_len = cmd.size();
826 sghdr.sbp = sense.data();
827 sghdr.dxferp = buffer.data();
828 sghdr.cmdp = cmd.data();
830 cmd[0] = GPCMD_SET_STREAMING;
831 cmd[10] = buffer.size();
838 buffer[12] = buffer[20] = (rate >> 24) & 0xff;
839 buffer[13] = buffer[21] = (rate >> 16) & 0xff;
840 buffer[14] = buffer[22] = (rate >> 8) & 0xff;
841 buffer[15] = buffer[23] = rate & 0xff;
844 buffer[18] = buffer[26] = 0x03;
845 buffer[19] = buffer[27] = 0xe8;
847 if (ioctl(fd, SG_IO, &sghdr) < 0)
849 LOG(VB_MEDIA, LOG_ERR,
LOC +
" Limit CD/DVD Speed Failed" +
ENO);
855 if (ioctl(fd, CDROM_SELECT_SPEED, speed) < 0)
857 LOG(VB_MEDIA, LOG_ERR,
LOC +
858 " Limit CD/DVD CDROM_SELECT_SPEED Failed" +
ENO);
860 LOG(VB_MEDIA, LOG_INFO,
LOC +
861 ":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)