diff --git a/devices/common/scsi/sc53c94.cpp b/devices/common/scsi/sc53c94.cpp index 4feebae..0b3d474 100644 --- a/devices/common/scsi/sc53c94.cpp +++ b/devices/common/scsi/sc53c94.cpp @@ -462,18 +462,23 @@ 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) { - std::memmove(this->data_fifo, &this->data_fifo[this->bytes_out], this->data_fifo_pos); + if (this->data_fifo_pos < 1 && this->is_dma_cmd) { + this->drq_cb(1); + this->int_status |= INTSTAT_SR; + this->update_irq(); + break; } + this->bus_obj->target_xfer_data(); this->bus_obj->release_ctrl_line(this->my_bus_id, SCSI_CTRL_ATN); 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; + if (this->data_fifo_pos < 1 && this->is_dma_cmd) { + this->drq_cb(1); + this->int_status |= INTSTAT_SR; + this->update_irq(); + break; + } + this->bus_obj->target_xfer_data(); break; case SeqState::CMD_COMPLETE: this->seq_step = this->cmd_steps->step_num; @@ -568,6 +573,7 @@ void Sc53C94::notify(ScsiMsg msg_type, int param) } break; case ScsiMsg::BUS_PHASE_CHANGE: + this->cur_bus_phase = param; if (param != ScsiPhase::BUS_FREE && this->cmd_steps != nullptr) { this->cmd_steps++; this->cur_state = this->cmd_steps->next_step; @@ -594,7 +600,7 @@ int Sc53C94::send_data(uint8_t* dst_ptr, int count) this->data_fifo_pos -= actual_count; if (this->data_fifo_pos > 0) { std::memmove(this->data_fifo, &this->data_fifo[actual_count], this->data_fifo_pos); - } else { + } else if (this->cur_bus_phase == ScsiPhase::DATA_OUT) { this->cmd_steps++; this->cur_state = this->cmd_steps->next_step; this->sequencer(); diff --git a/devices/common/scsi/scsi.h b/devices/common/scsi/scsi.h index 9a890bb..5c92952 100644 --- a/devices/common/scsi/scsi.h +++ b/devices/common/scsi/scsi.h @@ -176,6 +176,7 @@ public: virtual void switch_phase(const int new_phase); virtual bool has_data() { return this->data_size != 0; }; + virtual int xfer_data(); virtual int send_data(uint8_t* dst_ptr, int count); virtual int rcv_data(const uint8_t* src_ptr, const int count); @@ -243,6 +244,7 @@ public: void disconnect(int dev_id); bool pull_data(const int id, uint8_t* dst_ptr, const int size); bool push_data(const int id, const uint8_t* src_ptr, const int size); + int target_xfer_data(); void target_next_step(); bool negotiate_xfer(int& bytes_in, int& bytes_out); diff --git a/devices/common/scsi/scsibus.cpp b/devices/common/scsi/scsibus.cpp index ad3929d..0bdaf7f 100644 --- a/devices/common/scsi/scsibus.cpp +++ b/devices/common/scsi/scsibus.cpp @@ -261,6 +261,10 @@ bool ScsiBus::push_data(const int id, const uint8_t* src_ptr, const int size) return true; } +int ScsiBus::target_xfer_data() { + return this->devices[this->target_id]->xfer_data(); +} + void ScsiBus::target_next_step() { this->devices[this->target_id]->next_step(); diff --git a/devices/common/scsi/scsidevice.cpp b/devices/common/scsi/scsidevice.cpp index 4e5f802..7dbadad 100644 --- a/devices/common/scsi/scsidevice.cpp +++ b/devices/common/scsi/scsidevice.cpp @@ -48,16 +48,8 @@ void ScsiDevice::notify(ScsiMsg msg_type, int param) this->initiator_id = this->bus_obj->get_initiator_id(); if (this->bus_obj->test_ctrl_lines(SCSI_CTRL_ATN)) { this->switch_phase(ScsiPhase::MESSAGE_OUT); - if (this->msg_buf[0] != 0x80) { - LOG_F(INFO, "ScsiDevice: received message 0x%X", this->msg_buf[0]); - } - } - this->switch_phase(ScsiPhase::COMMAND); - this->process_command(); - if (this->prepare_data()) { - bus_obj->assert_ctrl_line(this->scsi_id, SCSI_CTRL_REQ); } else { - ABORT_F("ScsiDevice: prepare_data() failed"); + this->switch_phase(ScsiPhase::COMMAND); } }); } @@ -75,10 +67,6 @@ void ScsiDevice::switch_phase(const int new_phase) void ScsiDevice::next_step() { switch (this->cur_phase) { - case ScsiPhase::COMMAND: - this->process_command(); - this->switch_phase(ScsiPhase::DATA_IN); - break; case ScsiPhase::DATA_OUT: if (this->data_size >= this->incoming_size) { if (this->post_xfer_action != nullptr) { @@ -92,9 +80,22 @@ void ScsiDevice::next_step() this->switch_phase(ScsiPhase::STATUS); } break; + case ScsiPhase::COMMAND: + this->process_command(); + if (this->cur_phase != ScsiPhase::COMMAND) { + if (this->prepare_data()) { + this->bus_obj->assert_ctrl_line(this->scsi_id, SCSI_CTRL_REQ); + } else { + ABORT_F("ScsiDevice: prepare_data() failed"); + } + } + break; case ScsiPhase::STATUS: this->switch_phase(ScsiPhase::MESSAGE_IN); break; + case ScsiPhase::MESSAGE_OUT: + this->switch_phase(ScsiPhase::COMMAND); + break; case ScsiPhase::MESSAGE_IN: case ScsiPhase::BUS_FREE: this->bus_obj->release_ctrl_lines(this->scsi_id); @@ -112,7 +113,8 @@ void ScsiDevice::prepare_xfer(ScsiBus* bus_obj, int& bytes_in, int& bytes_out) switch (this->cur_phase) { case ScsiPhase::COMMAND: this->data_ptr = this->cmd_buf; - this->data_size = bytes_in; + this->data_size = 0; //bytes_in; + bytes_out = 0; break; case ScsiPhase::STATUS: this->data_ptr = &this->status; @@ -127,6 +129,7 @@ void ScsiDevice::prepare_xfer(ScsiBus* bus_obj, int& bytes_in, int& bytes_out) case ScsiPhase::MESSAGE_OUT: this->data_ptr = this->msg_buf; this->data_size = bytes_in; + bytes_out = 0; break; case ScsiPhase::MESSAGE_IN: this->data_ptr = this->msg_buf; @@ -138,6 +141,42 @@ void ScsiDevice::prepare_xfer(ScsiBus* bus_obj, int& bytes_in, int& bytes_out) } } +static const int CmdGroupLen[8] = {6, 10, 10, -1, -1, 12, -1, -1}; + +int ScsiDevice::xfer_data() { + this->cur_phase = bus_obj->current_phase(); + + switch (this->cur_phase) { + case ScsiPhase::MESSAGE_OUT: + if (this->bus_obj->pull_data(this->initiator_id, this->msg_buf, 1)) { + if (this->msg_buf[0] & 0x80) { + LOG_F(9, "%s: IDENTIFY MESSAGE received, code = 0x%X", + this->name.c_str(), this->msg_buf[0]); + this->next_step(); + } else { + ABORT_F("%s: unsupported message received, code = 0x%X", + this->name.c_str(), this->msg_buf[0]); + } + } + break; + case ScsiPhase::COMMAND: + if (this->bus_obj->pull_data(this->initiator_id, this->cmd_buf, 1)) { + int cmd_len = CmdGroupLen[this->cmd_buf[0] >> 5]; + if (cmd_len < 0) { + ABORT_F("%s: unsupported command received, code = 0x%X", + this->name.c_str(), this->msg_buf[0]); + } + if (this->bus_obj->pull_data(this->initiator_id, &this->cmd_buf[1], cmd_len - 1)) + this->next_step(); + } + break; + default: + ABORT_F("ScsiDevice: unhandled phase %d in xfer_data()", this->cur_phase); + } + + return 0; +} + int ScsiDevice::send_data(uint8_t* dst_ptr, const int count) { if (dst_ptr == nullptr || !count) { @@ -160,5 +199,8 @@ int ScsiDevice::rcv_data(const uint8_t* src_ptr, const int count) this->data_ptr += count; this->data_size += count; + if (this->cur_phase == ScsiPhase::COMMAND) + this->next_step(); + return count; }