From 6fc5b4e8253c525715ac3f7f5d47e0f7a52051a4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 Sep 2019 21:59:56 -0400 Subject: [PATCH] Simplifies INQUIRY for future targets; implements enough of SENSE MODE to advance. The HD SC setup utility is now looking to read buffer. --- .../MassStorage/SCSI/DirectAccessDevice.cpp | 23 +-- .../MassStorage/SCSI/DirectAccessDevice.hpp | 2 +- Storage/MassStorage/SCSI/Target.cpp | 13 ++ Storage/MassStorage/SCSI/Target.hpp | 140 +++++++++++++++++- .../MassStorage/SCSI/TargetImplementation.hpp | 1 + 5 files changed, 156 insertions(+), 23 deletions(-) diff --git a/Storage/MassStorage/SCSI/DirectAccessDevice.cpp b/Storage/MassStorage/SCSI/DirectAccessDevice.cpp index bfbd382a0..7d391f59e 100644 --- a/Storage/MassStorage/SCSI/DirectAccessDevice.cpp +++ b/Storage/MassStorage/SCSI/DirectAccessDevice.cpp @@ -25,25 +25,6 @@ bool DirectAccessDevice::read(const Target::CommandState &state, Target::Respond return true; } -bool DirectAccessDevice::inquiry(const Target::CommandState &state, Target::Responder &responder) { - if(!device_) return false; - - std::vector response = { - 0x00, /* Peripheral device type: directly addressible. */ - 0x00, /* Non-removeable (0x80 = removeable). */ - 0x00, /* Version: does not claim conformance to any standard. */ - 0x00, /* Response data format: 0 for SCSI-1? */ - 0x00, /* Additional length. */ - }; - - const auto allocated_bytes = state.allocated_inquiry_bytes(); - if(allocated_bytes < response.size()) { - response.resize(allocated_bytes); - } - - responder.send_data(std::move(response), [] (const Target::CommandState &state, Target::Responder &responder) { - responder.terminate_command(Target::Responder::Status::Good); - }); - - return true; +Target::Executor::Inquiry DirectAccessDevice::inquiry_values() { + return Inquiry("Apple", "ProFile", "1"); // All just guesses. } diff --git a/Storage/MassStorage/SCSI/DirectAccessDevice.hpp b/Storage/MassStorage/SCSI/DirectAccessDevice.hpp index fa755eb9f..192955c32 100644 --- a/Storage/MassStorage/SCSI/DirectAccessDevice.hpp +++ b/Storage/MassStorage/SCSI/DirectAccessDevice.hpp @@ -26,7 +26,7 @@ class DirectAccessDevice: public Target::Executor { /* SCSI commands. */ bool read(const Target::CommandState &, Target::Responder &); - bool inquiry(const Target::CommandState &, Target::Responder &); + Inquiry inquiry_values(); private: std::shared_ptr device_; diff --git a/Storage/MassStorage/SCSI/Target.cpp b/Storage/MassStorage/SCSI/Target.cpp index d901e50e1..f3fbbc329 100644 --- a/Storage/MassStorage/SCSI/Target.cpp +++ b/Storage/MassStorage/SCSI/Target.cpp @@ -41,5 +41,18 @@ uint16_t CommandState::number_of_blocks() const { } size_t CommandState::allocated_inquiry_bytes() const { + // 0 means 256 bytes allocated for inquiry. return size_t(((data_[4] - 1) & 0xff) + 1); } + +CommandState::ModeSense CommandState::mode_sense_specs() const { + CommandState::ModeSense specs; + + specs.exclude_block_descriptors = (data_[1] & 0x08); + specs.page_control_values = ModeSense::PageControlValues(data_[2] >> 5); + specs.page_code = data_[2] & 0x3f; + specs.subpage_code = data_[3]; + specs.allocated_bytes = number_of_blocks(); + + return specs; +} diff --git a/Storage/MassStorage/SCSI/Target.hpp b/Storage/MassStorage/SCSI/Target.hpp index f8e60b1cc..e514525cb 100644 --- a/Storage/MassStorage/SCSI/Target.hpp +++ b/Storage/MassStorage/SCSI/Target.hpp @@ -11,6 +11,7 @@ #include "SCSI.hpp" +#include #include namespace SCSI { @@ -31,6 +32,21 @@ class CommandState { // For inquiry commands. size_t allocated_inquiry_bytes() const; + // For mode sense commands. + struct ModeSense { + bool exclude_block_descriptors = false; + enum class PageControlValues { + Current = 0, + Changeable = 1, + Default = 2, + Saved = 3 + } page_control_values = PageControlValues::Current; + uint8_t page_code; + uint8_t subpage_code; + uint16_t allocated_bytes; + }; + ModeSense mode_sense_specs() const; + private: const std::vector &data_; }; @@ -119,7 +135,129 @@ struct Executor { 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; } + + /// Mode sense: the default implementation will call into the appropriate + /// strucutred getter. + bool mode_sense(const CommandState &state, Responder &responder) { + const auto specs = state.mode_sense_specs(); + std::vector response = { + specs.page_code, + uint8_t(specs.allocated_bytes) + }; + switch(specs.page_code) { + default: + printf("Unknown mode sense page code %02x\n", specs.page_code); + response.resize(specs.allocated_bytes); + break; + + case 0x30: + response.resize(34); + strcpy(reinterpret_cast(&response[14]), "APPLE COMPUTER, INC"); // This seems to be required to satisfy the Apple HD SC Utility. + break; + } + + if(specs.allocated_bytes < response.size()) { + response.resize(specs.allocated_bytes); + } + responder.send_data(std::move(response), [] (const Target::CommandState &state, Target::Responder &responder) { + responder.terminate_command(Target::Responder::Status::Good); + }); + + return true; + } + + /// Inquiry: the default implementation will call the structured version and + /// package appropriately. + struct Inquiry { + enum class DeviceType { + DirectAccess = 0, + SequentialAccess = 1, + Printer = 2, + Processor = 3, + WriteOnceMultipleRead = 4, + ReadOnlyDirectAccess = 5, + Scanner = 6, + OpticalMemory = 7, + MediumChanger = 8, + Communications = 9, + } device_type = DeviceType::DirectAccess; + bool is_removeable = false; + uint8_t iso_standard = 0, ecma_standard = 0, ansi_standard = 0; + bool supports_asynchronous_events = false; + bool supports_terminate_io_process = false; + bool supports_relative_addressing = false; + bool supports_synchronous_transfer = true; + bool supports_linked_commands = false; + bool supports_command_queing = false; + bool supports_soft_reset = false; + char vendor_identifier[9] = ""; + char product_identifier[17] = ""; + char product_revision_level[5] = ""; + + Inquiry(const char *vendor, const char *product, const char *revision) { + assert(strlen(vendor) <= 8); + assert(strlen(product) <= 16); + assert(strlen(revision) <= 4); + strcpy(vendor_identifier, vendor); + strcpy(product_identifier, product); + strcpy(product_revision_level, revision); + } + Inquiry() = default; + }; + Inquiry inquiry_values() { + return Inquiry(); + } + bool inquiry(const CommandState &state, Responder &responder) { + const Inquiry inq = inquiry_values(); + + // Set up the easy fields. + std::vector response = { + uint8_t(inq.device_type), + uint8_t(inq.is_removeable ? 0x80 : 0x00), + uint8_t((inq.iso_standard << 5) | (inq.ecma_standard << 3) | (inq.ansi_standard)), + uint8_t((inq.supports_asynchronous_events ? 0x80 : 0x00) | (inq.supports_terminate_io_process ? 0x40 : 0x00) | 0x02), + 32, /* Additional length: 36 - 4. */ + 0x00, /* Reserved. */ + 0x00, /* Reserved. */ + uint8_t( + (inq.supports_relative_addressing ? 0x80 : 0x00) | + /* b6: supports 32-bit data; b5: supports 16-bit data. */ + (inq.supports_synchronous_transfer ? 0x10 : 0x00) | + (inq.supports_linked_commands ? 0x08 : 0x00) | + /* b3: reserved. */ + (inq.supports_command_queing ? 0x02 : 0x00) | + (inq.supports_soft_reset ? 0x01 : 0x00) + ), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Space for the vendor ID. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Space for the product ID. */ + 0x00, 0x00, 0x00, 0x00 /* Space for the revision level. */ + }; + + auto copy_string = [] (uint8_t *destination, const char *source, size_t length) -> void { + // Copy as much of the string as will fit, and pad with spaces. + uint8_t *end = reinterpret_cast(stpncpy(reinterpret_cast(destination), source, length)); + while(end < destination + length) { + *end = ' '; + ++end; + } + }; + copy_string(&response[8], inq.vendor_identifier, 8); + copy_string(&response[16], inq.product_identifier, 16); + copy_string(&response[32], inq.product_revision_level, 4); + + // Truncate if requested. + const auto allocated_bytes = state.allocated_inquiry_bytes(); + if(allocated_bytes < response.size()) { + response.resize(allocated_bytes); + } + + responder.send_data(std::move(response), [] (const Target::CommandState &state, Target::Responder &responder) { + responder.terminate_command(Target::Responder::Status::Good); + }); + + return true; + } /* Group 0/1 commands. */ bool read(const CommandState &, Responder &) { return false; } diff --git a/Storage/MassStorage/SCSI/TargetImplementation.hpp b/Storage/MassStorage/SCSI/TargetImplementation.hpp index 8726bf948..0d75bcce8 100644 --- a/Storage/MassStorage/SCSI/TargetImplementation.hpp +++ b/Storage/MassStorage/SCSI/TargetImplementation.hpp @@ -191,6 +191,7 @@ template bool Target::dispatch_command() { 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 G0(0x1a): return executor_.mode_sense(arguments, *this); case G1(0x05): return executor_.read_capacity(arguments, *this); case G1(0x08): return executor_.read(arguments, *this);