esp.c: process non-DMA FIFO writes in esp_do_nodma()
Currently any write to the ESP FIFO in the MESSAGE OUT or COMMAND phases will manually raise the bus service interrupt. Instead of duplicating the interrupt logic in esp_reg_write(), update esp_do_nodma() to correctly process incoming FIFO data during the MESSAGE OUT and COMMAND phases. Part of this change is to call esp_nodma_ti_dataout() from handle_ti() to ensure that the DATA OUT phase FIFO transfer only occurs when executing a non-DMA TI command instead of for each byte entering the FIFO. One slight complication is that NextSTEP uses multiple TI commands to transfer the CDB one byte at a time (as opposed to loading the FIFO and using a single TI command), so it is necessary to determine the expected length of the SCSI CDB being received. This is handled by the introduction of a new esp_cdb_length() function which returns the expected SCSI CDB length based upon the first command byte. Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Tested-by: Helge Deller <deller@gmx.de> Tested-by: Thomas Huth <thuth@redhat.com> Message-Id: <20240112125420.514425-67-mark.cave-ayland@ilande.co.uk> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
This commit is contained in:
parent
a1b8d38935
commit
5d02add4d7
103
hw/scsi/esp.c
103
hw/scsi/esp.c
@ -420,6 +420,7 @@ static void handle_satn_stop(ESPState *s)
|
|||||||
|
|
||||||
esp_set_phase(s, STAT_MO);
|
esp_set_phase(s, STAT_MO);
|
||||||
s->rregs[ESP_RSEQ] = SEQ_MO;
|
s->rregs[ESP_RSEQ] = SEQ_MO;
|
||||||
|
s->cmdfifo_cdb_offset = 0;
|
||||||
|
|
||||||
if (s->dma) {
|
if (s->dma) {
|
||||||
esp_do_dma(s);
|
esp_do_dma(s);
|
||||||
@ -454,6 +455,22 @@ static void write_response(ESPState *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int esp_cdb_length(ESPState *s)
|
||||||
|
{
|
||||||
|
const uint8_t *pbuf;
|
||||||
|
int cmdlen, len;
|
||||||
|
|
||||||
|
cmdlen = fifo8_num_used(&s->cmdfifo);
|
||||||
|
if (cmdlen < s->cmdfifo_cdb_offset) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pbuf = fifo8_peek_buf(&s->cmdfifo, cmdlen, NULL);
|
||||||
|
len = scsi_cdb_length((uint8_t *)&pbuf[s->cmdfifo_cdb_offset]);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
static void esp_dma_ti_check(ESPState *s)
|
static void esp_dma_ti_check(ESPState *s)
|
||||||
{
|
{
|
||||||
if (esp_get_tc(s) == 0 && fifo8_num_used(&s->fifo) < 2) {
|
if (esp_get_tc(s) == 0 && fifo8_num_used(&s->fifo) < 2) {
|
||||||
@ -738,17 +755,41 @@ static void esp_do_nodma(ESPState *s)
|
|||||||
fifo8_push_all(&s->cmdfifo, buf, n);
|
fifo8_push_all(&s->cmdfifo, buf, n);
|
||||||
s->cmdfifo_cdb_offset += n;
|
s->cmdfifo_cdb_offset += n;
|
||||||
|
|
||||||
/*
|
switch (s->rregs[ESP_CMD]) {
|
||||||
* Extra message out bytes received: update cmdfifo_cdb_offset
|
case CMD_SELATN:
|
||||||
* and then switch to command phase
|
if (fifo8_num_used(&s->cmdfifo) >= 1) {
|
||||||
*/
|
/* First byte received, switch to command phase */
|
||||||
|
esp_set_phase(s, STAT_CD);
|
||||||
|
s->cmdfifo_cdb_offset = 1;
|
||||||
|
|
||||||
|
if (fifo8_num_used(&s->cmdfifo) > 1) {
|
||||||
|
/* Process any additional command phase data */
|
||||||
|
esp_do_nodma(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_SELATNS:
|
||||||
|
if (fifo8_num_used(&s->cmdfifo) == 1) {
|
||||||
|
/* First byte received, stop in message out phase */
|
||||||
|
s->cmdfifo_cdb_offset = 1;
|
||||||
|
|
||||||
|
/* Raise command completion interrupt */
|
||||||
|
s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC;
|
||||||
|
esp_raise_irq(s);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_TI:
|
||||||
|
/* ATN remains asserted until FIFO empty */
|
||||||
s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo);
|
s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo);
|
||||||
esp_set_phase(s, STAT_CD);
|
esp_set_phase(s, STAT_CD);
|
||||||
s->rregs[ESP_CMD] = 0;
|
s->rregs[ESP_CMD] = 0;
|
||||||
s->rregs[ESP_RSEQ] = SEQ_CD;
|
|
||||||
s->rregs[ESP_RINTR] |= INTR_BS;
|
s->rregs[ESP_RINTR] |= INTR_BS;
|
||||||
esp_raise_irq(s);
|
esp_raise_irq(s);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case STAT_CD:
|
case STAT_CD:
|
||||||
/* Copy FIFO into cmdfifo */
|
/* Copy FIFO into cmdfifo */
|
||||||
@ -756,21 +797,40 @@ static void esp_do_nodma(ESPState *s)
|
|||||||
n = MIN(fifo8_num_free(&s->cmdfifo), n);
|
n = MIN(fifo8_num_free(&s->cmdfifo), n);
|
||||||
fifo8_push_all(&s->cmdfifo, buf, n);
|
fifo8_push_all(&s->cmdfifo, buf, n);
|
||||||
|
|
||||||
|
switch (s->rregs[ESP_CMD]) {
|
||||||
|
case CMD_TI:
|
||||||
cmdlen = fifo8_num_used(&s->cmdfifo);
|
cmdlen = fifo8_num_used(&s->cmdfifo);
|
||||||
trace_esp_handle_ti_cmd(cmdlen);
|
trace_esp_handle_ti_cmd(cmdlen);
|
||||||
s->ti_size = 0;
|
|
||||||
|
|
||||||
/* No command received */
|
|
||||||
if (s->cmdfifo_cdb_offset == fifo8_num_used(&s->cmdfifo)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* CDB may be transferred in one or more TI commands */
|
||||||
|
if (esp_cdb_length(s) && esp_cdb_length(s) ==
|
||||||
|
fifo8_num_used(&s->cmdfifo) - s->cmdfifo_cdb_offset) {
|
||||||
/* Command has been received */
|
/* Command has been received */
|
||||||
do_cmd(s);
|
do_cmd(s);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* If data was transferred from the FIFO then raise bus
|
||||||
|
* service interrupt to indicate transfer complete. Otherwise
|
||||||
|
* defer until the next FIFO write.
|
||||||
|
*/
|
||||||
|
if (n) {
|
||||||
|
/* Raise interrupt to indicate transfer complete */
|
||||||
|
s->rregs[ESP_RINTR] |= INTR_BS;
|
||||||
|
esp_raise_irq(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CMD_SEL:
|
||||||
|
case CMD_SELATN:
|
||||||
|
/* FIFO already contain entire CDB */
|
||||||
|
do_cmd(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STAT_DO:
|
case STAT_DO:
|
||||||
esp_nodma_ti_dataout(s);
|
/* Accumulate data in FIFO until non-DMA TI is executed */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STAT_DI:
|
case STAT_DI:
|
||||||
@ -945,6 +1005,10 @@ static void handle_ti(ESPState *s)
|
|||||||
} else {
|
} else {
|
||||||
trace_esp_handle_ti(s->ti_size);
|
trace_esp_handle_ti(s->ti_size);
|
||||||
esp_do_nodma(s);
|
esp_do_nodma(s);
|
||||||
|
|
||||||
|
if (esp_get_phase(s) == STAT_DO) {
|
||||||
|
esp_nodma_ti_dataout(s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1141,23 +1205,10 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
|
|||||||
s->rregs[ESP_RSTAT] &= ~STAT_TC;
|
s->rregs[ESP_RSTAT] &= ~STAT_TC;
|
||||||
break;
|
break;
|
||||||
case ESP_FIFO:
|
case ESP_FIFO:
|
||||||
if (esp_get_phase(s) == STAT_MO || esp_get_phase(s) == STAT_CD) {
|
|
||||||
if (!fifo8_is_full(&s->fifo)) {
|
if (!fifo8_is_full(&s->fifo)) {
|
||||||
esp_fifo_push(&s->fifo, val);
|
esp_fifo_push(&s->fifo, val);
|
||||||
esp_fifo_push(&s->cmdfifo, fifo8_pop(&s->fifo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If any unexpected message out/command phase data is
|
|
||||||
* transferred using non-DMA, raise the interrupt
|
|
||||||
*/
|
|
||||||
if (s->rregs[ESP_CMD] == CMD_TI) {
|
|
||||||
s->rregs[ESP_RINTR] |= INTR_BS;
|
|
||||||
esp_raise_irq(s);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
esp_fifo_push(&s->fifo, val);
|
|
||||||
}
|
}
|
||||||
|
esp_do_nodma(s);
|
||||||
break;
|
break;
|
||||||
case ESP_CMD:
|
case ESP_CMD:
|
||||||
s->rregs[saddr] = val;
|
s->rregs[saddr] = val;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user