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,
};
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
// 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);
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
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:

View File

@ -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);