mirror of
https://github.com/akuker/RASCSI.git
synced 2025-06-12 23:38:02 +00:00
* Use foreach * Renaming * Revert "Renaming" This reverts commitb0554b9c0a
. * Manpage updates * Removed obsolete assertions * Replaced QWORD by uint64_t and removed respective typedef * Removed LPCSTR typedef * Removed LPCTSTR typedef * Removed LPTSTR typedef * Renamed SCSI command interface classes * Renamed xm6.h to rascsi.h * Moved interface classes to new interfaces subfolder * Added include * Fixed compilation issues of 64 bit Ubuntu * Renaming * Sort block sizes * protobuf interface description update * Fixed handling for sector size for non-disk devices * Fixed typo * Fixed comment * Translate code commends into English, removing redundant ones (#214) * Comment update * For other bridge interfaces than eth0 IP address and netmask can be provided * Added special rule for testing on x86 PCs * Translated code comments into English, removing some redundant ones in the process, plus fixing typos (#215) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * Comment update * Removed unused typedefs * Added special rule for testing on x86 PCs * Comment update * Comment update * Updated capacity calculation * Updated protobuf interface to signal parameter support * Simplified protobuf interface * Updated rasctl server info output * Updated logging * protobuf interface has to return block only if it is configurable * Updated block size handling * Improved error message if ID is already in use * Removed typedef * Renamed protobuf interface method * Renaming * Use protobuf::Messsge instead of protobuf::MessageLite * default_image_folder cannot be an empty string, removed obsolete check * Logging update * Made some error messages more concise * Removed magic constant * Updated error message * Comment update * Names of removable media drives must be constant and not contain the capacity * Improved DeviceFactory error handling * More error handing improvements * Interface comment update * Pass interface list to ctapdriver when creating bridge * Moved initialization code * Updated rasctl server information output * Improved handling of MO capacities * Renaming * Comment update * Reject inserting a medium when there is already a medium present (eject first) * Save list of files in use before dry-run * Updated rasctl server info message * Comment update * Fixed typo * Cleaned list handling * Sort devices list by ID *and unit* * Improved block size check * Fixed issue with missing method in old Raspberry Pi OS protobuf implementation * Updated error message * Improve and fix bugs with saving&loading configuration files for rascsi-web (#218) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Removed unused structures, code and type cleanup * Use unscoped enums for commands * SASI Format opcode is 0x06, not 0x04 (see comment in code) * Removed duplicate command * Code review, data type updates * Data type updated, use #pragma once * Logging update * Renaming * Renaming * Removed duplicate code * Renaming * Refactoring * Removed TODO * Updated logging * Comment update * Comment update * Updated GetEventStatusNotification * Removed goto * Options -h and -v do not require to be the root user (fixes issue #166) * Updated error messages and exception handling * Added number of supported LUNs to protobuf interface * Updated list handling of protobuf interface * Comment update * Improved error handling * Added missing return statement * Allow empty device list * Fixed unnecessary detach_all() when config file isn't read (#221) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Run detach_all() only after succeeding to open a file for reading * Protecting/unprotecing a non-ready medium is considered not possible * Updated error message * Extract detaching all devices, add parameter list support * Comment update * Fixed typos * Restore files in use if dry-run fails * Feature configurable reserved id for rascsi-web (#223) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Run detach_all() only after succeeding to open a file for reading * Make the reserved SCSI id configurable through an argument to start.sh; make the rascsi-web service reserve 7 by default to maintain current behavior. * Make it possible to reserve multiple scsi ids in the web ui * Added support for reserved IDs * rasctl output update * Re-ordered logging * Logging update * Make use of the newly introduced 'rasctl -r' to have the webui reserve ids on the backend side upon startup (#224) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Run detach_all() only after succeeding to open a file for reading * Make use of the new 'rasctl -r' command to reserve IDs on the backend side as well. * Updated string to integer conversions * Improved string to integer conversion * Move string to integer conversion to rasutil * Removed unused variable * Fixed detach, which did not remove the filename from the filenames set * Re-added folder to gitignore * Set/Display patch version * Fix issue where reserved ids were not reserved again when restarting rascsi-service from within the web ui (#226) * Translate code commends into English, removing redundant ones * - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h - Removed some redundant comments where the context is obvious from the code - Fixed a few typos and mistakes * - Store only file path and name to configuration csv - Strip known non-file path strings when reading configuration csv (backwards compatibility) - Validate SCSI ID before attempting to attach a device * Add comment and TODO * Partial translation of cfilesystem.h * Move csv read/write logic into file_cmd.py * Load default.csv on rascsi-web startup * Add rudimentary error handling to config loading/saving * Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods. * Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids * Fix error handling when failing to open a csv file for read or write * Run detach_all() only after succeeding to open a file for reading * Make use of the new 'rasctl -r' command to reserve IDs on the backend side as well. * Make sure reserved SCSI IDs gets reserved again when restarting rascsi-service from within the web ui * Updated interface comment * Accept daynaport as legacy type * Fixed typo * Remove file from the list of files in use when ejected with a SCSI command * Check for attached device for INSERT, EJECT, PROTECT, UNPROTECT * Fixed error handling * Fixed filepath handling * Added more device shortcuts to rasctl * Fixed function declaration * Extraced ATTACH and DETACH * Extracted INSERT * Simplified ProcessCmd * Comment update * Fixed memory leak * Log information on whether a new device is protected or read-only * Updated errro message * Updated error message * Initialize private fields * Updated rasctl help message * Added DEVICE_INFO to protobuf interface * Improved error handling * DEVICE_INFO supports device list * Updated server info handling * Unified result handling with oneof, all commands now return PbResult * A result can always return a message string * Fixed typo * Simplified sending of commands * Improved error handling * Removed unused code * Updated error handling * Code cleanup * Comment update * Updated logging * Updated error handling * Updated handling of removed status for devices without image file support * Comment update * Fixed typo * Updated logging * Updated parameter handling * Updated setting default interfaces * Revert "Updated setting default interfaces" This reverts commit210abc775d
. * Revert "Updated parameter handling" This reverts commit35302addd5
. * rascsi supports reserving IDs * Updated help message * Replaced BOOL by bool * Logging update * Logging update * Added default parameters to device properties * Return parameters a device was set up with * Improved device initialization * Updated default parameter handling * Updated default parameter handling * Fixed typo * Comment updates * Comment update * Manage default parameters in the respective device * Do not pass empty parameter string * Added supports_params flag * Made comparisons more consistent * Updated error handling * Updated exception handling * Renaming * Comment update * NEC sectors size must be 512 bytes * Updated logging * Updated vendor name handling * Updated handling of media loading/unloading * Added stoppable property and stopped status * Made MO stoppable * Removed duplicate code * Removed duplicate code * Copy read-only property * Renaming * Removed duplicate code, added START/STOP * Improved default parameter handling * Updated load/eject handling * Logging update * Fixed typo * Verified START/STOP UNIT * Updated logging * Updated status handling * Updated status handling * More status handling updates * Logging update * Made instance fields local variables * Made disk_t private * Made some data structures private * Fixed ARM compile issue * Fixed ctapdriver initialization issue * Reset read-only status when opening an image file * Made logging more consistent * Updated log level * Log load/eject on error level for testing * Revert "Log load/eject on error level for testing" This reverts commitd35a15ea8e
. * Assume drive is not ready after having been stopped * Updated status handling * Fixed typo * Rebuild manpage * Fixed issue #234 (MODE SENSE (10) returns wrong mode parameter header) * Removed unused code * Enum data type update * Removed duplicate range check * Removed duplicate code * Removed more duplicate code * Logging update * SCCD sector size was not meant to be configurable * Updated configurable sector size properties * Removed assertion * Improved error handling * Updated error handling * Re-added special error handling only relevant for SASI * Added TODOs * Comment update * Added override modifier * Removed obsolete debug flag (related code was not called) * Comment and logging updates * Removed obsolete try/catch * Revert "Removed obsolete try/catch" This reverts commit39ca12d8b1
. * Comment update * Removed duplicate code * Updated error messages, use more foreach loops * Updated logging * Logging update * README update * Added block_count * Evaluate block size when inserting a media * rasctl display capacity if available * Info message update * Added missing product name to NEC vital product data * MO block size depends on capacity only * Extended property/status display * Property display update * Updated error handling * (Doc only changes) Fix typos and add clarification that SASI is used on Unix workstations Co-authored-by: Daniel Markstedt <markstedt@gmail.com> Co-authored-by: Tony Kuker <akuker@gmail.com>
1262 lines
28 KiB
C++
1262 lines
28 KiB
C++
//---------------------------------------------------------------------------
|
||
//
|
||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||
// for Raspberry Pi
|
||
//
|
||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||
// Copyright (C) 2014-2020 GIMONS
|
||
// Copyright (C) akuker
|
||
//
|
||
// Licensed under the BSD 3-Clause License.
|
||
// See LICENSE file in the project root folder.
|
||
//
|
||
// [ SASI device controller ]
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
#include "controllers/sasidev_ctrl.h"
|
||
#include "filepath.h"
|
||
#include "gpiobus.h"
|
||
#include "devices/scsi_host_bridge.h"
|
||
#include "devices/scsi_daynaport.h"
|
||
#include <sstream>
|
||
|
||
//===========================================================================
|
||
//
|
||
// SASI Device
|
||
//
|
||
//===========================================================================
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Constructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
SASIDEV::SASIDEV()
|
||
{
|
||
// Work initialization
|
||
ctrl.phase = BUS::busfree;
|
||
ctrl.m_scsi_id = UNKNOWN_SCSI_ID;
|
||
ctrl.bus = NULL;
|
||
memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd));
|
||
ctrl.status = 0x00;
|
||
ctrl.message = 0x00;
|
||
ctrl.execstart = 0;
|
||
// The initial buffer size will default to either the default buffer size OR
|
||
// the size of an Ethernet message, whichever is larger.
|
||
ctrl.bufsize = std::max(DEFAULT_BUFFER_SIZE, ETH_FRAME_LEN + 16 + ETH_FCS_LEN);
|
||
ctrl.buffer = (BYTE *)malloc(ctrl.bufsize);
|
||
memset(ctrl.buffer, 0x00, ctrl.bufsize);
|
||
ctrl.blocks = 0;
|
||
ctrl.next = 0;
|
||
ctrl.offset = 0;
|
||
ctrl.length = 0;
|
||
|
||
// Logical unit initialization
|
||
for (int i = 0; i < UnitMax; i++) {
|
||
ctrl.unit[i] = NULL;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Destructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
SASIDEV::~SASIDEV()
|
||
{
|
||
// Free the buffer
|
||
if (ctrl.buffer) {
|
||
free(ctrl.buffer);
|
||
ctrl.buffer = NULL;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Device reset
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Reset()
|
||
{
|
||
// Work initialization
|
||
memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd));
|
||
ctrl.phase = BUS::busfree;
|
||
ctrl.status = 0x00;
|
||
ctrl.message = 0x00;
|
||
ctrl.execstart = 0;
|
||
memset(ctrl.buffer, 0x00, ctrl.bufsize);
|
||
ctrl.blocks = 0;
|
||
ctrl.next = 0;
|
||
ctrl.offset = 0;
|
||
ctrl.length = 0;
|
||
|
||
// Unit initialization
|
||
for (int i = 0; i < UnitMax; i++) {
|
||
if (ctrl.unit[i]) {
|
||
ctrl.unit[i]->Reset();
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Connect the controller
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Connect(int id, BUS *bus)
|
||
{
|
||
ctrl.m_scsi_id = id;
|
||
ctrl.bus = bus;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Set the logical unit
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::SetUnit(int no, Disk *dev)
|
||
{
|
||
ASSERT(no < UnitMax);
|
||
|
||
ctrl.unit[no] = dev;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Check to see if this has a valid LUN
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
bool SASIDEV::HasUnit()
|
||
{
|
||
for (int i = 0; i < UnitMax; i++) {
|
||
if (ctrl.unit[i]) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Run
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BUS::phase_t SASIDEV::Process()
|
||
{
|
||
// Do nothing if not connected
|
||
if (ctrl.m_scsi_id < 0 || ctrl.bus == NULL) {
|
||
return ctrl.phase;
|
||
}
|
||
|
||
// Get bus information
|
||
((GPIOBUS*)ctrl.bus)->Aquire();
|
||
|
||
// For the monitor tool, we shouldn't need to reset. We're just logging information
|
||
// Reset
|
||
if (ctrl.bus->GetRST()) {
|
||
LOGINFO("RESET signal received");
|
||
|
||
// Reset the controller
|
||
Reset();
|
||
|
||
// Reset the bus
|
||
ctrl.bus->Reset();
|
||
return ctrl.phase;
|
||
}
|
||
|
||
// Phase processing
|
||
switch (ctrl.phase) {
|
||
// Bus free
|
||
case BUS::busfree:
|
||
BusFree();
|
||
break;
|
||
|
||
// Selection
|
||
case BUS::selection:
|
||
Selection();
|
||
break;
|
||
|
||
// Data out (MCI=000)
|
||
case BUS::dataout:
|
||
DataOut();
|
||
break;
|
||
|
||
// Data in (MCI=001)
|
||
case BUS::datain:
|
||
DataIn();
|
||
break;
|
||
|
||
// Command (MCI=010)
|
||
case BUS::command:
|
||
Command();
|
||
break;
|
||
|
||
// Status (MCI=011)
|
||
case BUS::status:
|
||
Status();
|
||
break;
|
||
|
||
// Msg in (MCI=111)
|
||
case BUS::msgin:
|
||
MsgIn();
|
||
break;
|
||
|
||
// Other
|
||
default:
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
return ctrl.phase;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Bus free phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::BusFree()
|
||
{
|
||
// Phase change
|
||
if (ctrl.phase != BUS::busfree) {
|
||
LOGTRACE("%s Bus free phase", __PRETTY_FUNCTION__);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::busfree;
|
||
|
||
// Set Signal lines
|
||
ctrl.bus->SetREQ(FALSE);
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(FALSE);
|
||
ctrl.bus->SetIO(FALSE);
|
||
ctrl.bus->SetBSY(false);
|
||
|
||
// Initialize status and message
|
||
ctrl.status = 0x00;
|
||
ctrl.message = 0x00;
|
||
return;
|
||
}
|
||
|
||
// Move to selection phase
|
||
if (ctrl.bus->GetSEL() && !ctrl.bus->GetBSY()) {
|
||
Selection();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Selection phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Selection()
|
||
{
|
||
// Phase change
|
||
if (ctrl.phase != BUS::selection) {
|
||
// Invalid if IDs do not match
|
||
DWORD id = 1 << ctrl.m_scsi_id;
|
||
if ((ctrl.bus->GetDAT() & id) == 0) {
|
||
return;
|
||
}
|
||
|
||
// Return if there is no valid LUN
|
||
if (!HasUnit()) {
|
||
return;
|
||
}
|
||
|
||
LOGTRACE("%s Selection Phase ID=%d (with device)", __PRETTY_FUNCTION__, (int)ctrl.m_scsi_id);
|
||
|
||
// Phase change
|
||
ctrl.phase = BUS::selection;
|
||
|
||
// Raiase BSY and respond
|
||
ctrl.bus->SetBSY(true);
|
||
return;
|
||
}
|
||
|
||
// Command phase shifts when selection is completed
|
||
if (!ctrl.bus->GetSEL() && ctrl.bus->GetBSY()) {
|
||
Command();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Command phase (used by SASI and SCSI)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Command()
|
||
{
|
||
// Phase change
|
||
if (ctrl.phase != BUS::command) {
|
||
LOGTRACE("%s Command Phase", __PRETTY_FUNCTION__);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::command;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(TRUE);
|
||
ctrl.bus->SetIO(FALSE);
|
||
|
||
// Data transfer is 6 bytes x 1 block
|
||
ctrl.offset = 0;
|
||
ctrl.length = 6;
|
||
ctrl.blocks = 1;
|
||
|
||
// Command reception handshake (10 bytes are automatically received at the first command)
|
||
int count = ctrl.bus->CommandHandShake(ctrl.buffer);
|
||
|
||
// If no byte can be received move to the status phase
|
||
if (count == 0) {
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
ctrl.length = GPIOBUS::GetCommandByteCount(ctrl.buffer[0]);
|
||
|
||
// If not able to receive all, move to the status phase
|
||
if (count != (int)ctrl.length) {
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Command data transfer
|
||
for (int i = 0; i < (int)ctrl.length; i++) {
|
||
ctrl.cmd[i] = (DWORD)ctrl.buffer[i];
|
||
}
|
||
|
||
// Clear length and block
|
||
ctrl.length = 0;
|
||
ctrl.blocks = 0;
|
||
|
||
// Execution Phase
|
||
Execute();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Execution Phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Execute()
|
||
{
|
||
LOGTRACE("%s Execution Phase Command %02X", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0]);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::execute;
|
||
|
||
// Initialization for data transfer
|
||
ctrl.offset = 0;
|
||
ctrl.blocks = 1;
|
||
ctrl.execstart = SysTimer::GetTimerLow();
|
||
|
||
// Process by command
|
||
// TODO This code does not belong here. Each device type needs such a dispatcher, which the controller has to call.
|
||
switch ((SASIDEV::sasi_command)ctrl.cmd[0]) {
|
||
// TEST UNIT READY
|
||
case SASIDEV::eCmdTestUnitReady:
|
||
CmdTestUnitReady();
|
||
return;
|
||
|
||
// REZERO UNIT
|
||
case SASIDEV::eCmdRezero:
|
||
CmdRezero();
|
||
return;
|
||
|
||
// REQUEST SENSE
|
||
case SASIDEV::eCmdRequestSense:
|
||
CmdRequestSense();
|
||
return;
|
||
|
||
// FORMAT
|
||
case SASIDEV::eCmdFormat:
|
||
CmdFormat();
|
||
return;
|
||
|
||
// REASSIGN BLOCKS
|
||
case SASIDEV::eCmdReassign:
|
||
CmdReassignBlocks();
|
||
return;
|
||
|
||
// READ(6)
|
||
case SASIDEV::eCmdRead6:
|
||
CmdRead6();
|
||
return;
|
||
|
||
// WRITE(6)
|
||
case SASIDEV::eCmdWrite6:
|
||
CmdWrite6();
|
||
return;
|
||
|
||
// SEEK(6)
|
||
case SASIDEV::eCmdSeek6:
|
||
CmdSeek6();
|
||
return;
|
||
|
||
// ASSIGN (SASI only)
|
||
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
|
||
// leaving it here for now....
|
||
case SASIDEV::eCmdSasiCmdAssign:
|
||
CmdAssign();
|
||
return;
|
||
|
||
// RESERVE UNIT(16)
|
||
case SASIDEV::eCmdReserve6:
|
||
CmdReserveUnit();
|
||
return;
|
||
|
||
// RELEASE UNIT(17)
|
||
case eCmdRelease6:
|
||
CmdReleaseUnit();
|
||
return;
|
||
|
||
// SPECIFY (SASI only)
|
||
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
|
||
// leaving it here for now....
|
||
case SASIDEV::eCmdInvalid:
|
||
CmdSpecify();
|
||
return;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
// Unsupported command
|
||
LOGWARN("%s ID %d received unsupported command: $%02X", __PRETTY_FUNCTION__, GetSCSIID(), (BYTE)ctrl.cmd[0]);
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
if (ctrl.unit[lun]) {
|
||
// Command processing on drive
|
||
ctrl.unit[lun]->SetStatusCode(STATUS_INVALIDCMD);
|
||
}
|
||
|
||
// Failure (Error)
|
||
Error();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Status phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Status()
|
||
{
|
||
// Phase change
|
||
if (ctrl.phase != BUS::status) {
|
||
// Minimum execution time
|
||
if (ctrl.execstart > 0) {
|
||
DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||
DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||
if (time < min_exec_time) {
|
||
SysTimer::SleepUsec(min_exec_time - time);
|
||
}
|
||
ctrl.execstart = 0;
|
||
} else {
|
||
SysTimer::SleepUsec(5);
|
||
}
|
||
|
||
LOGTRACE("%s Status phase", __PRETTY_FUNCTION__);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::status;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(TRUE);
|
||
ctrl.bus->SetIO(TRUE);
|
||
|
||
// Data transfer is 1 byte x 1 block
|
||
ctrl.offset = 0;
|
||
ctrl.length = 1;
|
||
ctrl.blocks = 1;
|
||
ctrl.buffer[0] = (BYTE)ctrl.status;
|
||
|
||
LOGTRACE( "%s Status Phase $%02X",__PRETTY_FUNCTION__, (unsigned int)ctrl.status);
|
||
|
||
return;
|
||
}
|
||
|
||
// Send
|
||
Send();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Message in phase (used by SASI and SCSI)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::MsgIn()
|
||
{
|
||
// Phase change
|
||
if (ctrl.phase != BUS::msgin) {
|
||
LOGTRACE("%s Starting Message in phase", __PRETTY_FUNCTION__);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::msgin;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(TRUE);
|
||
ctrl.bus->SetCD(TRUE);
|
||
ctrl.bus->SetIO(TRUE);
|
||
|
||
// length, blocks are already set
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.blocks > 0);
|
||
ctrl.offset = 0;
|
||
return;
|
||
}
|
||
|
||
//Send
|
||
LOGTRACE("%s Transitioning to Send()", __PRETTY_FUNCTION__);
|
||
Send();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data-in Phase (used by SASI and SCSI)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::DataIn()
|
||
{
|
||
ASSERT(ctrl.length >= 0);
|
||
|
||
// Phase change
|
||
if (ctrl.phase != BUS::datain) {
|
||
// Minimum execution time
|
||
if (ctrl.execstart > 0) {
|
||
DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||
DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||
if (time < min_exec_time) {
|
||
SysTimer::SleepUsec(min_exec_time - time);
|
||
}
|
||
ctrl.execstart = 0;
|
||
}
|
||
|
||
// If the length is 0, go to the status phase
|
||
if (ctrl.length == 0) {
|
||
Status();
|
||
return;
|
||
}
|
||
|
||
LOGTRACE("%s Going into Data-in Phase", __PRETTY_FUNCTION__);
|
||
// Phase Setting
|
||
ctrl.phase = BUS::datain;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(FALSE);
|
||
ctrl.bus->SetIO(TRUE);
|
||
|
||
// length, blocks are already set
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.blocks > 0);
|
||
ctrl.offset = 0;
|
||
|
||
return;
|
||
}
|
||
|
||
// Send
|
||
Send();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data out phase (used by SASI and SCSI)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::DataOut()
|
||
{
|
||
ASSERT(ctrl.length >= 0);
|
||
|
||
// Phase change
|
||
if (ctrl.phase != BUS::dataout) {
|
||
// Minimum execution time
|
||
if (ctrl.execstart > 0) {
|
||
DWORD min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||
DWORD time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||
if (time < min_exec_time) {
|
||
SysTimer::SleepUsec(min_exec_time - time);
|
||
}
|
||
ctrl.execstart = 0;
|
||
}
|
||
|
||
// If the length is 0, go to the status phase
|
||
if (ctrl.length == 0) {
|
||
Status();
|
||
return;
|
||
}
|
||
|
||
LOGTRACE("%s Data out phase", __PRETTY_FUNCTION__);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::dataout;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(FALSE);
|
||
ctrl.bus->SetIO(FALSE);
|
||
|
||
// length, blocks are already calculated
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.blocks > 0);
|
||
ctrl.offset = 0;
|
||
return;
|
||
}
|
||
|
||
// Receive
|
||
Receive();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Error
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
|
||
{
|
||
// Get bus information
|
||
((GPIOBUS*)ctrl.bus)->Aquire();
|
||
|
||
// Reset check
|
||
if (ctrl.bus->GetRST()) {
|
||
// Reset the controller
|
||
Reset();
|
||
|
||
// Reset the bus
|
||
ctrl.bus->Reset();
|
||
return;
|
||
}
|
||
|
||
// Bus free for status phase and message in phase
|
||
if (ctrl.phase == BUS::status || ctrl.phase == BUS::msgin) {
|
||
BusFree();
|
||
return;
|
||
}
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
|
||
// Set status and message(CHECK CONDITION)
|
||
ctrl.status = (lun << 5) | 0x02;
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// TEST UNIT READY
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdTestUnitReady()
|
||
{
|
||
LOGTRACE("%s TEST UNIT READY Command ", __PRETTY_FUNCTION__);
|
||
|
||
// Command processing on drive
|
||
ctrl.device->TestUnitReady(this);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REZERO UNIT
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdRezero()
|
||
{
|
||
LOGTRACE( "%s REZERO UNIT Command ", __PRETTY_FUNCTION__);
|
||
|
||
// Command processing on drive
|
||
ctrl.device->Rezero(this);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REQUEST SENSE
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdRequestSense()
|
||
{
|
||
LOGTRACE( "%s REQUEST SENSE Command ", __PRETTY_FUNCTION__);
|
||
|
||
// Command processing on drive
|
||
ctrl.device->RequestSense(this);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// FORMAT UNIT
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdFormat()
|
||
{
|
||
LOGTRACE( "%s FORMAT UNIT Command ", __PRETTY_FUNCTION__);
|
||
|
||
// Command processing on drive
|
||
ctrl.device->FormatUnit(this);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REASSIGN BLOCKS
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdReassignBlocks()
|
||
{
|
||
LOGTRACE("%s REASSIGN BLOCKS Command ", __PRETTY_FUNCTION__);
|
||
|
||
// Command processing on drive
|
||
ctrl.device->ReassignBlocks(this);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// RESERVE UNIT(16)
|
||
//
|
||
// The reserve/release commands are only used in multi-initiator
|
||
// environments. RaSCSI doesn't support this use case. However, some old
|
||
// versions of Solaris will issue the reserve/release commands. We will
|
||
// just respond with an OK status.
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdReserveUnit()
|
||
{
|
||
LOGTRACE( "%s Reserve(6) Command", __PRETTY_FUNCTION__);
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// RELEASE UNIT(17)
|
||
//
|
||
// The reserve/release commands are only used in multi-initiator
|
||
// environments. RaSCSI doesn't support this use case. However, some old
|
||
// versions of Solaris will issue the reserve/release commands. We will
|
||
// just respond with an OK status.
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdReleaseUnit()
|
||
{
|
||
LOGTRACE( "%s Release(6) Command", __PRETTY_FUNCTION__);
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// READ(6)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdRead6()
|
||
{
|
||
// Get record number and block number
|
||
DWORD record = ctrl.cmd[1] & 0x1f;
|
||
record <<= 8;
|
||
record |= ctrl.cmd[2];
|
||
record <<= 8;
|
||
record |= ctrl.cmd[3];
|
||
ctrl.blocks = ctrl.cmd[4];
|
||
if (ctrl.blocks == 0) {
|
||
ctrl.blocks = 0x100;
|
||
}
|
||
|
||
LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks);
|
||
|
||
// Command processing on drive
|
||
ctrl.length = ctrl.device->Read(ctrl.cmd, ctrl.buffer, record);
|
||
if (ctrl.length <= 0) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Set next block
|
||
ctrl.next = record + 1;
|
||
|
||
// Read phase
|
||
DataIn();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// WRITE(6)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdWrite6()
|
||
{
|
||
// Get record number and block number
|
||
DWORD record = ctrl.cmd[1] & 0x1f;
|
||
record <<= 8;
|
||
record |= ctrl.cmd[2];
|
||
record <<= 8;
|
||
record |= ctrl.cmd[3];
|
||
ctrl.blocks = ctrl.cmd[4];
|
||
if (ctrl.blocks == 0) {
|
||
ctrl.blocks = 0x100;
|
||
}
|
||
|
||
LOGTRACE("%s WRITE(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, (WORD)record, (WORD)ctrl.blocks);
|
||
|
||
// Command processing on drive
|
||
ctrl.length = ctrl.device->WriteCheck(record);
|
||
if (ctrl.length <= 0) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Set next block
|
||
ctrl.next = record + 1;
|
||
|
||
// Write phase
|
||
DataOut();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SEEK(6)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdSeek6()
|
||
{
|
||
LOGTRACE("%s SEEK(6) Command ", __PRETTY_FUNCTION__);
|
||
|
||
// Command processing on drive
|
||
ctrl.device->Seek6(this);
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// ASSIGN
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdAssign()
|
||
{
|
||
LOGTRACE("%s ASSIGN Command ", __PRETTY_FUNCTION__);
|
||
|
||
// Command processing on drive
|
||
bool status = ctrl.device->CheckReady();
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Request 4 bytes of data
|
||
ctrl.length = 4;
|
||
|
||
// Write phase
|
||
DataOut();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SPECIFY
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::CmdSpecify()
|
||
{
|
||
LOGTRACE("%s SPECIFY Command ", __PRETTY_FUNCTION__);
|
||
|
||
// Command processing on drive
|
||
bool status = ctrl.device->CheckReady();
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Request 10 bytes of data
|
||
ctrl.length = 10;
|
||
|
||
// Write phase
|
||
DataOut();
|
||
}
|
||
|
||
//===========================================================================
|
||
//
|
||
// Data transfer
|
||
//
|
||
//===========================================================================
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data transmission
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Send()
|
||
{
|
||
ASSERT(!ctrl.bus->GetREQ());
|
||
ASSERT(ctrl.bus->GetIO());
|
||
|
||
// Check that the length isn't 0
|
||
if (ctrl.length != 0) {
|
||
int len = ctrl.bus->SendHandShake(
|
||
&ctrl.buffer[ctrl.offset], ctrl.length, BUS::SEND_NO_DELAY);
|
||
|
||
// If you can not send it all, move on to the status phase
|
||
if (len != (int)ctrl.length) {
|
||
LOGERROR("%s ctrl.length (%d) did not match the amount of data sent (%d)",__PRETTY_FUNCTION__, (int)ctrl.length, len);
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Offset and Length
|
||
ctrl.offset += ctrl.length;
|
||
ctrl.length = 0;
|
||
return;
|
||
}
|
||
else{
|
||
LOGINFO("%s ctrl.length was 0", __PRETTY_FUNCTION__);
|
||
}
|
||
|
||
// Remove block and initialize the result
|
||
ctrl.blocks--;
|
||
bool result = true;
|
||
|
||
// Process after data collection (read/data-in only)
|
||
if (ctrl.phase == BUS::datain) {
|
||
if (ctrl.blocks != 0) {
|
||
// Set next buffer (set offset, length)
|
||
result = XferIn(ctrl.buffer);
|
||
LOGTRACE("%s xfer in: %d",__PRETTY_FUNCTION__, result);
|
||
LOGTRACE("%s processing after data collection", __PRETTY_FUNCTION__);
|
||
}
|
||
}
|
||
|
||
// If result FALSE, move to the status phase
|
||
if (!result) {
|
||
LOGERROR("%s Send result was false", __PRETTY_FUNCTION__);
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Continue sending if block != 0
|
||
if (ctrl.blocks != 0){
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.offset == 0);
|
||
return;
|
||
}
|
||
|
||
// Move to the next phase
|
||
switch (ctrl.phase) {
|
||
// Message in phase
|
||
case BUS::msgin:
|
||
// Bus free phase
|
||
BusFree();
|
||
break;
|
||
|
||
// Data-in Phase
|
||
case BUS::datain:
|
||
// status phase
|
||
Status();
|
||
break;
|
||
|
||
// Status phase
|
||
case BUS::status:
|
||
// Message in phase
|
||
ctrl.length = 1;
|
||
ctrl.blocks = 1;
|
||
ctrl.buffer[0] = (BYTE)ctrl.message;
|
||
MsgIn();
|
||
break;
|
||
|
||
// Other (impossible)
|
||
default:
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Receive data
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::Receive()
|
||
{
|
||
// REQ is low
|
||
ASSERT(!ctrl.bus->GetREQ());
|
||
ASSERT(!ctrl.bus->GetIO());
|
||
|
||
// Length != 0 if received
|
||
if (ctrl.length != 0) {
|
||
// Receive
|
||
int len = ctrl.bus->ReceiveHandShake(
|
||
&ctrl.buffer[ctrl.offset], ctrl.length);
|
||
LOGDEBUG("%s Received %d bytes", __PRETTY_FUNCTION__, len);
|
||
|
||
// If not able to receive all, move to status phase
|
||
if (len != (int)ctrl.length) {
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Offset and Length
|
||
ctrl.offset += ctrl.length;
|
||
ctrl.length = 0;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
LOGDEBUG("%s ctrl.length was 0", __PRETTY_FUNCTION__);
|
||
}
|
||
|
||
// Remove the control block and initialize the result
|
||
ctrl.blocks--;
|
||
bool result = true;
|
||
|
||
// Process the data out phase
|
||
if (ctrl.phase == BUS::dataout) {
|
||
if (ctrl.blocks == 0) {
|
||
// End with this buffer
|
||
result = XferOut(false);
|
||
} else {
|
||
// Continue to next buffer (set offset, length)
|
||
result = XferOut(true);
|
||
}
|
||
}
|
||
|
||
// If result is false, move to the status phase
|
||
if (!result) {
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Continue to receive is block != 0
|
||
if (ctrl.blocks != 0){
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.offset == 0);
|
||
return;
|
||
}
|
||
|
||
// Move to the next phase
|
||
switch (ctrl.phase) {
|
||
// Data out phase
|
||
case BUS::dataout:
|
||
LOGTRACE("%s transitioning to FlushUnit()",__PRETTY_FUNCTION__);
|
||
// Flush
|
||
FlushUnit();
|
||
|
||
// status phase
|
||
Status();
|
||
break;
|
||
|
||
// Other (impossible)
|
||
default:
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data transfer IN
|
||
// *Reset offset and length
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
bool SASIDEV::XferIn(BYTE *buf)
|
||
{
|
||
ASSERT(ctrl.phase == BUS::datain);
|
||
LOGTRACE("%s ctrl.cmd[0]=%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
if (!ctrl.unit[lun]) {
|
||
return false;
|
||
}
|
||
|
||
// Limited to read commands
|
||
switch (ctrl.cmd[0]) {
|
||
case eCmdRead6:
|
||
case eCmdRead10:
|
||
case eCmdRead16:
|
||
// Read from disk
|
||
ctrl.length = ctrl.unit[lun]->Read(ctrl.cmd, buf, ctrl.next);
|
||
ctrl.next++;
|
||
|
||
// If there is an error, go to the status phase
|
||
if (ctrl.length <= 0) {
|
||
// Cancel data-in
|
||
return false;
|
||
}
|
||
|
||
// If things are normal, work setting
|
||
ctrl.offset = 0;
|
||
break;
|
||
|
||
// Other (impossible)
|
||
default:
|
||
ASSERT(FALSE);
|
||
return false;
|
||
}
|
||
|
||
// Succeeded in setting the buffer
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data transfer OUT
|
||
// *If cont=true, reset the offset and length
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
bool SASIDEV::XferOut(bool cont)
|
||
{
|
||
ASSERT(ctrl.phase == BUS::dataout);
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
if (!ctrl.unit[lun]) {
|
||
return false;
|
||
}
|
||
Disk *device = ctrl.unit[lun];
|
||
|
||
switch (ctrl.cmd[0]) {
|
||
case SASIDEV::eCmdModeSelect6:
|
||
case SASIDEV::eCmdModeSelect10:
|
||
if (!device->ModeSelect(ctrl.cmd, ctrl.buffer, ctrl.offset)) {
|
||
// MODE SELECT failed
|
||
return false;
|
||
}
|
||
break;
|
||
|
||
case SASIDEV::eCmdWrite6:
|
||
case SASIDEV::eCmdWrite10:
|
||
case SASIDEV::eCmdWrite16:
|
||
case SASIDEV::eCmdVerify10:
|
||
case SASIDEV::eCmdVerify16:
|
||
// If we're a host bridge, use the host bridge's SendMessage10 function
|
||
// TODO This class must not know about SCSIBR
|
||
if (device->IsBridge()) {
|
||
if (!((SCSIBR*)device)->SendMessage10(ctrl.cmd, ctrl.buffer)) {
|
||
// write failed
|
||
return false;
|
||
}
|
||
|
||
// If normal, work setting
|
||
ctrl.offset = 0;
|
||
break;
|
||
}
|
||
|
||
// Special case Write function for DaynaPort
|
||
// TODO This class must not know about DaynaPort
|
||
if (device->IsDaynaPort()) {
|
||
if (!device->Write(ctrl.cmd, ctrl.buffer, ctrl.length)) {
|
||
// write failed
|
||
return false;
|
||
}
|
||
|
||
// If normal, work setting
|
||
ctrl.offset = 0;
|
||
ctrl.blocks = 0;
|
||
break;
|
||
}
|
||
|
||
if (!device->Write(ctrl.cmd, ctrl.buffer, ctrl.next - 1)) {
|
||
// Write failed
|
||
return false;
|
||
}
|
||
|
||
// If you do not need the next block, end here
|
||
ctrl.next++;
|
||
if (!cont) {
|
||
break;
|
||
}
|
||
|
||
// Check the next block
|
||
ctrl.length = device->WriteCheck(ctrl.next - 1);
|
||
if (ctrl.length <= 0) {
|
||
// Cannot write
|
||
return false;
|
||
}
|
||
|
||
// If normal, work setting
|
||
ctrl.offset = 0;
|
||
break;
|
||
|
||
// SPECIFY(SASI only)
|
||
case SASIDEV::eCmdInvalid:
|
||
break;
|
||
|
||
case SASIDEV::eCmdSetMcastAddr:
|
||
LOGTRACE("%s Done with DaynaPort Set Multicast Address", __PRETTY_FUNCTION__);
|
||
break;
|
||
|
||
default:
|
||
LOGWARN("Received an unexpected command (%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__)
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
// Buffer saved successfully
|
||
return true;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Logical unit flush
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::FlushUnit()
|
||
{
|
||
ASSERT(ctrl.phase == BUS::dataout);
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
if (!ctrl.unit[lun]) {
|
||
return;
|
||
}
|
||
Disk *device = ctrl.unit[lun];
|
||
|
||
// WRITE system only
|
||
switch ((SASIDEV::sasi_command)ctrl.cmd[0]) {
|
||
case SASIDEV::eCmdWrite6:
|
||
case SASIDEV::eCmdWrite10:
|
||
case SASIDEV::eCmdWrite16:
|
||
case SASIDEV::eCmdVerify10:
|
||
case SASIDEV::eCmdVerify16:
|
||
break;
|
||
|
||
case SASIDEV::eCmdModeSelect6:
|
||
case SASIDEV::eCmdModeSelect10:
|
||
// Debug code related to Issue #2 on github, where we get an unhandled Mode Select when
|
||
// the mac is rebooted
|
||
// https://github.com/akuker/RASCSI/issues/2
|
||
LOGWARN("Received \'Mode Select\'\n");
|
||
LOGWARN(" Operation Code: [%02X]\n", (WORD)ctrl.cmd[0]);
|
||
LOGWARN(" Logical Unit %01X, PF %01X, SP %01X [%02X]\n",\
|
||
(WORD)ctrl.cmd[1] >> 5, 1 & ((WORD)ctrl.cmd[1] >> 4), \
|
||
(WORD)ctrl.cmd[1] & 1, (WORD)ctrl.cmd[1]);
|
||
LOGWARN(" Reserved: %02X\n", (WORD)ctrl.cmd[2]);
|
||
LOGWARN(" Reserved: %02X\n", (WORD)ctrl.cmd[3]);
|
||
LOGWARN(" Parameter List Len %02X\n", (WORD)ctrl.cmd[4]);
|
||
LOGWARN(" Reserved: %02X\n",(WORD)ctrl.cmd[5]);
|
||
LOGWARN(" Ctrl Len: %08X\n",(WORD)ctrl.length);
|
||
|
||
if (!device->ModeSelect(
|
||
ctrl.cmd, ctrl.buffer, ctrl.offset)) {
|
||
// MODE SELECT failed
|
||
LOGWARN("Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]);
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case SASIDEV::eCmdSetMcastAddr:
|
||
// TODO: Eventually, we should store off the multicast address configuration data here...
|
||
break;
|
||
|
||
default:
|
||
LOGWARN("Received an unexpected flush command %02X!!!!!\n",(WORD)ctrl.cmd[0]);
|
||
// The following statement makes debugging a huge pain. You can un-comment it
|
||
// if you're not trying to add new devices....
|
||
// ASSERT(FALSE);
|
||
break;
|
||
}
|
||
}
|