1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-30 07:55:01 +00:00

Sketches out further SCSI infrastructure.

This commit is contained in:
Thomas Harte 2019-08-21 22:37:39 -04:00
parent 252650808d
commit bb1a0a0b76
3 changed files with 115 additions and 50 deletions

View File

@ -10,9 +10,9 @@
using namespace SCSI::Target;
CommandArguments::CommandArguments(const std::vector<uint8_t> &data) : data_(data) {}
CommandState::CommandState(const std::vector<uint8_t> &data) : data_(data) {}
uint32_t CommandArguments::address() {
uint32_t CommandState::address() {
switch(data_.size()) {
default: return 0;
case 6:
@ -30,7 +30,7 @@ uint32_t CommandArguments::address() {
}
}
uint16_t CommandArguments::number_of_blocks() {
uint16_t CommandState::number_of_blocks() {
switch(data_.size()) {
default: return 0;
case 6:

View File

@ -11,17 +11,18 @@
#include "SCSI.hpp"
#include <functional>
namespace SCSI {
namespace Target {
/*!
Encapsulates the arguments supplied for a target SCSI command during
the command phase. An instance of TargetCommandArguments will be
supplied to the target whenever a function is called.
the command phase plus any other data read since then.
*/
class CommandArguments {
class CommandState {
public:
CommandArguments(const std::vector<uint8_t> &data);
CommandState(const std::vector<uint8_t> &data);
uint32_t address();
uint16_t number_of_blocks();
@ -30,6 +31,56 @@ class CommandArguments {
const std::vector<uint8_t> &data_;
};
/*!
A Responder is supplied both (i) to the initial call-in to an Executor; and
(ii) to all continuations provided by that Executor. It allows the next
set of bus interactions to be dictated.
*/
struct Responder {
using continuation = std::function<void(const CommandState &, Responder &)>;
enum class Status {
Good = 0x00,
CheckCondition = 0x02,
ConditionMet = 0x04,
Busy = 0x08,
Intermediate = 0x10,
IntermediateConditionMet = 0x14,
ReservationConflict = 0x18,
CommandTerminated = 0x22,
TaskSetFull = 0x28,
ACAActive = 0x30,
TaskAborted = 0x40
};
enum class Message {
CommandComplete = 0x00
};
/*!
Causes the SCSI device to send @c data to the initiator and
call @c next when done.
*/
virtual void send_data(std::vector<uint8_t> &&data, continuation next) = 0;
/*!
Causes the SCSI device to receive @c length bytes from the initiator and
call @c next when done. The bytes will be accessible via the CommandInput object.
*/
virtual void receive_data(size_t length, continuation next) = 0;
/*!
Communicates the supplied status to the initiator.
*/
virtual void send_status(Status, continuation next) = 0;
/*!
Communicates the supplied message to the initiator.
*/
virtual void send_message(Message, continuation next) = 0;
/*!
Ends the SCSI command.
*/
virtual void end_command() = 0;
};
/*!
Executors contain device-specific logic; when the target has completed
the command phase it will call the appropriate method on its executor,
@ -41,32 +92,31 @@ class CommandArguments {
*/
struct Executor {
/* Group 0 commands. */
bool test_unit_ready(const CommandArguments &) { return false; }
bool rezero_unit(const CommandArguments &) { return false; }
bool request_sense(const CommandArguments &) { return false; }
bool format_unit(const CommandArguments &) { return false; }
bool seek(const CommandArguments &) { return false; }
bool reserve_unit(const CommandArguments &) { return false; }
bool release_unit(const CommandArguments &) { return false; }
bool read_diagnostic(const CommandArguments &) { return false; }
bool write_diagnostic(const CommandArguments &) { return false; }
bool inquiry(const CommandArguments &) { return false; }
bool test_unit_ready(const CommandState &, Responder &) { return false; }
bool rezero_unit(const CommandState &, Responder &) { return false; }
bool request_sense(const CommandState &, Responder &) { return false; }
bool format_unit(const CommandState &, Responder &) { return false; }
bool seek(const CommandState &, Responder &) { return false; }
bool reserve_unit(const CommandState &, Responder &) { return false; }
bool release_unit(const CommandState &, Responder &) { return false; }
bool read_diagnostic(const CommandState &, Responder &) { return false; }
bool write_diagnostic(const CommandState &, Responder &) { return false; }
bool inquiry(const CommandState &, Responder &) { return false; }
/* Group 0/1 commands. */
bool read(const CommandArguments &) { return false; }
bool write(const CommandArguments &) { return false; }
bool read(const CommandState &, Responder &) { return false; }
bool write(const CommandState &, Responder &) { return false; }
/* Group 1 commands. */
bool read_capacity(const CommandArguments &) { return false; }
bool write_and_verify(const CommandArguments &) { return false; }
bool verify(const CommandArguments &) { return false; }
bool search_data_equal(const CommandArguments &) { return false; }
bool search_data_high(const CommandArguments &) { return false; }
bool search_data_low(const CommandArguments &) { return false; }
bool read_capacity(const CommandState &, Responder &) { return false; }
bool write_and_verify(const CommandState &, Responder &) { return false; }
bool verify(const CommandState &, Responder &) { return false; }
bool search_data_equal(const CommandState &, Responder &) { return false; }
bool search_data_high(const CommandState &, Responder &) { return false; }
bool search_data_low(const CommandState &, Responder &) { return false; }
/* Group 5 commands. */
bool set_block_limits(const CommandArguments &) { return false; }
void reset_block_limits(const CommandArguments &) {}
bool set_block_limits(const CommandState &, Responder &) { return false; }
};
/*!
@ -74,7 +124,7 @@ struct Executor {
receive and respond to commands. Specific targets should be implemented
as Executors.
*/
template <typename Executor> class Target: public Bus::Observer {
template <typename Executor> class Target: public Bus::Observer, public Responder {
public:
/*!
Instantiates a target attached to @c bus,
@ -87,8 +137,17 @@ template <typename Executor> class Target: public Bus::Observer {
Executor executor;
private:
// Bus::Observer.
void scsi_bus_did_change(Bus *, BusState new_state) final;
// Responder
void send_data(std::vector<uint8_t> &&data, continuation next) final;
void receive_data(size_t length, continuation next) final;
void send_status(Status, continuation next) final;
void send_message(Message, continuation next) final;
void end_command() final;
// Instance storage.
Bus &bus_;
const BusState scsi_id_mask_;
const size_t scsi_bus_device_id_;

View File

@ -102,7 +102,7 @@ template <typename Executor> void Target<Executor>::begin_command(uint8_t first_
template <typename Executor> bool Target<Executor>::dispatch_command() {
CommandArguments arguments(command_);
CommandState arguments(command_);
#define G0(x) x
#define G1(x) (0x20|x)
@ -111,29 +111,29 @@ template <typename Executor> bool Target<Executor>::dispatch_command() {
switch(command_[0]) {
default: return false;
case G0(0x00): return executor.test_unit_ready(arguments);
case G0(0x01): return executor.rezero_unit(arguments);
case G0(0x03): return executor.request_sense(arguments);
case G0(0x04): return executor.format_unit(arguments);
case G0(0x08): return executor.read(arguments);
case G0(0x0a): return executor.write(arguments);
case G0(0x0b): return executor.seek(arguments);
case G0(0x16): return executor.reserve_unit(arguments);
case G0(0x17): return executor.release_unit(arguments);
case G0(0x1c): return executor.read_diagnostic(arguments);
case G0(0x1d): return executor.write_diagnostic(arguments);
case G0(0x12): return executor.inquiry(arguments);
case G0(0x00): return executor.test_unit_ready(arguments, *this);
case G0(0x01): return executor.rezero_unit(arguments, *this);
case G0(0x03): return executor.request_sense(arguments, *this);
case G0(0x04): return executor.format_unit(arguments, *this);
case G0(0x08): return executor.read(arguments, *this);
case G0(0x0a): return executor.write(arguments, *this);
case G0(0x0b): return executor.seek(arguments, *this);
case G0(0x16): return executor.reserve_unit(arguments, *this);
case G0(0x17): return executor.release_unit(arguments, *this);
case G0(0x1c): return executor.read_diagnostic(arguments, *this);
case G0(0x1d): return executor.write_diagnostic(arguments, *this);
case G0(0x12): return executor.inquiry(arguments, *this);
case G1(0x05): return executor.read_capacity(arguments);
case G1(0x08): return executor.read(arguments);
case G1(0x0a): return executor.write(arguments);
case G1(0x0e): return executor.write_and_verify(arguments);
case G1(0x0f): return executor.verify(arguments);
case G1(0x11): return executor.search_data_equal(arguments);
case G1(0x10): return executor.search_data_high(arguments);
case G1(0x12): return executor.search_data_low(arguments);
case G1(0x05): return executor.read_capacity(arguments, *this);
case G1(0x08): return executor.read(arguments, *this);
case G1(0x0a): return executor.write(arguments, *this);
case G1(0x0e): return executor.write_and_verify(arguments, *this);
case G1(0x0f): return executor.verify(arguments, *this);
case G1(0x11): return executor.search_data_equal(arguments, *this);
case G1(0x10): return executor.search_data_high(arguments, *this);
case G1(0x12): return executor.search_data_low(arguments, *this);
case G5(0x09): return executor.set_block_limits(arguments);
case G5(0x09): return executor.set_block_limits(arguments, *this);
}
#undef G0
@ -142,3 +142,9 @@ template <typename Executor> bool Target<Executor>::dispatch_command() {
return false;
}
template <typename Executor> void Target<Executor>::send_data(std::vector<uint8_t> &&data, continuation next) {}
template <typename Executor> void Target<Executor>::receive_data(size_t length, continuation next) {}
template <typename Executor> void Target<Executor>::send_status(Status, continuation next) {}
template <typename Executor> void Target<Executor>::send_message(Message, continuation next) {}
template <typename Executor> void Target<Executor>::end_command() {}