mirror of
https://github.com/dingusdev/dingusppc.git
synced 2025-01-11 20:29:46 +00:00
scsidevice: refactor states.
This commit is contained in:
parent
49331635b2
commit
40a02cc0f7
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user