mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +00:00
Simplifies INQUIRY for future targets; implements enough of SENSE MODE to advance.
The HD SC setup utility is now looking to read buffer.
This commit is contained in:
parent
00ce7f8ae0
commit
6fc5b4e825
@ -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<uint8_t> 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.
|
||||
}
|
||||
|
@ -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<Storage::MassStorage::MassStorageDevice> device_;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "SCSI.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
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<uint8_t> &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<uint8_t> 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<char *>(&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<uint8_t> 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<uint8_t *>(stpncpy(reinterpret_cast<char *>(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; }
|
||||
|
@ -191,6 +191,7 @@ template <typename Executor> bool Target<Executor>::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);
|
||||
|
Loading…
Reference in New Issue
Block a user