Files
RASCSI/src/raspberrypi/sasidump.cpp

820 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ HDDダンプユーティリティ(イニシーエタモード/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 "os.h"
#include "xm6.h"
#include "fileio.h"
#include "filepath.h"
#include "gpiobus.h"
//---------------------------------------------------------------------------
//
// 定数宣言
//
//---------------------------------------------------------------------------
#define BUFSIZE 1024 * 64 // 64KBぐらいかなぁ
//---------------------------------------------------------------------------
//
// 変数宣言
//
//---------------------------------------------------------------------------
GPIOBUS bus; // バス
int targetid; // ターゲットデバイスID
int unitid; // ターゲットユニットID
int bsiz; // ブロックサイズ
int bnum; // ブロック数
Filepath hdffile; // HDFファイル
BOOL restore; // リストアフラグ
BYTE buffer[BUFSIZE]; // ワークバッファ
int result; // 結果コード
//---------------------------------------------------------------------------
//
// 関数宣言
//
//---------------------------------------------------------------------------
void Cleanup();
//---------------------------------------------------------------------------
//
// シグナル処理
//
//---------------------------------------------------------------------------
void KillHandler(int sig)
{
// 停止指示
Cleanup();
exit(0);
}
//---------------------------------------------------------------------------
//
// バナー出力
//
//---------------------------------------------------------------------------
BOOL Banner(int argc, char* argv[])
{
printf("RaSCSI hard disk dump utility(SASI HDD) ");
printf("version %01d.%01d%01d\n",
(int)((VERSION >> 8) & 0xf),
(int)((VERSION >> 4) & 0xf),
(int)((VERSION ) & 0xf));
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()
{
// 割り込みハンドラ設定
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初期化
if (!bus.Init(BUS::INITIATOR)) {
return FALSE;
}
// ワーク初期化
targetid = -1;
unitid = 0;
bsiz = 256;
bnum = -1;
restore = FALSE;
return TRUE;
}
//---------------------------------------------------------------------------
//
// クリーンアップ
//
//---------------------------------------------------------------------------
void Cleanup()
{
// バスをクリーンアップ
bus.Cleanup();
}
//---------------------------------------------------------------------------
//
// リセット
//
//---------------------------------------------------------------------------
void Reset()
{
// バス信号線をリセット
bus.Reset();
}
//---------------------------------------------------------------------------
//
// 引数処理
//
//---------------------------------------------------------------------------
BOOL ParseArgument(int argc, char* argv[])
{
int opt;
char *file;
// 初期化
file = NULL;
// 引数解析
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チェック
if (targetid < 0 || targetid > 7) {
fprintf(stderr,
"Error : Invalid target id range\n");
return FALSE;
}
// UNIT IDチェック
if (unitid < 0 || unitid > 1) {
fprintf(stderr,
"Error : Invalid unit id range\n");
return FALSE;
}
// BSIZチェック
if (bsiz != 256 && bsiz != 512 && bsiz != 1024) {
fprintf(stderr,
"Error : Invalid block size\n");
return FALSE;
}
// BNUMチェック
if (bnum < 0) {
fprintf(stderr,
"Error : Invalid block count\n");
return FALSE;
}
// ファイルチェック
if (!file) {
fprintf(stderr,
"Error : Invalid file path\n");
return FALSE;
}
hdffile.SetPath(file);
return TRUE;
}
//---------------------------------------------------------------------------
//
// フェーズ待ち
//
//---------------------------------------------------------------------------
BOOL WaitPhase(BUS::phase_t phase)
{
DWORD now;
// タイムアウト(3000ms)
now = SysTimer::GetTimerLow();
while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) {
bus.Aquire();
if (bus.GetREQ() && bus.GetPhase() == phase) {
return TRUE;
}
}
return FALSE;
}
//---------------------------------------------------------------------------
//
// バスフリーフェーズ実行
//
//---------------------------------------------------------------------------
void BusFree()
{
// バスリセット
bus.Reset();
}
//---------------------------------------------------------------------------
//
// セレクションフェーズ実行
//
//---------------------------------------------------------------------------
BOOL Selection(int id)
{
BYTE data;
int count;
// ID設定とSELアサート
data = 0;
data |= (1 << id);
bus.SetDAT(data);
bus.SetSEL(TRUE);
// BSYを待つ
count = 10000;
do {
usleep(20);
bus.Aquire();
if (bus.GetBSY()) {
break;
}
} while (count--);
// SELネゲート
bus.SetSEL(FALSE);
// ターゲットがビジー状態なら成功
return bus.GetBSY();
}
//---------------------------------------------------------------------------
//
// コマンドフェーズ実行
//
//---------------------------------------------------------------------------
BOOL Command(BYTE *buf, int length)
{
int count;
// フェーズ待ち
if (!WaitPhase(BUS::command)) {
return FALSE;
}
// コマンド送信
count = bus.SendHandShake(buf, length);
// 送信結果が依頼数と同じなら成功
if (count == length) {
return TRUE;
}
// 送信エラー
return FALSE;
}
//---------------------------------------------------------------------------
//
// データインフェーズ実行
//
//---------------------------------------------------------------------------
int DataIn(BYTE *buf, int length)
{
// フェーズ待ち
if (!WaitPhase(BUS::datain)) {
return -1;
}
// データ受信
return bus.ReceiveHandShake(buf, length);
}
//---------------------------------------------------------------------------
//
// データアウトフェーズ実行
//
//---------------------------------------------------------------------------
int DataOut(BYTE *buf, int length)
{
// フェーズ待ち
if (!WaitPhase(BUS::dataout)) {
return -1;
}
// データ受信
return bus.SendHandShake(buf, length);
}
//---------------------------------------------------------------------------
//
// ステータスフェーズ実行
//
//---------------------------------------------------------------------------
int Status()
{
BYTE buf[256];
// フェーズ待ち
if (!WaitPhase(BUS::status)) {
return -2;
}
// データ受信
if (bus.ReceiveHandShake(buf, 1) == 1) {
return (int)buf[0];
}
// 受信エラー
return -1;
}
//---------------------------------------------------------------------------
//
// メッセージインフェーズ実行
//
//---------------------------------------------------------------------------
int MessageIn()
{
BYTE buf[256];
// フェーズ待ち
if (!WaitPhase(BUS::msgin)) {
return -2;
}
// データ受信
if (bus.ReceiveHandShake(buf, 1) == 1) {
return (int)buf[0];
}
// 受信エラー
return -1;
}
//---------------------------------------------------------------------------
//
// TEST UNIT READY実行
//
//---------------------------------------------------------------------------
int TestUnitReady(int id)
{
BYTE cmd[256];
// 結果コード初期化
result = 0;
// SELECTION
if (!Selection(id)) {
result = -1;
goto exit;
}
// COMMAND
memset(cmd, 0x00, 6);
cmd[0] = 0x00;
cmd[1] = unitid << 5;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
// STATUS
if (Status() < 0) {
result = -4;
goto exit;
}
// MESSAGE IN
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
// バスフリー
BusFree();
return result;
}
//---------------------------------------------------------------------------
//
// REQUEST SENSE実行
//
//---------------------------------------------------------------------------
int RequestSense(int id, BYTE *buf)
{
BYTE cmd[256];
int count;
// 結果コード初期化
result = 0;
count = 0;
// SELECTION
if (!Selection(id)) {
result = -1;
goto exit;
}
// COMMAND
memset(cmd, 0x00, 6);
cmd[0] = 0x03;
cmd[1] = unitid << 5;
cmd[4] = 4;
if (!Command(cmd, 6)) {
result = -2;
goto exit;
}
// DATAIN
memset(buf, 0x00, 256);
count = DataIn(buf, 256);
if (count <= 0) {
result = -3;
goto exit;
}
// STATUS
if (Status() < 0) {
result = -4;
goto exit;
}
// MESSAGE IN
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
// バスフリー
BusFree();
// 成功であれば転送数を返す
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// READ6実行
//
//---------------------------------------------------------------------------
int Read6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
{
BYTE cmd[256];
int count;
// 結果コード初期化
result = 0;
count = 0;
// SELECTION
if (!Selection(id)) {
result = -1;
goto exit;
}
// COMMAND
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;
}
// DATAIN
count = DataIn(buf, length);
if (count <= 0) {
result = -3;
goto exit;
}
// STATUS
if (Status() < 0) {
result = -4;
goto exit;
}
// MESSAGE IN
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
// バスフリー
BusFree();
// 成功であれば転送数を返す
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// WRITE6実行
//
//---------------------------------------------------------------------------
int Write6(int id, DWORD bstart, DWORD blength, DWORD length, BYTE *buf)
{
BYTE cmd[256];
int count;
// 結果コード初期化
result = 0;
count = 0;
// SELECTION
if (!Selection(id)) {
result = -1;
goto exit;
}
// COMMAND
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;
}
// DATAOUT
count = DataOut(buf, length);
if (count <= 0) {
result = -3;
goto exit;
}
// STATUS
if (Status() < 0) {
result = -4;
goto exit;
}
// MESSAGE IN
if (MessageIn() < 0) {
result = -5;
goto exit;
}
exit:
// バスフリー
BusFree();
// 成功であれば転送数を返す
if (result == 0) {
return count;
}
return result;
}
//---------------------------------------------------------------------------
//
// 主処理
//
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
int i;
int count;
DWORD duni;
DWORD dsiz;
DWORD dnum;
Fileio fio;
Fileio::OpenMode omode;
off64_t size;
// バナー出力
if (!Banner(argc, argv)) {
exit(0);
}
// 初期化
if (!Init()) {
fprintf(stderr, "Error : Initializing\n");
// 恐らくrootでは無い
exit(EPERM);
}
// 構築
if (!ParseArgument(argc, argv)) {
// クリーンアップ
Cleanup();
// 引数エラーで終了
exit(EINVAL);
}
// リセット
Reset();
// ファイルオープン
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();
// RESETシグナル発行
bus.SetRST(TRUE);
usleep(1000);
bus.SetRST(FALSE);
// ダンプ開始
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);
// データサイズの表示
printf("Total length : %d MBytes %d Bytes\n",
(bsiz * bnum / 1024 / 1024),
(bsiz * bnum));
// リストアファイルサイズの取得
if (restore) {
size = fio.GetFileSize();
printf("Restore file size : %d bytes", (int)size);
if (size > (off64_t)(bsiz * bnum)) {
printf("(WARNING : File size is larger than disk size)");
} else if (size < (off64_t)(bsiz * bnum)) {
printf("(ERROR : File size is smaller than disk size)\n");
goto cleanup_exit;
}
printf("\n");
}
// バッファサイズ毎にダンプする
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");
}
// 容量上の端数処理
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);
}
}
}
// 完了メッセージ
printf("%3d%%(%7d/%7d)\n", 100, bnum, bnum);
cleanup_exit:
// ファイルクローズ
fio.Close();
// クリーンアップ
Cleanup();
// 終了
exit(0);
}