From 40a02cc0f7fce20516ddd525466eb4fe0d1df0ce Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Mon, 7 Nov 2022 12:24:02 +0100 Subject: [PATCH] scsidevice: refactor states. --- devices/common/scsi/scsi.h | 27 +++++--- devices/common/scsi/scsi_bus.cpp | 68 ++++++++------------- devices/common/scsi/scsi_hd.cpp | 21 +++---- devices/common/scsi/scsi_hd.h | 4 +- devices/common/scsi/scsidevice.cpp | 98 +++++++++++++++++++++++++++--- 5 files changed, 142 insertions(+), 76 deletions(-) diff --git a/devices/common/scsi/scsi.h b/devices/common/scsi/scsi.h index 0151d04..8f209bf 100644 --- a/devices/common/scsi/scsi.h +++ b/devices/common/scsi/scsi.h @@ -79,6 +79,8 @@ enum ScsiMsg : int { BUS_PHASE_CHANGE, SEND_CMD_BEGIN, SEND_CMD_END, + MESSAGE_BEGIN, + MESSAGE_END, }; enum ScsiCommand : int { @@ -154,17 +156,24 @@ 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 bool prepare_data() = 0; - virtual bool has_data() = 0; - virtual bool send_bytes(uint8_t* dst_ptr, int count) = 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 void process_command() = 0; protected: - uint8_t cmd_buf[16] = {}; - int cur_phase; - int scsi_id; + uint8_t cmd_buf[16] = {}; + uint8_t msg_buf[16] = {}; // TODO: clarify how big this one should be + int scsi_id; + int initiator_id; + int cur_phase; + uint8_t* data_ptr = nullptr; + int data_size; + uint8_t status; }; /** This class provides a higher level abstraction for the SCSI bus. */ @@ -176,6 +185,8 @@ public: // low-level state management void register_device(int id, ScsiDevice* dev_obj); int current_phase() { return this->cur_phase; }; + int get_initiator_id() { return this->initiator_id; }; + int get_target_id() { return this->target_id; }; // reading/writing control lines void assert_ctrl_line(int id, uint16_t mask); @@ -193,11 +204,11 @@ public: bool begin_selection(int initiator_id, int target_id, bool atn); void confirm_selection(int target_id); bool end_selection(int initiator_id, int target_id); - bool transfer_command(uint8_t* dst_ptr); void disconnect(int dev_id); - bool target_request_data(); - bool target_pull_data(uint8_t* dst_ptr, int size); + 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); void target_next_step(); + bool negotiate_xfer(int& bytes_in, int& bytes_out); protected: void change_bus_phase(int initiator_id); diff --git a/devices/common/scsi/scsi_bus.cpp b/devices/common/scsi/scsi_bus.cpp index 0b626cb..0079fa9 100644 --- a/devices/common/scsi/scsi_bus.cpp +++ b/devices/common/scsi/scsi_bus.cpp @@ -134,6 +134,9 @@ int ScsiBus::switch_phase(int id, int new_phase) case ScsiPhase::STATUS: this->release_ctrl_line(id, SCSI_CTRL_CD | SCSI_CTRL_IO); break; + case ScsiPhase::MESSAGE_OUT: + this->release_ctrl_line(id, SCSI_CTRL_CD | SCSI_CTRL_MSG); + break; } // enter new phase (low-level) @@ -147,6 +150,9 @@ int ScsiBus::switch_phase(int id, int new_phase) case ScsiPhase::STATUS: this->assert_ctrl_line(id, SCSI_CTRL_CD | SCSI_CTRL_IO); break; + case ScsiPhase::MESSAGE_OUT: + this->assert_ctrl_line(id, SCSI_CTRL_CD | SCSI_CTRL_MSG); + break; } // switch the bus to the new phase (high-level) @@ -223,53 +229,24 @@ bool ScsiBus::end_selection(int initiator_id, int target_id) return this->target_id == target_id; } -bool ScsiBus::transfer_command(uint8_t* dst_ptr) -{ - static uint8_t cmd_to_cdb_length[8] = {6, 10, 10, 6, 6, 12, 6, 6}; - - this->switch_phase(target_id, ScsiPhase::COMMAND); - - this->devices[this->initiator_id]->notify(this, ScsiMsg::SEND_CMD_BEGIN, 0); - - // attempt to transfer the shortest Command Description Block (CDB) - if (!this->devices[this->initiator_id]->send_bytes(dst_ptr, 6)) { - LOG_F(ERROR, "ScsiBus: error while transferring CDB!"); - return false; - } - - // transfer the remaining CDB bytes if any - int cdb_length = cmd_to_cdb_length[dst_ptr[0] >> 5]; - if (cdb_length > 6) { - if (!this->devices[this->initiator_id]->send_bytes(dst_ptr + 6, cdb_length - 6)) { - LOG_F(ERROR, "ScsiBus: error while transferring CDB!"); - return false; - } - } - - this->devices[this->initiator_id]->notify(this, ScsiMsg::SEND_CMD_END, 0); - - return true; -} - -bool ScsiBus::target_request_data() -{ - if (this->devices[this->target_id]->prepare_data()) { - this->assert_ctrl_line(target_id, SCSI_CTRL_REQ); - return true; - } else { - return false; - } -} - -bool ScsiBus::target_pull_data(uint8_t* dst_ptr, int size) +bool ScsiBus::pull_data(const int id, uint8_t* dst_ptr, const int size) { if (dst_ptr == nullptr || !size) { return false; } - // transfer the requested number of bytes from target to initiator - if (!this->devices[this->target_id]->send_bytes(dst_ptr, size)) { - LOG_F(ERROR, "ScsiBus: error while transferring data from target!"); + if (!this->devices[id]->send_data(dst_ptr, size)) { + LOG_F(ERROR, "ScsiBus: error while transferring T->I data!"); + return false; + } + + return true; +} + +bool ScsiBus::push_data(const int id, const uint8_t* src_ptr, const int size) +{ + if (!this->devices[id]->rcv_data(src_ptr, size)) { + LOG_F(ERROR, "ScsiBus: error while transferring I->T data!"); return false; } @@ -281,6 +258,13 @@ void ScsiBus::target_next_step() this->devices[this->target_id]->next_step(this); } +bool ScsiBus::negotiate_xfer(int& bytes_in, int& bytes_out) +{ + this->devices[this->target_id]->prepare_xfer(this, bytes_in, bytes_out); + + return true; +} + void ScsiBus::disconnect(int dev_id) { this->release_ctrl_lines(dev_id); diff --git a/devices/common/scsi/scsi_hd.cpp b/devices/common/scsi/scsi_hd.cpp index 2b9cf88..1ce7dd2 100644 --- a/devices/common/scsi/scsi_hd.cpp +++ b/devices/common/scsi/scsi_hd.cpp @@ -66,8 +66,13 @@ void ScsiHardDisk::process_command() { uint8_t page_code = 0; uint8_t subpage_code = 0; + // 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]); + switch (cmd[0]) { case ScsiCommand::TEST_UNIT_READY: test_unit_ready(); @@ -130,6 +135,8 @@ bool ScsiHardDisk::prepare_data() { switch (this->cur_phase) { case ScsiPhase::DATA_IN: + this->data_ptr = (uint8_t*)this->img_buffer; + this->data_size = this->cur_buf_cnt; break; case ScsiPhase::STATUS: if (!error) { @@ -151,18 +158,6 @@ bool ScsiHardDisk::prepare_data() { return true; } -bool ScsiHardDisk::send_bytes(uint8_t* dst_ptr, int count) { - if (dst_ptr == nullptr || !count || count > this->cur_buf_cnt) { - return false; - } - - std::memcpy(dst_ptr, &this->img_buffer[this->cur_buf_pos], count); - this->cur_buf_pos += count; - this->cur_buf_cnt -= count; - - return true; -} - int ScsiHardDisk::test_unit_ready() { if (img_path.empty() || img_path == " ") { return ScsiError::DEV_NOT_READY; @@ -246,7 +241,7 @@ void ScsiHardDisk::read(uint32_t lba, uint16_t transfer_len, uint8_t cmd_len) { this->hdd_img.read(img_buffer, transfer_size); this->cur_buf_cnt = transfer_size; - this->msg_code = ScsiMessage::COMMAND_COMPLETE; + this->msg_buf[0] = ScsiMessage::COMMAND_COMPLETE; } void ScsiHardDisk::write(uint32_t lba, uint16_t transfer_len, uint8_t cmd_len) { diff --git a/devices/common/scsi/scsi_hd.h b/devices/common/scsi/scsi_hd.h index 03365fe..b4d8c89 100644 --- a/devices/common/scsi/scsi_hd.h +++ b/devices/common/scsi/scsi_hd.h @@ -42,7 +42,6 @@ public: void insert_image(std::string filename); void process_command(); bool prepare_data(); - bool has_data() { return this->cur_buf_cnt != 0; }; bool send_bytes(uint8_t* dst_ptr, int count); int test_unit_ready(); @@ -65,12 +64,11 @@ private: uint64_t img_size; char img_buffer[1 << 17]; uint64_t file_offset = 0; - uint8_t status = ScsiError::NO_ERROR; // SCSI transfer pointers uint32_t cur_buf_pos = 0; uint32_t cur_buf_cnt = 0; - uint8_t error = 0; + uint8_t error = ScsiError::NO_ERROR; uint8_t msg_code = 0; //inquiry info diff --git a/devices/common/scsi/scsidevice.cpp b/devices/common/scsi/scsidevice.cpp index 0cf9309..ff8ef34 100644 --- a/devices/common/scsi/scsidevice.cpp +++ b/devices/common/scsi/scsidevice.cpp @@ -44,13 +44,22 @@ void ScsiDevice::notify(ScsiBus* bus_obj, ScsiMsg msg_type, int param) return; 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(); if (bus_obj->test_ctrl_lines(SCSI_CTRL_ATN)) { - LOG_F(WARNING, "ScsiDevice: MESSAGE_OUT isn't supported yet"); + this->cur_phase = ScsiPhase::MESSAGE_OUT; + bus_obj->switch_phase(this->scsi_id, ScsiPhase::MESSAGE_OUT); + LOG_F(INFO, "ScsiDevice: received message 0x%X", this->msg_buf[0]); } - bus_obj->transfer_command(this->cmd_buf); + this->cur_phase = ScsiPhase::COMMAND; + bus_obj->switch_phase(this->scsi_id, ScsiPhase::COMMAND); this->process_command(); - bus_obj->switch_phase(this->scsi_id, ScsiPhase::DATA_IN); 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 { + ABORT_F("ScsiDevice: prepare_data() failed"); + } }); } break; @@ -61,27 +70,96 @@ void ScsiDevice::notify(ScsiBus* bus_obj, ScsiMsg msg_type, int param) 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; + break; case ScsiPhase::DATA_IN: - if (this->has_data()) { - LOG_F(WARNING, "ScsiDevice: attempt to leave DATA_IN phase with xfer != 0"); - } else { - bus_obj->switch_phase(this->scsi_id, ScsiPhase::STATUS); + if (!this->has_data()) { this->cur_phase = ScsiPhase::STATUS; + bus_obj->switch_phase(this->scsi_id, ScsiPhase::STATUS); } break; case ScsiPhase::STATUS: - bus_obj->switch_phase(this->scsi_id, ScsiPhase::MESSAGE_IN); this->cur_phase = ScsiPhase::MESSAGE_IN; + bus_obj->switch_phase(this->scsi_id, ScsiPhase::MESSAGE_IN); break; case ScsiPhase::MESSAGE_IN: this->cur_phase = ScsiPhase::BUS_FREE; - break; + /* 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; break; default: - LOG_F(WARNING, "ScsiDevice: nothing to do for the current phase %d", this->cur_phase); + LOG_F(WARNING, "ScsiDevice: nothing to do for phase %d", this->cur_phase); } } + +void ScsiDevice::prepare_xfer(ScsiBus* bus_obj, int& bytes_in, int& bytes_out) +{ + this->cur_phase = bus_obj->current_phase(); + + switch (this->cur_phase) { + case ScsiPhase::COMMAND: + this->data_ptr = this->cmd_buf; + this->data_size = bytes_in; + break; + case ScsiPhase::STATUS: + this->data_ptr = &this->status; + this->data_size = 1; + bytes_out = 1; + break; + case ScsiPhase::DATA_IN: + bytes_out = this->data_size; + break; + case ScsiPhase::MESSAGE_OUT: + this->data_ptr = this->msg_buf; + this->data_size = bytes_in; + break; + case ScsiPhase::MESSAGE_IN: + this->data_ptr = this->msg_buf; + this->data_size = 1; + bytes_out = 1; + break; + default: + ABORT_F("ScsiDevice: unhandled phase %d in prepare_xfer()", this->cur_phase); + } +} + +int ScsiDevice::send_data(uint8_t* dst_ptr, const int count) +{ + if (dst_ptr == nullptr || !count) { + return 0; + } + + int actual_count = std::min(this->data_size, count); + + std::memcpy(dst_ptr, this->data_ptr, actual_count); + this->data_ptr += actual_count; + this->data_size -= actual_count; + + return actual_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); + + return count; +}