scsi_bus: various fixes and improvements.

This commit is contained in:
Maxim Poliakovski 2022-10-25 02:45:44 +02:00
parent 9efac80271
commit c51ea575ca
2 changed files with 65 additions and 11 deletions

View File

@ -45,13 +45,19 @@ enum {
SCSI_CTRL_RST = 1 << 8, SCSI_CTRL_RST = 1 << 8,
}; };
enum ScsiPhase : int { namespace ScsiPhase {
enum : int {
BUS_FREE = 0, BUS_FREE = 0,
ARBITRATION, ARBITRATION,
SELECTION, SELECTION,
RESELECTION, RESELECTION,
COMMAND,
DATA,
STATUS,
MESSAGE,
RESET, RESET,
}; };
};
enum ScsiMsg : int { enum ScsiMsg : int {
CONFIRM_SEL = 1, CONFIRM_SEL = 1,
@ -143,12 +149,18 @@ public:
ScsiBus(); ScsiBus();
~ScsiBus() = default; ~ScsiBus() = default;
// low-level control/status // low-level state management
void register_device(int id, ScsiDevice* dev_obj); 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 assert_ctrl_line(int id, uint16_t mask);
void release_ctrl_line(int id, uint16_t mask); void release_ctrl_line(int id, uint16_t mask);
void release_ctrl_lines(int id); void release_ctrl_lines(int id);
int current_phase() { return this->cur_phase; }; 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 // high-level control/status
bool begin_arbitration(int id); bool begin_arbitration(int id);
@ -156,6 +168,7 @@ public:
bool begin_selection(int initiator_id, int target_id, bool atn); bool begin_selection(int initiator_id, int target_id, bool atn);
void confirm_selection(int target_id); void confirm_selection(int target_id);
bool end_selection(int initiator_id, int target_id); bool end_selection(int initiator_id, int target_id);
bool transfer_command(uint8_t* dst_ptr);
void disconnect(int dev_id); void disconnect(int dev_id);
protected: protected:

View File

@ -98,6 +98,8 @@ void ScsiBus::release_ctrl_line(int id, uint16_t mask)
this->cur_phase = ScsiPhase::BUS_FREE; this->cur_phase = ScsiPhase::BUS_FREE;
change_bus_phase(id); 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); 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) bool ScsiBus::begin_arbitration(int initiator_id)
{ {
if (this->cur_phase == ScsiPhase::BUS_FREE) { 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) if (this->cur_phase != ScsiPhase::ARBITRATION || this->arb_winner_id != initiator_id)
return false; return false;
this->assert_ctrl_line(initiator_id, SCSI_CTRL_SEL);
this->data_lines |= (1 << initiator_id) | (1 << target_id); this->data_lines |= (1 << initiator_id) | (1 << target_id);
if (atn) { if (atn) {
@ -171,6 +187,31 @@ bool ScsiBus::end_selection(int initiator_id, int target_id)
return this->target_id == 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) void ScsiBus::disconnect(int dev_id)
{ {
this->release_ctrl_lines(dev_id); this->release_ctrl_lines(dev_id);