CD-ROM: IOCTL now detects medium changes without having to read the host drive's TOC on every operation, improves performance.

This commit is contained in:
OBattler
2025-02-03 10:57:19 +01:00
parent a8918de768
commit 35a12e0826
9 changed files with 340 additions and 192 deletions

View File

@@ -40,7 +40,6 @@
typedef struct ioctl_t {
cdrom_t *dev;
void *log;
int toc_valid;
void *handle;
char path[256];
} ioctl_t;
@@ -77,8 +76,6 @@ ioctl_open_handle(UNUSED(ioctl_t *ioctl))
static void
ioctl_read_toc(ioctl_t *ioctl)
{
if (!ioctl->toc_valid)
ioctl->toc_valid = 1;
}
/* Shared functions. */
@@ -159,6 +156,12 @@ ioctl_has_audio(UNUSED(const void *local))
return 0;
}
static int
ioctl_is_empty(const void *local)
{
return 1;
}
static int
ioctl_ext_medium_changed(UNUSED(void *local))
{
@@ -186,6 +189,18 @@ ioctl_close(void *local)
ioctl->log = NULL;
}
static void
ioctl_load(const void *local)
{
const ioctl_t *ioctl = (const ioctl_t *) local;
if (ioctl_open_handle((ioctl_t *) ioctl)) {
ioctl_close_handle((ioctl_t *) ioctl);
ioctl_read_toc((ioctl_t *) ioctl);
}
}
static const cdrom_ops_t ioctl_ops = {
ioctl_get_track_info,
ioctl_get_raw_track_info,
@@ -196,8 +211,9 @@ static const cdrom_ops_t ioctl_ops = {
ioctl_read_dvd_structure,
ioctl_is_dvd,
ioctl_has_audio,
ioctl_ext_medium_changed,
ioctl_close
ioctl_is_empty,
ioctl_close,
ioctl_load
};
/* Public functions. */
@@ -218,7 +234,6 @@ ioctl_open(cdrom_t *dev, const char *drv)
ioctl_log(ioctl->log, "Path is %s\n", ioctl->path);
ioctl->dev = dev;
ioctl->toc_valid = 0;
dev->ops = &ioctl_ops;
}

View File

