//--------------------------------------------------------------------------- // // 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); }