diff --git a/devices/common/scsi/sc53c94.cpp b/devices/common/scsi/sc53c94.cpp index cf6db9c..422d2c7 100644 --- a/devices/common/scsi/sc53c94.cpp +++ b/devices/common/scsi/sc53c94.cpp @@ -445,6 +445,7 @@ void Sc53C94::sequencer() } break; case SeqState::SEND_MSG: + this->bus_obj->negotiate_xfer(this->data_fifo_pos, this->bytes_out); this->bus_obj->push_data(this->target_id, this->data_fifo, this->bytes_out); this->data_fifo_pos -= this->bytes_out; if (this->data_fifo_pos > 0) { @@ -452,6 +453,7 @@ void Sc53C94::sequencer() } break; case SeqState::SEND_CMD: + this->bus_obj->negotiate_xfer(this->data_fifo_pos, this->bytes_out); this->bus_obj->push_data(this->target_id, this->data_fifo, this->data_fifo_pos); this->data_fifo_pos = 0; break; @@ -464,18 +466,17 @@ void Sc53C94::sequencer() case SeqState::XFER_BEGIN: this->cur_bus_phase = this->bus_obj->current_phase(); switch (this->cur_bus_phase) { - case ScsiPhase::COMMAND: case ScsiPhase::DATA_OUT: - case ScsiPhase::MESSAGE_OUT: + if (this->cmd_fifo[0] & 0x80) { + this->cur_state = SeqState::SEND_DATA; + break; + } this->bus_obj->push_data(this->target_id, this->data_fifo, this->data_fifo_pos); this->data_fifo_pos = 0; this->cur_state = SeqState::XFER_END; - this->cmd_steps++; this->sequencer(); break; - case ScsiPhase::STATUS: case ScsiPhase::DATA_IN: - case ScsiPhase::MESSAGE_IN: this->bus_obj->negotiate_xfer(this->data_fifo_pos, this->bytes_out); this->cur_state = SeqState::RCV_DATA; this->rcv_data(); @@ -493,6 +494,8 @@ void Sc53C94::sequencer() this->update_irq(); exec_next_command(); break; + case SeqState::SEND_DATA: + break; case SeqState::RCV_DATA: // check for unexpected bus phase changes if (this->bus_obj->current_phase() != this->cur_bus_phase) { @@ -607,7 +610,25 @@ void Sc53C94::real_dma_xfer(int direction) bool is_done = false; if (direction) { - ABORT_F("Sc53C94: real DMA transfers from host to target not implemented yet"); + uint32_t got_bytes; + uint8_t* src_ptr; + + while (this->xfer_count) { + this->dma_ch->pull_data(std::min((int)this->xfer_count, DATA_FIFO_MAX), + &got_bytes, &src_ptr); + std::memcpy(this->data_fifo, src_ptr, got_bytes); + this->data_fifo_pos = got_bytes; + this->bus_obj->push_data(this->target_id, 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(); + } + } } else { // transfer data from target to host's memory while (this->xfer_count) { if (this->data_fifo_pos) { diff --git a/devices/common/scsi/scsi.h b/devices/common/scsi/scsi.h index 8f209bf..34d2bdf 100644 --- a/devices/common/scsi/scsi.h +++ b/devices/common/scsi/scsi.h @@ -28,6 +28,7 @@ along with this program. If not, see . #include #include +#include #include /** SCSI control signals. @@ -146,6 +147,8 @@ enum ScsiError : int { class ScsiBus; +typedef std::function action_callback; + class ScsiDevice : public HWComponent { public: ScsiDevice(int my_id) { @@ -157,12 +160,13 @@ public: virtual void notify(ScsiBus* bus_obj, ScsiMsg msg_type, int param); virtual void next_step(ScsiBus* bus_obj); virtual void prepare_xfer(ScsiBus* bus_obj, int& bytes_in, int& bytes_out); + virtual void switch_phase(const int new_phase); - virtual bool prepare_data() = 0; virtual bool has_data() { return this->data_size != 0; }; virtual int send_data(uint8_t* dst_ptr, int count); virtual int rcv_data(const uint8_t* src_ptr, const int count); + virtual bool prepare_data() = 0; virtual void process_command() = 0; protected: @@ -173,7 +177,13 @@ protected: int cur_phase; uint8_t* data_ptr = nullptr; int data_size; + int incoming_size; uint8_t status; + + ScsiBus* bus_obj; + + action_callback pre_xfer_action = nullptr; + action_callback post_xfer_action = nullptr; }; /** This class provides a higher level abstraction for the SCSI bus. */ diff --git a/devices/common/scsi/scsi_hd.cpp b/devices/common/scsi/scsi_hd.cpp index 1ce7dd2..5b7f43d 100644 --- a/devices/common/scsi/scsi_hd.cpp +++ b/devices/common/scsi/scsi_hd.cpp @@ -66,12 +66,17 @@ void ScsiHardDisk::process_command() { uint8_t page_code = 0; uint8_t subpage_code = 0; + this->pre_xfer_action = nullptr; + this->post_xfer_action = nullptr; + // assume successful command execution this->status = ScsiStatus::GOOD; uint8_t* cmd = this->cmd_buf; - LOG_F(INFO, "SCSI-HD: processing command 0x%X", cmd[0]); + if (cmd[0] != 8 && cmd[0] != 0xA && cmd[0] != 0x28 && cmd[0] != 0x2A) { + ABORT_F("SCSI-HD: untested command 0x%X", cmd[0]); + } switch (cmd[0]) { case ScsiCommand::TEST_UNIT_READY: @@ -107,6 +112,7 @@ void ScsiHardDisk::process_command() { lba = (cmd[2] << 24) + (cmd[3] << 16) + (cmd[4] << 8) + cmd[5]; transfer_len = (cmd[7] << 8) + cmd[8]; write(lba, transfer_len, 10); + this->switch_phase(ScsiPhase::DATA_OUT); break; case ScsiCommand::SEEK_6: lba = ((cmd[1] & 0x1F) << 16) + (cmd[2] << 8) + cmd[3]; @@ -138,6 +144,10 @@ bool ScsiHardDisk::prepare_data() { this->data_ptr = (uint8_t*)this->img_buffer; this->data_size = this->cur_buf_cnt; break; + case ScsiPhase::DATA_OUT: + this->data_ptr = (uint8_t*)this->img_buffer; + this->data_size = 0; + break; case ScsiPhase::STATUS: if (!error) { this->img_buffer[0] = ScsiStatus::GOOD; @@ -242,6 +252,8 @@ void ScsiHardDisk::read(uint32_t lba, uint16_t transfer_len, uint8_t cmd_len) { this->cur_buf_cnt = transfer_size; this->msg_buf[0] = ScsiMessage::COMMAND_COMPLETE; + + this->switch_phase(ScsiPhase::DATA_IN); } void ScsiHardDisk::write(uint32_t lba, uint16_t transfer_len, uint8_t cmd_len) { @@ -253,8 +265,13 @@ void ScsiHardDisk::write(uint32_t lba, uint16_t transfer_len, uint8_t cmd_len) { transfer_size *= sector_size; uint64_t device_offset = lba * sector_size; + this->incoming_size = transfer_size; + this->hdd_img.seekg(device_offset, this->hdd_img.beg); - this->hdd_img.write((char*)&img_buffer, transfer_size); + + this->post_xfer_action = [this]() { + this->hdd_img.write(this->img_buffer, this->incoming_size); + }; } void ScsiHardDisk::seek(uint32_t lba) { diff --git a/devices/common/scsi/scsidevice.cpp b/devices/common/scsi/scsidevice.cpp index ff8ef34..a861ed4 100644 --- a/devices/common/scsi/scsidevice.cpp +++ b/devices/common/scsi/scsidevice.cpp @@ -45,16 +45,13 @@ void ScsiDevice::notify(ScsiBus* bus_obj, ScsiMsg msg_type, int param) bus_obj->assert_ctrl_line(this->scsi_id, SCSI_CTRL_BSY); bus_obj->confirm_selection(this->scsi_id); this->initiator_id = bus_obj->get_initiator_id(); + this->bus_obj = bus_obj; if (bus_obj->test_ctrl_lines(SCSI_CTRL_ATN)) { - this->cur_phase = ScsiPhase::MESSAGE_OUT; - bus_obj->switch_phase(this->scsi_id, ScsiPhase::MESSAGE_OUT); + this->switch_phase(ScsiPhase::MESSAGE_OUT); LOG_F(INFO, "ScsiDevice: received message 0x%X", this->msg_buf[0]); } - this->cur_phase = ScsiPhase::COMMAND; - bus_obj->switch_phase(this->scsi_id, ScsiPhase::COMMAND); + this->switch_phase(ScsiPhase::COMMAND); this->process_command(); - this->cur_phase = ScsiPhase::DATA_IN; - bus_obj->switch_phase(this->scsi_id, ScsiPhase::DATA_IN); if (this->prepare_data()) { bus_obj->assert_ctrl_line(this->scsi_id, SCSI_CTRL_REQ); } else { @@ -67,31 +64,39 @@ void ScsiDevice::notify(ScsiBus* bus_obj, ScsiMsg msg_type, int param) } } +void ScsiDevice::switch_phase(const int new_phase) +{ + this->cur_phase = new_phase; + this->bus_obj->switch_phase(this->scsi_id, this->cur_phase); +} + void ScsiDevice::next_step(ScsiBus* bus_obj) { switch (this->cur_phase) { case ScsiPhase::COMMAND: this->process_command(); - bus_obj->switch_phase(this->scsi_id, ScsiPhase::DATA_IN); - this->cur_phase = ScsiPhase::DATA_IN; + this->switch_phase(ScsiPhase::DATA_IN); + break; + case ScsiPhase::DATA_OUT: + if (this->data_size >= this->incoming_size) { + if (this->post_xfer_action != nullptr) { + this->post_xfer_action(); + } + this->switch_phase(ScsiPhase::STATUS); + } break; case ScsiPhase::DATA_IN: if (!this->has_data()) { - this->cur_phase = ScsiPhase::STATUS; - bus_obj->switch_phase(this->scsi_id, ScsiPhase::STATUS); + this->switch_phase(ScsiPhase::STATUS); } break; case ScsiPhase::STATUS: - this->cur_phase = ScsiPhase::MESSAGE_IN; - bus_obj->switch_phase(this->scsi_id, ScsiPhase::MESSAGE_IN); + this->switch_phase(ScsiPhase::MESSAGE_IN); break; case ScsiPhase::MESSAGE_IN: - this->cur_phase = ScsiPhase::BUS_FREE; - /* fall-through */ case ScsiPhase::BUS_FREE: bus_obj->release_ctrl_lines(this->scsi_id); - bus_obj->switch_phase(this->scsi_id, ScsiPhase::BUS_FREE); - this->cur_phase = ScsiPhase::BUS_FREE; + this->switch_phase(ScsiPhase::BUS_FREE); break; default: LOG_F(WARNING, "ScsiDevice: nothing to do for phase %d", this->cur_phase); @@ -115,6 +120,8 @@ void ScsiDevice::prepare_xfer(ScsiBus* bus_obj, int& bytes_in, int& bytes_out) case ScsiPhase::DATA_IN: bytes_out = this->data_size; break; + case ScsiPhase::DATA_OUT: + break; case ScsiPhase::MESSAGE_OUT: this->data_ptr = this->msg_buf; this->data_size = bytes_in; @@ -146,20 +153,10 @@ int ScsiDevice::send_data(uint8_t* dst_ptr, const int count) int ScsiDevice::rcv_data(const uint8_t* src_ptr, const int count) { - uint8_t* dst_ptr; - - switch (this->cur_phase) { - case ScsiPhase::COMMAND: - dst_ptr = this->cmd_buf; - break; - case ScsiPhase::MESSAGE_OUT: - dst_ptr = this->msg_buf; - break; - default: - ABORT_F("ScsiDevice: invalid phase %d in rcv_data()", this->cur_phase); - } - - std::memcpy(dst_ptr, src_ptr, count); + // accumulating incoming data in the pre-configured buffer + std::memcpy(this->data_ptr, src_ptr, count); + this->data_ptr += count; + this->data_size += count; return count; }