@@ -51,7 +51,10 @@
#include <86box/mouse.h>
#include <86box/plat.h>
#include <86box/86box.h>
#include <86box/cdrom.h>
#include <86box/video.h>
#include <dbt.h>
#include <strsafe.h>
extern void win_keyboard_handle(uint32_t scancode, int up, int e0, int e1);
@@ -151,72 +154,142 @@ WindowsRawInputFilter::~WindowsRawInputFilter()
RegisterRawInputDevices(rid, 2, sizeof(rid[0]));
}
static void
notify_drives(ULONG unitmask, int empty)
{
char p[1024] = { 0 };
for (int i = 0; i < 26; ++i) {
if (unitmask & 0x1) {
cdrom_t *dev = NULL;
sprintf(p, "ioctl://\\\\.\\%c:", 'A' + i);
for (int i = 0; i < CDROM_NUM; i++)
if (!stricmp(cdrom[i].image_path, p)) {
dev = &(cdrom[i]);
if (empty)
cdrom_set_empty(dev);
else
cdrom_update_status(dev);
// pclog("CD-ROM %i : Drive notified of media %s\n",
// dev->id, empty ? "removal" : "change");
}
}
unitmask = unitmask >> 1;
}
}
static void
device_change(WPARAM wParam, LPARAM lParam)
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR) lParam;
switch(wParam) {
case DBT_DEVICEARRIVAL:
case DBT_DEVICEREMOVECOMPLETE:
/* Check whether a CD or DVD was inserted into a drive. */
if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME) {
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME) lpdb;
if (lpdbv->dbcv_flags & DBTF_MEDIA)
notify_drives(lpdbv->dbcv_unitmask,
(wParam == DBT_DEVICEREMOVECOMPLETE));
}
break;
default:
/*
Process other WM_DEVICECHANGE notifications for other
devices or reasons.
*/
break;
}
}
bool
WindowsRawInputFilter::nativeEventFilter(const QByteArray &eventType, void *message, result_t *result)
{
if (eventType == "windows_generic_MSG") {
MSG *msg = static_cast<MSG *>(message);
if (msg->message == WM_INPUT) {
if (window->isActiveWindow() && menus_open == 0)
handle_input((HRAWINPUT) msg->lParam);
else
{
for (auto &w : window->renderers) {
if (w && w->isActiveWindow()) {
handle_input((HRAWINPUT) msg->lParam);
break;
if (msg != nullptr) switch(msg->message) {
case WM_INPUT:
if (window->isActiveWindow() && (menus_open == 0))
handle_input((HRAWINPUT) msg->lParam);
else {
for (auto &w : window->renderers) {
if (w && w->isActiveWindow()) {
handle_input((HRAWINPUT) msg->lParam);
break;
}
}
}
}
return true;
} else if ((msg != nullptr) && (msg->message == WM_SETTINGCHANGE) &&
(((void *) msg->lParam) != nullptr) &&
(wcscmp(L"ImmersiveColorSet", (wchar_t*)msg->lParam) == 0)) {
if (!windows_is_light_theme()) {
QFile f(":qdarkstyle/dark/darkstyle.qss");
if (!f.exists()) {
printf("Unable to set stylesheet, file not found\n");
} else {
f.open(QFile::ReadOnly | QFile::Text);
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
}
QTimer::singleShot(1000, [this] () {
BOOL DarkMode = TRUE;
DwmSetWindowAttribute((HWND)window->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&DarkMode, sizeof(DarkMode));
window->ui->stackedWidget->switchRenderer((RendererStack::Renderer) vid_api);
for (int i = 1; i < MONITORS_NUM; i++) {
if (window->renderers[i] && !window->renderers[i]->isHidden())
window->renderers[i]->switchRenderer((RendererStack::Renderer) vid_api);
}
});
} else {
qApp->setStyleSheet("");
QTimer::singleShot(1000, [this] () {
BOOL DarkMode = FALSE;
DwmSetWindowAttribute((HWND)window->winId(), DWMWA_USE_IMMERSIVE_DARK_MODE, (LPCVOID)&DarkMode, sizeof(DarkMode));
});
}
QTimer::singleShot(1000, [this] () {
window->resizeContents(monitors[0].mon_scrnsz_x, monitors[0].mon_scrnsz_y);
for (int i = 1; i < MONITORS_NUM; i++) {
if (window->renderers[i] && !window->renderers[i]->isHidden()) {
window->resizeContentsMonitor(monitors[i].mon_scrnsz_x, monitors[i].mon_scrnsz_y, i);
}
}
});
}
/* Stop processing of Alt-F4 */
if (msg->message == WM_SYSKEYDOWN) {
if (msg->wParam == 0x73) {
return true;
}
case WM_SETTINGCHANGE:
if ((((void *) msg->lParam) != nullptr) &&
(wcscmp(L"ImmersiveColorSet", (wchar_t*)msg->lParam) == 0)) {
if (!windows_is_light_theme()) {
QFile f(":qdarkstyle/dark/darkstyle.qss");
if (!f.exists())
printf("Unable to set stylesheet, file not found\n");
else {
f.open(QFile::ReadOnly | QFile::Text);
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
}
QTimer::singleShot(1000, [this] () {
BOOL DarkMode = TRUE;
auto vid_stack = (RendererStack::Renderer) vid_api;
DwmSetWindowAttribute((HWND) window->winId(),
DWMWA_USE_IMMERSIVE_DARK_MODE,
(LPCVOID) &DarkMode,
sizeof(DarkMode));
window->ui->stackedWidget->switchRenderer(vid_stack);
for (int i = 1; i < MONITORS_NUM; i++) {
if ((window->renderers[i] != nullptr) &&
!window->renderers[i]->isHidden())
window->renderers[i]->switchRenderer(vid_stack);
}
});
} else {
qApp->setStyleSheet("");
QTimer::singleShot(1000, [this] () {
BOOL DarkMode = FALSE;
DwmSetWindowAttribute((HWND) window->winId(),
DWMWA_USE_IMMERSIVE_DARK_MODE,
(LPCVOID) &DarkMode,
sizeof(DarkMode));
});
}
QTimer::singleShot(1000, [this] () {
window->resizeContents(monitors[0].mon_scrnsz_x,
monitors[0].mon_scrnsz_y);
for (int i = 1; i < MONITORS_NUM; i++) {
auto mon = &(monitors[i]);
if ((window->renderers[i] != nullptr) &&
!window->renderers[i]->isHidden())
window->resizeContentsMonitor(mon->mon_scrnsz_x,
mon->mon_scrnsz_y,
i);
}
});
}
break;
case WM_SYSKEYDOWN:
/* Stop processing of Alt-F4 */
if (msg->wParam == 0x73)
return true;
break;
case WM_DEVICECHANGE:
if (msg->hwnd == (HWND) window->winId())
device_change(msg->wParam, msg->lParam);
break;
}
}

View File

@@ -32,9 +32,11 @@
#include <stdlib.h>
#include <stddef.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/cdrom.h>
#include <86box/log.h>
#include <86box/plat_cdrom_ioctl.h>
#include <86box/scsi_device.h>
typedef struct ioctl_t {
cdrom_t *dev;
@@ -42,7 +44,6 @@ typedef struct ioctl_t {
int is_dvd;
int has_audio;
int32_t tracks_num;
int toc_valid;
uint8_t cur_toc[65536];
CDROM_READ_TOC_EX cur_read_toc_ex;
int blocks_num;
@@ -51,9 +52,8 @@ typedef struct ioctl_t {
WCHAR path[256];
} ioctl_t;
static void ioctl_read_toc(ioctl_t *ioctl);
static int ioctl_read_dvd_structure(const void *local, uint8_t layer, uint8_t format,
uint8_t *buffer, uint32_t *info);
static int ioctl_read_dvd_structure(const void *local, uint8_t layer, uint8_t format,
uint8_t *buffer, uint32_t *info);
#ifdef ENABLE_IOCTL_LOG
int ioctl_do_log = ENABLE_IOCTL_LOG;
@@ -94,25 +94,6 @@ ioctl_open_handle(ioctl_t *ioctl)
return (ioctl->handle != INVALID_HANDLE_VALUE);
}
static int
ioctl_load(ioctl_t *ioctl)
{
int ret = 0;
if (ioctl_open_handle(ioctl)) {
long size;
DeviceIoControl(ioctl->handle, IOCTL_STORAGE_LOAD_MEDIA,
NULL, 0, NULL, 0,
(LPDWORD) &size, NULL);
ret = 1;
ioctl_close_handle(ioctl);
ioctl_read_toc(ioctl);
}
return ret;
}
static int
ioctl_read_normal_toc(ioctl_t *ioctl, uint8_t *toc_buf)
{
@@ -266,11 +247,8 @@ ioctl_read_raw_toc(ioctl_t *ioctl)
static void
ioctl_read_toc(ioctl_t *ioctl)
{
if (!ioctl->toc_valid) {
ioctl->toc_valid = 1;
(void) ioctl_read_normal_toc(ioctl, ioctl->cur_toc);
ioctl_read_raw_toc(ioctl);
}
(void) ioctl_read_normal_toc(ioctl, ioctl->cur_toc);
ioctl_read_raw_toc(ioctl);
}
static int
@@ -301,8 +279,6 @@ ioctl_is_track_audio(const ioctl_t *ioctl, const uint32_t pos)
const raw_track_info_t *rti = (const raw_track_info_t *) ioctl->cur_rti;
int ret = 0;
ioctl_read_toc((ioctl_t *) ioctl);
if (ioctl->has_audio && !ioctl->is_dvd) {
const int track = ioctl_get_track(ioctl, pos);
const int control = rti[track].adr_ctl;
@@ -324,8 +300,6 @@ ioctl_get_track_info(const void *local, const uint32_t track,
const CDROM_TOC *toc = (const CDROM_TOC *) ioctl->cur_toc;
int ret = 1;
ioctl_read_toc((ioctl_t *) ioctl);
if ((track < 1) || (track == 0xaa) || (track > (toc->LastTrack + 1))) {
ioctl_log(ioctl->log, "ioctl_get_track_info(%02i)\n", track);
ret = 0;
@@ -363,8 +337,6 @@ ioctl_is_track_pre(const void *local, const uint32_t sector)
const raw_track_info_t *rti = (const raw_track_info_t *) ioctl->cur_rti;
int ret = 0;
ioctl_read_toc((ioctl_t *) ioctl);
if (ioctl->has_audio && !ioctl->is_dvd) {
const int track = ioctl_get_track(ioctl, sector);
const int control = rti[track].adr_ctl;
@@ -572,8 +544,6 @@ ioctl_get_last_block(const void *local)
const CDROM_TOC *toc = (const CDROM_TOC *) ioctl->cur_toc;
uint32_t lb = 0;
ioctl_read_toc((ioctl_t *) ioctl);
for (int c = 0; c <= toc->LastTrack; c++) {
const TRACK_DATA *td = &toc->TrackData[c];
const uint32_t address = MSFtoLBA(td->Address[1], td->Address[2],
@@ -688,44 +658,80 @@ ioctl_has_audio(const void *local)
}
static int
ioctl_ext_medium_changed(void *local)
ioctl_is_empty(const void *local)
{
ioctl_t * ioctl = (ioctl_t *) local;
const CDROM_TOC *toc = (CDROM_TOC *) ioctl->cur_toc;
const TRACK_DATA *ltd = &toc->TrackData[toc->LastTrack];
const uint32_t old_addr = *(uint32_t *) ltd->Address;
const int temp = ioctl_read_normal_toc(ioctl, ioctl->cur_toc);
int ret = 0;
typedef struct SCSI_PASS_THROUGH_DIRECT_BUF {
SCSI_PASS_THROUGH_DIRECT spt;
ULONG Filler;
UCHAR SenseBuf[64];
} SCSI_PASS_THROUGH_DIRECT_BUF;
if (temp == 1) {
if (ioctl->toc_valid && ((*(uint32_t *) ltd->Address) != old_addr)) {
/* The TOC has changed. */
ioctl->toc_valid = 0;
ret = 1;
}
const ioctl_t * ioctl = (const ioctl_t *) local;
unsigned long int unused = 0;
SCSI_PASS_THROUGH_DIRECT_BUF req;
if (!ioctl->toc_valid) {
ioctl->toc_valid = 1;
ioctl_read_raw_toc(ioctl);
}
} else {
/* There has been some kind of error - not a medium change, but a not ready
condition. */
ret = -1;
}
ioctl_open_handle((ioctl_t *) ioctl);
if (ret == 1) {
if (ioctl->is_dvd)
ioctl->dev->cd_status = CD_STATUS_DVD;
else
ioctl->dev->cd_status = ioctl->has_audio ? CD_STATUS_STOPPED :
CD_STATUS_DATA_ONLY;
memset(&req, 0x00, sizeof(SCSI_PASS_THROUGH_DIRECT_BUF));
req.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
req.spt.PathId = 0;
req.spt.TargetId = 1;
req.spt.Lun = 0;
req.spt.CdbLength = 12;
req.spt.DataIn = SCSI_IOCTL_DATA_IN;
req.spt.SenseInfoLength = sizeof(req.SenseBuf);
req.spt.DataTransferLength = 0;
req.spt.TimeOutValue = 6;
req.spt.DataBuffer = NULL;
req.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_BUF, SenseBuf);
ioctl->dev->cdrom_capacity = ioctl_get_last_block(ioctl);
} else if (ret == -1)
ioctl->dev->cd_status = CD_STATUS_EMPTY;
/* Fill in the CDB. */
req.spt.Cdb[0] = 0x00;
req.spt.Cdb[1] = 0x00;
req.spt.Cdb[2] = 0x00;
req.spt.Cdb[3] = 0x00;
req.spt.Cdb[4] = 0x00;
req.spt.Cdb[5] = 0x00;
req.spt.Cdb[6] = 0x00;
req.spt.Cdb[7] = 0x00;
req.spt.Cdb[8] = 0x00;
req.spt.Cdb[9] = 0x00;
req.spt.Cdb[10] = 0x00;
req.spt.Cdb[11] = 0x00;
ioctl_log(ioctl->log, "ioctl_ext_medium_changed(): %i\n", ret);
DWORD length = sizeof(SCSI_PASS_THROUGH_DIRECT_BUF);
#ifdef ENABLE_IOCTL_LOG
uint8_t *cdb = (uint8_t *) req.spt.Cdb;
ioctl_log(ioctl->log, "Host CDB: %02X %02X %02X %02X %02X %02X "
"%02X %02X %02X %02X %02X %02X\n",
cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5],
cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11]);
#endif
int ret = DeviceIoControl(ioctl->handle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&req, length,
&req, length,
&unused, NULL);
ioctl_log(ioctl->log, "ioctl_read_dvd_structure(): ret = %d, "
"req.spt.DataTransferLength = %lu\n",
ret, req.spt.DataTransferLength);
ioctl_log(ioctl->log, "Sense: %08X, %08X\n", req.spt.SenseInfoLength,
req.spt.SenseInfoOffset);
if (req.spt.SenseInfoLength >= 16) {
uint8_t *sb = (uint8_t *) req.SenseBuf;
/* Return sense to the host as is. */
ret = ((sb[2] == SENSE_NOT_READY) && (sb[12] == ASC_MEDIUM_NOT_PRESENT));
ioctl_log(ioctl->log, "Host sense: %02X %02X %02X %02X %02X %02X %02X %02X\n",
sb[0], sb[1], sb[ 2], sb[ 3], sb[ 4], sb[ 5], sb[ 6], sb[ 7]);
ioctl_log(ioctl->log, " %02X %02X %02X %02X %02X %02X %02X %02X\n",
sb[8], sb[9], sb[10], sb[11], sb[12], sb[13], sb[14], sb[15]);
} else
ret = 0;
ioctl_close_handle((ioctl_t *) ioctl);
return ret;
}
@@ -744,6 +750,22 @@ ioctl_close(void *local)
ioctl->log = NULL;
}
static void
ioctl_load(const void *local)
{
const ioctl_t *ioctl = (const ioctl_t *) local;
if (ioctl_open_handle((ioctl_t *) ioctl)) {
long size;
DeviceIoControl(ioctl->handle, IOCTL_STORAGE_LOAD_MEDIA,
NULL, 0, NULL, 0,
(LPDWORD) &size, NULL);
ioctl_close_handle((ioctl_t *) ioctl);
ioctl_read_toc((ioctl_t *) ioctl);
}
}
static const cdrom_ops_t ioctl_ops = {
ioctl_get_track_info,
ioctl_get_raw_track_info,
@@ -754,8 +776,9 @@ static const cdrom_ops_t ioctl_ops = {
ioctl_read_dvd_structure,
ioctl_is_dvd,
ioctl_has_audio,
ioctl_ext_medium_changed,
ioctl_close
ioctl_is_empty,
ioctl_close,
ioctl_load
};
/* Public functions. */
@@ -776,7 +799,6 @@ ioctl_open(cdrom_t *dev, const char *drv)
ioctl_log(ioctl->log, "Path is %S\n", ioctl->path);
ioctl->dev = dev;
ioctl->toc_valid = 0;
dev->ops = &ioctl_ops;