Reworked the unit attention workaround, fixes #4770.

This commit is contained in:
OBattler
2024-08-30 00:57:18 +02:00
parent dd2bbf3cd3
commit 252c3034af

View File

@@ -1182,7 +1182,7 @@ scsi_cdrom_cmd_error(scsi_cdrom_t *dev)
{ {
scsi_cdrom_set_phase(dev, SCSI_PHASE_STATUS); scsi_cdrom_set_phase(dev, SCSI_PHASE_STATUS);
dev->tf->error = ((scsi_cdrom_sense_key & 0xf) << 4) | ABRT_ERR; dev->tf->error = ((scsi_cdrom_sense_key & 0xf) << 4) | ABRT_ERR;
if (dev->unit_attention) if (dev->unit_attention > 2)
dev->tf->error |= MCR_ERR; dev->tf->error |= MCR_ERR;
dev->tf->status = READY_STAT | ERR_STAT; dev->tf->status = READY_STAT | ERR_STAT;
dev->tf->phase = 3; dev->tf->phase = 3;
@@ -1199,7 +1199,7 @@ scsi_cdrom_unit_attention(scsi_cdrom_t *dev)
{ {
scsi_cdrom_set_phase(dev, SCSI_PHASE_STATUS); scsi_cdrom_set_phase(dev, SCSI_PHASE_STATUS);
dev->tf->error = (SENSE_UNIT_ATTENTION << 4) | ABRT_ERR; dev->tf->error = (SENSE_UNIT_ATTENTION << 4) | ABRT_ERR;
if (dev->unit_attention) if (dev->unit_attention > 2)
dev->tf->error |= MCR_ERR; dev->tf->error |= MCR_ERR;
dev->tf->status = READY_STAT | ERR_STAT; dev->tf->status = READY_STAT | ERR_STAT;
dev->tf->phase = 3; dev->tf->phase = 3;
@@ -1546,12 +1546,40 @@ scsi_cdrom_insert(void *priv)
if (!dev) if (!dev)
return; return;
dev->unit_attention = 0x11; dev->unit_attention = 1;
/* Turn off the medium changed status. */ /* Turn off the medium changed status. */
dev->drv->cd_status &= ~CD_STATUS_MEDIUM_CHANGED; dev->drv->cd_status &= ~CD_STATUS_MEDIUM_CHANGED;
scsi_cdrom_log("CD-ROM %i: Media insert\n", dev->id); scsi_cdrom_log("CD-ROM %i: Media insert\n", dev->id);
} }
static int
scsi_command_check_ready(scsi_cdrom_t *dev, uint8_t *cdb)
{
int ret = 0;
if (scsi_cdrom_command_flags[cdb[0]] & CHECK_READY) {
/*Note by TC1995: Some vendor commands from X vendor don't really check for ready status
but they do on Y vendor. Quite confusing I know.*/
if (scsi_cdrom_command_flags[cdb[0]] & SCSI_ONLY) switch (dev->drv->type) {
default:
ret = 1;
break;
case CDROM_TYPE_DEC_RRD45_0436:
case CDROM_TYPE_SONY_CDU541_10i:
case CDROM_TYPE_SONY_CDU561_18k:
case CDROM_TYPE_SONY_CDU76S_100:
case CDROM_TYPE_TEXEL_DMXX24_100:
if (cdb[0] == 0xC0)
break;
ret = 1;
break;
} else
ret = 1;
}
return ret;
}
static int static int
scsi_cdrom_pre_execution_check(scsi_cdrom_t *dev, uint8_t *cdb) scsi_cdrom_pre_execution_check(scsi_cdrom_t *dev, uint8_t *cdb)
{ {
@@ -1598,23 +1626,24 @@ scsi_cdrom_pre_execution_check(scsi_cdrom_t *dev, uint8_t *cdb)
ready = (dev->drv->cd_status != CD_STATUS_EMPTY) || (ext_medium_changed == -1); ready = (dev->drv->cd_status != CD_STATUS_EMPTY) || (ext_medium_changed == -1);
/* Transition, pretend we're not ready for one check so that
Windows XP can recognize a medium change. */
if ((dev->unit_attention & 0xf0) == 0x10) {
ready = 0;
goto skip_ua_check;
}
skip_ready_check: skip_ready_check:
/* If the drive is not ready, there is no reason to keep the /* If the drive is not ready, there is no reason to keep the
UNIT ATTENTION condition present, as we only use it to mark UNIT ATTENTION condition present, as we only use it to mark
disc changes. */ disc changes. */
if (!ready && dev->unit_attention) if (!ready && (dev->unit_attention > 2))
dev->unit_attention = 0; dev->unit_attention = 0;
/* If the UNIT ATTENTION condition is set and the command does not allow /* If the UNIT ATTENTION condition is set and the command does not allow
execution under it, error out and report the condition. */ execution under it, error out and report the condition. */
if (dev->unit_attention == 1) { if ((dev->unit_attention > 0) && (dev->unit_attention < 3)) {
dev->media_status = MEC_MEDIA_REMOVAL;
if (scsi_command_check_ready(dev, cdb)) {
dev->unit_attention++;
scsi_cdrom_log("CD-ROM %i: Simulated not ready phase (%02X)\n", dev->id, cdb[0]);
scsi_cdrom_not_ready(dev);
return 0;
}
} else if (dev->unit_attention == 3) {
/* Only increment the unit attention phase if the command can not pass through it. */ /* Only increment the unit attention phase if the command can not pass through it. */
if (!(scsi_cdrom_command_flags[cdb[0]] & ALLOW_UA)) { if (!(scsi_cdrom_command_flags[cdb[0]] & ALLOW_UA)) {
/* scsi_cdrom_log("CD-ROM %i: Unit attention now 2\n", dev->id); */ /* scsi_cdrom_log("CD-ROM %i: Unit attention now 2\n", dev->id); */
@@ -1624,53 +1653,31 @@ skip_ready_check:
scsi_cdrom_unit_attention(dev); scsi_cdrom_unit_attention(dev);
return 0; return 0;
} }
} else if (dev->unit_attention == 2) { } else if (dev->unit_attention == 4) {
if (cdb[0] != GPCMD_REQUEST_SENSE) { if (cdb[0] != GPCMD_REQUEST_SENSE) {
/* scsi_cdrom_log("CD-ROM %i: Unit attention now 0\n", dev->id); */ /* scsi_cdrom_log("CD-ROM %i: Unit attention now 0\n", dev->id); */
dev->unit_attention = 0; dev->unit_attention = 0;
} }
} }
skip_ua_check:
/* Unless the command is REQUEST SENSE, clear the sense. This will *NOT* /* Unless the command is REQUEST SENSE, clear the sense. This will *NOT*
the UNIT ATTENTION condition if it's set. */ the UNIT ATTENTION condition if it's set. */
if (cdb[0] != GPCMD_REQUEST_SENSE) if (cdb[0] != GPCMD_REQUEST_SENSE)
scsi_cdrom_sense_clear(dev, cdb[0]); scsi_cdrom_sense_clear(dev, cdb[0]);
/* Next it's time for NOT READY. */ /* Next it's time for NOT READY. */
if (!ready) if (!ready || ((dev->unit_attention > 0) && (dev->unit_attention < 3)))
dev->media_status = MEC_MEDIA_REMOVAL; dev->media_status = MEC_MEDIA_REMOVAL;
else else
dev->media_status = (dev->unit_attention) ? MEC_NEW_MEDIA : MEC_NO_CHANGE; dev->media_status = (dev->unit_attention > 2) ? MEC_NEW_MEDIA : MEC_NO_CHANGE;
if ((scsi_cdrom_command_flags[cdb[0]] & CHECK_READY) && !ready) { if (!ready && scsi_command_check_ready(dev, cdb)) {
if (scsi_cdrom_command_flags[cdb[0]] & SCSI_ONLY) { /*Note by TC1995: Some vendor commands from X vendor don't really check for ready status scsi_cdrom_log("CD-ROM %i: Not ready (%02X)\n", dev->id, cdb[0]);
but they do on Y vendor. Quite confusing I know.*/ scsi_cdrom_not_ready(dev);
switch (dev->drv->type) { return 0;
case CDROM_TYPE_DEC_RRD45_0436:
case CDROM_TYPE_SONY_CDU541_10i:
case CDROM_TYPE_SONY_CDU561_18k:
case CDROM_TYPE_SONY_CDU76S_100:
case CDROM_TYPE_TEXEL_DMXX24_100:
if (cdb[0] == 0xC0)
break;
scsi_cdrom_log("CD-ROM %i: Not ready (%02X)\n", dev->id, cdb[0]);
scsi_cdrom_not_ready(dev);
return 0;
default:
scsi_cdrom_log("CD-ROM %i: Not ready (%02X)\n", dev->id, cdb[0]);
scsi_cdrom_not_ready(dev);
return 0;
}
} else {
scsi_cdrom_log("CD-ROM %i: Not ready (%02X)\n", dev->id, cdb[0]);
scsi_cdrom_not_ready(dev);
return 0;
}
} }
scsi_cdrom_log("CD-ROM %i: Continuing with command %02X\n", dev->id, cdb[0]); scsi_cdrom_log("CD-ROM %i: Continuing with command %02X\n", dev->id, cdb[0]);
return 1; return 1;
} }
@@ -1719,7 +1726,7 @@ scsi_cdrom_request_sense(scsi_cdrom_t *dev, uint8_t *buffer, uint8_t alloc_lengt
buffer[2] = SENSE_ILLEGAL_REQUEST; buffer[2] = SENSE_ILLEGAL_REQUEST;
buffer[12] = ASC_AUDIO_PLAY_OPERATION; buffer[12] = ASC_AUDIO_PLAY_OPERATION;
buffer[13] = (dev->drv->cd_status == CD_STATUS_PLAYING) ? ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS : ASCQ_AUDIO_PLAY_OPERATION_PAUSED; buffer[13] = (dev->drv->cd_status == CD_STATUS_PLAYING) ? ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS : ASCQ_AUDIO_PLAY_OPERATION_PAUSED;
} else if (dev->unit_attention && ((dev->unit_attention & 0xf0) != 0x10) && } else if ((dev->unit_attention > 2) &&
((scsi_cdrom_sense_key == 0) || (scsi_cdrom_sense_key == 2))) { ((scsi_cdrom_sense_key == 0) || (scsi_cdrom_sense_key == 2))) {
buffer[2] = SENSE_UNIT_ATTENTION; buffer[2] = SENSE_UNIT_ATTENTION;
buffer[12] = ASC_MEDIUM_MAY_HAVE_CHANGED; buffer[12] = ASC_MEDIUM_MAY_HAVE_CHANGED;
@@ -1728,9 +1735,6 @@ scsi_cdrom_request_sense(scsi_cdrom_t *dev, uint8_t *buffer, uint8_t alloc_lengt
scsi_cdrom_log("CD-ROM %i: Reporting sense: %02X %02X %02X\n", dev->id, buffer[2], buffer[12], buffer[13]); scsi_cdrom_log("CD-ROM %i: Reporting sense: %02X %02X %02X\n", dev->id, buffer[2], buffer[12], buffer[13]);
if ((dev->unit_attention & 0xf0) == 0x10)
dev->unit_attention &= 0x0f;
if (buffer[2] == SENSE_UNIT_ATTENTION) { if (buffer[2] == SENSE_UNIT_ATTENTION) {
/* If the last remaining sense is unit attention, clear /* If the last remaining sense is unit attention, clear
that condition. */ that condition. */
@@ -1746,7 +1750,7 @@ scsi_cdrom_request_sense_for_scsi(scsi_common_t *sc, uint8_t *buffer, uint8_t al
if (dev->drv->cd_status & CD_STATUS_MEDIUM_CHANGED) if (dev->drv->cd_status & CD_STATUS_MEDIUM_CHANGED)
scsi_cdrom_insert((void *) dev); scsi_cdrom_insert((void *) dev);
if ((dev->drv->cd_status == CD_STATUS_EMPTY) && dev->unit_attention) { if ((dev->drv->cd_status == CD_STATUS_EMPTY) && (dev->unit_attention > 2)) {
/* If the drive is not ready, there is no reason to keep the /* If the drive is not ready, there is no reason to keep the
UNIT ATTENTION condition present, as we only use it to mark UNIT ATTENTION condition present, as we only use it to mark
disc changes. */ disc changes. */