From c51ea575cab51eb7a8708190a5190667f3d7a763 Mon Sep 17 00:00:00 2001 From: Maxim Poliakovski Date: Tue, 25 Oct 2022 02:45:44 +0200 Subject: [PATCH] scsi_bus: various fixes and improvements. --- devices/common/scsi/scsi.h | 27 +++++++++++++----- devices/common/scsi/scsi_bus.cpp | 49 +++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/devices/common/scsi/scsi.h b/devices/common/scsi/scsi.h index 24c418c..7d3a414 100644 --- a/devices/common/scsi/scsi.h +++ b/devices/common/scsi/scsi.h @@ -45,13 +45,19 @@ enum { SCSI_CTRL_RST = 1 << 8, }; -enum ScsiPhase : int { +namespace ScsiPhase { +enum : int { BUS_FREE = 0, ARBITRATION, SELECTION, RESELECTION, + COMMAND, + DATA, + STATUS, + MESSAGE, RESET, }; +}; enum ScsiMsg : int { CONFIRM_SEL = 1, @@ -143,12 +149,18 @@ public: ScsiBus(); ~ScsiBus() = default; - // low-level control/status - void register_device(int id, ScsiDevice* dev_obj); - void assert_ctrl_line(int id, uint16_t mask); - void release_ctrl_line(int id, uint16_t mask); - void release_ctrl_lines(int id); - int current_phase() { return this->cur_phase; }; + // low-level state management + void register_device(int id, ScsiDevice* dev_obj); + int current_phase() { return this->cur_phase; }; + + // reading/writing control lines + void assert_ctrl_line(int id, uint16_t mask); + void release_ctrl_line(int id, uint16_t mask); + void release_ctrl_lines(int id); + uint16_t test_ctrl_lines(uint16_t mask); + + // reading/writing data lines + uint8_t get_data_lines() { return this->data_lines; }; // high-level control/status bool begin_arbitration(int id); @@ -156,6 +168,7 @@ 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); protected: diff --git a/devices/common/scsi/scsi_bus.cpp b/devices/common/scsi/scsi_bus.cpp index a50efe3..3bb06ee 100644 --- a/devices/common/scsi/scsi_bus.cpp +++ b/devices/common/scsi/scsi_bus.cpp @@ -36,11 +36,11 @@ ScsiBus::ScsiBus() this->dev_ctrl_lines[i] = 0; } - this->ctrl_lines = 0; // all control lines released - this->data_lines = 0; // data lines released + this->ctrl_lines = 0; // all control lines released + this->data_lines = 0; // data lines released this->arb_winner_id = -1; - this->initiator_id = -1; - this->target_id = -1; + this->initiator_id = -1; + this->target_id = -1; } void ScsiBus::register_device(int id, ScsiDevice* dev_obj) @@ -98,6 +98,8 @@ void ScsiBus::release_ctrl_line(int id, uint16_t mask) this->cur_phase = ScsiPhase::BUS_FREE; change_bus_phase(id); } + } else { + this->ctrl_lines = new_state; } } @@ -106,6 +108,18 @@ void ScsiBus::release_ctrl_lines(int id) this->release_ctrl_line(id, 0xFFFFUL); } +uint16_t ScsiBus::test_ctrl_lines(uint16_t mask) +{ + uint16_t new_state = 0; + + // OR control lines of all devices together + for (int i = 0; i < SCSI_MAX_DEVS; i++) { + new_state |= this->dev_ctrl_lines[i]; + } + + return new_state & mask; +} + bool ScsiBus::begin_arbitration(int initiator_id) { if (this->cur_phase == ScsiPhase::BUS_FREE) { @@ -143,6 +157,8 @@ bool ScsiBus::begin_selection(int initiator_id, int target_id, bool atn) if (this->cur_phase != ScsiPhase::ARBITRATION || this->arb_winner_id != initiator_id) return false; + this->assert_ctrl_line(initiator_id, SCSI_CTRL_SEL); + this->data_lines |= (1 << initiator_id) | (1 << target_id); if (atn) { @@ -171,6 +187,31 @@ 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->cur_phase = ScsiPhase::COMMAND; + change_bus_phase(target_id); + + // 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 + 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; + } + } + + return true; +} + void ScsiBus::disconnect(int dev_id) { this->release_ctrl_lines(dev_id);