diff options
-rw-r--r-- | hw/scsi/lsi53c895a.c | 48 |
1 files changed, 36 insertions, 12 deletions
diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 3f207f607c..52a38933b6 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -298,6 +298,18 @@ static inline int lsi_irq_on_rsl(LSIState *s) return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE); } +static lsi_request *get_pending_req(LSIState *s) +{ + lsi_request *p; + + QTAILQ_FOREACH(p, &s->queue, next) { + if (p->pending) { + return p; + } + } + return NULL; +} + static void lsi_soft_reset(LSIState *s) { trace_lsi_reset(); @@ -446,7 +458,6 @@ static void lsi_update_irq(LSIState *s) { int level; static int last_level; - lsi_request *p; /* It's unclear whether the DIP/SIP bits should be cleared when the Interrupt Status Registers are cleared or when istat0 is read. @@ -476,13 +487,13 @@ static void lsi_update_irq(LSIState *s) } lsi_set_irq(s, level); - if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { + if (!s->current && !level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { + lsi_request *p; + trace_lsi_update_irq_disconnected(); - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->pending) { - lsi_reselect(s, p); - break; - } + p = get_pending_req(s); + if (p) { + lsi_reselect(s, p); } } } @@ -1065,11 +1076,12 @@ static void lsi_wait_reselect(LSIState *s) trace_lsi_wait_reselect(); - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->pending) { - lsi_reselect(s, p); - break; - } + if (s->current) { + return; + } + p = get_pending_req(s); + if (p) { + lsi_reselect(s, p); } if (s->current == NULL) { s->waiting = 1; @@ -1259,6 +1271,18 @@ again: case 1: /* Disconnect */ trace_lsi_execute_script_io_disconnect(); s->scntl1 &= ~LSI_SCNTL1_CON; + /* FIXME: this is not entirely correct; the target need not ask + * for reselection until it has to send data, while here we force a + * reselection as soon as the bus is free. The correct flow would + * reselect before lsi_transfer_data and disconnect as soon as + * DMA ends. + */ + if (!s->current) { + lsi_request *p = get_pending_req(s); + if (p) { + lsi_reselect(s, p); + } + } break; case 2: /* Wait Reselect */ if (!lsi_irq_on_rsl(s)) { |