RASCSI/src/raspberrypi/sasidump.cpp
Uwe Seimet 45cd5e58d1
Inheritance hierarchy improvements, reduced dependencies to Disk class (#662)
* Fixed buster compile-time issue

* Host services inherit from ModePageDevice

* Call base class

* Visibility update

* Updated includes

* Updated dispatcher

* Added TODOs

* Logging update

* Code cleanup

* Use namespace instead of class for ScsiDefs

* Renaming

* Cleanup

* Use dispatcher template in order to remove duplicate code

* Updated all dispatchers

* Clean up commands

* Removed duplicate code

* Removed duplicate code

* Updated template definition

* Fixed typo

* Fixed warning

* Code cleanup

* Device list must be static

* Cleanup

* Logging update

* Added comments

* Cleanup

* Base class update

* SCSIBR is not a subclass of Disk anymore, but of PrimaryDevice

* Updated includes

* Fixed compile-time issue on the Pi

* Header file cleanup

* Interface cleanup

* Removed wrong override

* include file cleanup

* Removed obsolete usage of streams

* Removed more stream usages

* Stream usage cleanup

* Include cleanup

* Renaming

* Include cleanup

* Interface update
2022-02-13 13:30:02 -06:00

746 lines
13 KiB
C++

//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ HDD dump utility (Initiator mode/SASI Version) ]
//
// SASI IMAGE EXAMPLE
// X68000
// 10MB(10441728 BS=256 C=40788)
// 20MB(20748288 BS=256 C=81048)
// 40MB(41496576 BS=256 C=162096)
//
// MZ-2500/MZ-2800 MZ-1F23
// 20MB(22437888 BS=1024 C=21912)
//
//---------------------------------------------------------------------------
#include <cerrno>
#include "os.h"
#include "fileio.h"
#include "filepath.h"
#include "gpiobus.h"
#include "rascsi_version.h"
#define BUFSIZE 1024 * 64 // Maybe around 64KB?
GPIOBUS bus;
int targetid;
int unitid;
int bsiz; // Block size
int bnum; // Number of blocks
Filepath hdffile; // HDF file
bool restore; // Restore flag
BYTE buffer[BUFSIZE]; // Work buffer
int result; // Result code
void Cleanup();
//---------------------------------------------------------------------------
//
// Signal processing
//
//---------------------------------------------------------------------------
void KillHandler(int sig)
{
// Stop instruction
Cleanup();
exit(0);
}
//---------------------------------------------------------------------------
//
// Banner output
//
//---------------------------------------------------------------------------
bool Banner(int argc, char* argv[])
{
printf("RaSCSI hard disk dump utility (SASI HDD) ");
printf("version %s (%s, %s)\n",
rascsi_get_version_string(),
__DATE__,
__TIME__);
if (argc < 2 || strcmp(argv[1], "-h") == 0) {
printf("Usage: %s -i ID [-u UT] [-b BSIZE] -c COUNT -f FILE [-r]\n", argv[0]);
printf(" ID is target device SASI ID {0|1|2|3|4|5|6|7}.\n");
printf(" UT is target unit ID {0|1}. Default is 0.\n");
printf(" BSIZE is block size {256|512|1024}. Default is 256.\n");
printf(" COUNT is block count.\n");
printf(" FILE is HDF file path.\n");
printf(" -r is restore operation.\n");
return false;
}
return true;
}
bool Init()
{
// Interrupt handler settings
if (signal(SIGINT, KillHandler) == SIG_ERR) {
return false;
}
if (signal(SIGHUP, KillHandler) == SIG_ERR) {
return false;
}
if (signal(SIGTERM, KillHandler) == SIG_ERR) {
return false;
}
// GPIO initialization
if (!bus.Init(BUS::INITIATOR)) {
return false;
}
// Work initialization
targetid = -1;
unitid = 0;
bsiz = 256;
bnum = -1;
restore = false;
return true;
}
void Cleanup()
{
bus.Cleanup();
}
void Reset()
{
// Reset bus signal line
bus.Reset();
}
bool ParseArgument(int argc, char* argv[])
{
int opt;
char *file;
// Initialization
file = NULL;
// Argument parsing
opterr = 0;
while ((opt = getopt(argc, argv, "i:u:b:c:f:r")) != -1) {
switch (opt) {
case 'i':
targetid = optarg[0] - '0';
break;
case 'u':
unitid = optarg[0] - '0';
break;
case 'b':
bsiz = atoi(optarg);
break;
case 'c':
bnum = atoi(optarg);
break;
case 'f':
file = optarg;
break;
case 'r':
restore = true;
break;
}
}
// TARGET ID check
if (targetid < 0 || targetid > 7) {
fprintf(stderr,
"Error : Invalid target id range\n");
return false;
}
// UNIT ID check
if (unitid < 0 || unitid > 1) {
fprintf(stderr,
"Error : Invalid unit id range\n");
return false;
}
// BSIZ check
if (bsiz != 256 && bsiz != 512 && bsiz != 1024) {
fprintf(stderr,
"Error : Invalid block size\n");
return false;
}
// BNUM check
if (bnum < 0) {
fprintf(stderr,
"Error : Invalid block count\n");
return false;
}
// File check
if (!file) {
fprintf(stderr,
"Error : Invalid file path\n");
return false;
}
hdffile.SetPath(file);
return true;
}
bool WaitPhase(BUS::phase_t phase)
{
DWORD now;
// Timeout (3000ms)
now = SysTimer::GetTimerLow();
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
bus.Aquire();
if (bus.GetREQ() && bus.GetPhase() == phase) {
return true;
}
}
return false;
}
//---------------------------------------------------------------------------
//
// Bus free phase execution
//
//---------------------------------------------------------------------------
void BusFree()
{
bus.Reset();
}
//---------------------------------------------------------------------------
//
// Selection phase execution
//
//---------------------------------------------------------------------------
bool Selection(int id)
{
BYTE data;
int count;
// ID setting and SEL assertion
data = 0;
data |= (1 << id);
bus.SetDAT(data);
bus.SetSEL(TRUE);
// Wait for BSY
count = 10000;
do {
usleep(20);
bus.Aquire();
if (bus.GetBSY()) {
break;
}
} while (count--);
// SEL negate
bus.SetSEL(FALSE);
// Return true if target is busy
return bus.GetBSY();
}
//---------------------------------------------------------------------------
//
// Command phase execution
//
//---------------------------------------------------------------------------
bool Command(BYTE *buf, int length)
{
int count;
// Wait for phase
if (!WaitPhase(BUS::command)) {
return false;
}
// Send command
count = bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY);
// Return true is send results match number of requests
if (count == length) {
return true;
}
// Send error
return false;
}
//---------------------------------------------------------------------------
//
// Data in phase execution
//
//---------------------------------------------------------------------------
int DataIn(BYTE *buf, int length)
{
// Wait for phase
if (!WaitPhase(BUS::datain)) {
return -1;
}
// Receive data
return bus.ReceiveHandShake(buf, length);
}
//---------------------------------------------------------------------------
//
// Data out phase execution
//
//---------------------------------------------------------------------------
int DataOut(BYTE *buf, int length)
{
// Wait for phase
if (!WaitPhase(BUS::dataout)) {
return -1;
}
// Receive data
return bus.SendHandShake(buf, length, BUS::SEND_NO_DELAY);
}
//---------------------------------------------------------------------------
//
// Status phase execution
//
//---------------------------------------------------------------------------
int Status()
{
BYTE buf[256];
// Wait for phase
if (!WaitPhase(BUS::status)) {
return -2;
}
// Receive data
if (bus.ReceiveHandShake(buf, 1) == 1) {
return (int)buf[0];
}
// Receive error
return -1;
}
//---------------------------------------------------------------------------
//
// Message in phase execution
//
//---------------------------------------------------------------------------
int MessageIn()
{
BYTE buf[256];
// Wait for phase
if (!WaitPhase(BUS::msgin)) {
return -2;
}
// Receive data
if (bus.ReceiveHandShake(buf, 1) == 1) {
return (int)buf[0];
}
// Receive error
return -1;
}
//---------------------------------------------------------------------------
//
// TEST UNIT READY execution
//
//---------------------------------------------------------------------------
int TestUnitReady(int id)
{
BYTE cmd[256];
// Initialize result code
result = 0;
if (!Selection(id)) {
result = -1;
goto exit;
}
memset(cmd, 0x00, 6);
cmd[0] = 0x00;
cmd[1] = unitid << 5;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
if (Status() < 0) {
result = -4;
goto exit;
}
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
BusFree();
return result;
}
//---------------------------------------------------------------------------
//
// REQUEST SENSE execution
//
//---------------------------------------------------------------------------
int RequestSense(int id, BYTE *buf)
{
BYTE cmd[256];
int count;
// Initialize result codes
result = 0;
count = 0;
if (!Selection(id)) {
result = -1;
goto exit;
}
memset(cmd, 0x00, 6);
cmd[0] = 0x03;
cmd[1] = unitid << 5;
cmd[4] = 4;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
memset(buf, 0x00, 256);
count = DataIn(buf, 256);
if (count <= 0) {
result = -3;
goto exit;
}
if (Status() < 0) {
result = -4;
goto exit;
}
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
BusFree();
// If successful, return number of transfers
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// READ6 execution
//
//---------------------------------------------------------------------------
int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
{
BYTE cmd[256];
int count;
// Initialize result codes
result = 0;
count = 0;
if (!Selection(id)) {
result = -1;
goto exit;
}
memset(cmd, 0x00, 10);
cmd[0] = 0x8;
cmd[1] = (BYTE)(bstart >> 16);
cmd[1] &= 0x1f;
cmd[1] = unitid << 5;
cmd[2] = (BYTE)(bstart >> 8);
cmd[3] = (BYTE)bstart;
cmd[4] = (BYTE)blength;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
count = DataIn(buf, length);
if (count <= 0) {
result = -3;
goto exit;
}
if (Status() < 0) {
result = -4;
goto exit;
}
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
BusFree();
// If successful, return number of transfers
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// WRITE6 execution
//
//---------------------------------------------------------------------------
int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
{
BYTE cmd[256];
int count;
// Initialize result codes
result = 0;
count = 0;
if (!Selection(id)) {
result = -1;
goto exit;
}
memset(cmd, 0x00, 10);
cmd[0] = 0xa;
cmd[1] = (BYTE)(bstart >> 16);
cmd[1] &= 0x1f;
cmd[1] = unitid << 5;
cmd[2] = (BYTE)(bstart >> 8);
cmd[3] = (BYTE)bstart;
cmd[4] = (BYTE)blength;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
count = DataOut(buf, length);
if (count <= 0) {
result = -3;
goto exit;
}
if (Status() < 0) {
result = -4;
goto exit;
}
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
BusFree();
// If successful, return number of transfers
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// Main processing
//
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
int i;
int count;
DWORD duni;
DWORD dsiz;
DWORD dnum;
Fileio fio;
Fileio::OpenMode omode;
off_t size;
// Output banner
if (!Banner(argc, argv)) {
exit(0);
}
if (!Init()) {
fprintf(stderr, "Error : Initializing\n");
// Probably not executing as root?
exit(EPERM);
}
if (!ParseArgument(argc, argv)) {
Cleanup();
// Exit with argument error
exit(EINVAL);
}
Reset();
// Open file
if (restore) {
omode = Fileio::ReadOnly;
} else {
omode = Fileio::WriteOnly;
}
if (!fio.Open(hdffile.GetPath(), omode)) {
fprintf(stderr, "Error : Can't open hdf file\n");
Cleanup();
exit(EPERM);
}
BusFree();
// Execute RESET signal
bus.SetRST(TRUE);
usleep(1000);
bus.SetRST(FALSE);
// Start dump
printf("TARGET ID : %d\n", targetid);
printf("UNIT ID : %d\n", unitid);
// TEST UNIT READY
count = TestUnitReady(targetid);
if (count < 0) {
fprintf(stderr, "TEST UNIT READY ERROR %d\n", count);
goto cleanup_exit;
}
// REQUEST SENSE (for CHECK CONDITION)
count = RequestSense(targetid, buffer);
if (count < 0) {
fprintf(stderr, "REQUEST SENSE ERROR %d\n", count);
goto cleanup_exit;
}
printf("Number of blocks : %d Blocks\n", bnum);
printf("Block length : %d Bytes\n", bsiz);
// Display data size
printf("Total length : %d MBytes %d Bytes\n",
(bsiz * bnum / 1024 / 1024),
(bsiz * bnum));
// Get restore file size
if (restore) {
size = fio.GetFileSize();
printf("Restore file size : %d bytes", (int)size);
if (size > (off_t)(bsiz * bnum)) {
printf("(WARNING : File size is larger than disk size)");
} else if (size < (off_t)(bsiz * bnum)) {
printf("(ERROR : File size is smaller than disk size)\n");
goto cleanup_exit;
}
printf("\n");
}
// Dump by buffer size
duni = BUFSIZE;
duni /= bsiz;
dsiz = BUFSIZE;
dnum = bnum * bsiz;
dnum /= BUFSIZE;
if (restore) {
printf("Restore progress : ");
} else {
printf("Dump progress : ");
}
for (i = 0; i < (int)dnum; i++) {
if (i > 0) {
printf("\033[21D");
printf("\033[0K");
}
printf("%3d%%(%7d/%7d)",
(int)((i + 1) * 100 / dnum),
(int)(i * duni),
bnum);
fflush(stdout);
if (restore) {
if (fio.Read(buffer, dsiz)) {
if (Write6(targetid, i * duni, duni, dsiz, buffer) >= 0) {
continue;
}
}
} else {
if (Read6(targetid, i * duni, duni, dsiz, buffer) >= 0) {
if (fio.Write(buffer, dsiz)) {
continue;
}
}
}
printf("\n");
printf("Error occured and aborted... %d\n", result);
goto cleanup_exit;
}
if (dnum > 0) {
printf("\033[21D");
printf("\033[0K");
}
// Rounding of capacity
dnum = bnum % duni;
dsiz = dnum * bsiz;
if (dnum > 0) {
if (restore) {
if (fio.Read(buffer, dsiz)) {
Write6(targetid, i * duni, dnum, dsiz, buffer);
}
} else {
if (Read6(targetid, i * duni, dnum, dsiz, buffer) >= 0) {
fio.Write(buffer, dsiz);
}
}
}
// Completion message
printf("%3d%%(%7d/%7d)\n", 100, bnum, bnum);
cleanup_exit:
fio.Close();
Cleanup();
exit(0);
}