diff --git a/devices/common/scsi/sc53c94.cpp b/devices/common/scsi/sc53c94.cpp index 43294b7..514e331 100644 --- a/devices/common/scsi/sc53c94.cpp +++ b/devices/common/scsi/sc53c94.cpp @@ -657,7 +657,7 @@ void Sc53C94::real_dma_xfer_out() { // transfer data from host's memory to target - while (this->xfer_count) { + if (this->xfer_count) { uint32_t got_bytes; uint8_t* src_ptr; this->dma_ch->pull_data(std::min((int)this->xfer_count, DATA_FIFO_MAX), @@ -674,6 +674,16 @@ void Sc53C94::real_dma_xfer_out() this->sequencer(); } } + + if (this->xfer_count) { + this->dma_timer_id = TimerManager::get_instance()->add_oneshot_timer( + 10000, + [this]() { + // re-enter the sequencer with the state specified in next_state + this->dma_timer_id = 0; + this->real_dma_xfer_out(); + }); + } } void Sc53C94::real_dma_xfer_in() @@ -682,25 +692,60 @@ void Sc53C94::real_dma_xfer_in() // transfer data from target to host's memory - while (this->xfer_count) { - if (this->data_fifo_pos) { - this->dma_ch->push_data((char*)this->data_fifo, this->data_fifo_pos); + if (this->xfer_count && this->data_fifo_pos) { + this->dma_ch->push_data((char*)this->data_fifo, this->data_fifo_pos); - this->xfer_count -= this->data_fifo_pos; - this->data_fifo_pos = 0; - if (!this->xfer_count) { - is_done = true; - this->status |= STAT_TC; // signal zero transfer count - this->cur_state = SeqState::XFER_END; - this->sequencer(); - } - } - - // see if we need to refill FIFO - if (!this->data_fifo_pos && !is_done) { + this->xfer_count -= this->data_fifo_pos; + this->data_fifo_pos = 0; + if (!this->xfer_count) { + is_done = true; + this->status |= STAT_TC; // signal zero transfer count + this->cur_state = SeqState::XFER_END; this->sequencer(); } } + + // see if we need to refill FIFO + if (!this->data_fifo_pos && !is_done) { + this->sequencer(); + this->dma_timer_id = TimerManager::get_instance()->add_oneshot_timer( + 10000, + [this]() { + // re-enter the sequencer with the state specified in next_state + this->dma_timer_id = 0; + this->real_dma_xfer_in(); + }); + } +} + +void Sc53C94::dma_wait() { + if (this->cur_bus_phase == ScsiPhase::DATA_IN && this->cur_state == SeqState::RCV_DATA) { + real_dma_xfer_in(); + } + else if (this->cur_bus_phase == ScsiPhase::DATA_OUT && this->cur_state == SeqState::SEND_DATA) { + real_dma_xfer_out(); + } + else { + this->dma_timer_id = TimerManager::get_instance()->add_oneshot_timer( + 10000, + [this]() { + this->dma_timer_id = 0; + this->dma_wait(); + }); + } +} + +void Sc53C94::dma_start() +{ + dma_wait(); +} + +void Sc53C94::dma_stop() +{ + if (this->dma_timer_id) { + TimerManager::get_instance()->cancel_timer(this->dma_timer_id); + this->dma_timer_id = 0; + } } static const PropMap Sc53C94_properties = { diff --git a/devices/common/scsi/sc53c94.h b/devices/common/scsi/sc53c94.h index 0c65b0b..48a40ee 100644 --- a/devices/common/scsi/sc53c94.h +++ b/devices/common/scsi/sc53c94.h @@ -30,12 +30,12 @@ along with this program. If not, see . #define SC_53C94_H #include +#include #include #include #include -class DmaBidirChannel; class InterruptCtrl; /** 53C94 read registers */ @@ -209,8 +209,18 @@ public: void real_dma_xfer_out(); void real_dma_xfer_in(); + void dma_start(); + void dma_wait(); + void dma_stop(); void set_dma_channel(DmaBidirChannel *dma_ch) { this->dma_ch = dma_ch; + auto dbdma_ch = dynamic_cast(dma_ch); + if (dbdma_ch) { + dbdma_ch->set_callbacks( + std::bind(&Sc53C94::dma_start, this), + std::bind(&Sc53C94::dma_stop, this) + ); + } }; void set_drq_callback(DrqCb cb) { @@ -282,6 +292,7 @@ private: // DMA related stuff DmaBidirChannel* dma_ch = nullptr; DrqCb drq_cb = nullptr; + uint32_t dma_timer_id = 0; }; #endif // SC_53C94_H