mirror of
https://github.com/TomHarte/CLK.git
synced 2025-03-08 16:29:45 +00:00
Sketches out further SCSI infrastructure.
This commit is contained in:
parent
252650808d
commit
bb1a0a0b76
@ -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:
|
||||
|
@ -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_;
|
||||
|
@ -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() {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user