RASCSI/src/raspberrypi/rasdump.cpp

1004 lines
18 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ダンプユーティリティ(イニシーエタモード) ]
//
//---------------------------------------------------------------------------
#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 boardid; // ボードID(自身のID)
Filepath hdsfile; // HDSファイル
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 ");
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 [-b BID] -f FILE [-r]\n", argv[0]);
printf(" ID is target device SCSI ID {0|1|2|3|4|5|6|7}.\n");
printf(" BID is rascsi board SCSI ID {0|1|2|3|4|5|6|7}. Default is 7.\n");
printf(" FILE is HDS 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;
boardid = 7;
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:b:f:r")) != -1) {
switch (opt) {
case 'i':
targetid = optarg[0] - '0';
break;
case 'b':
boardid = optarg[0] - '0';
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;
}
// BOARD IDチェック
if (boardid < 0 || boardid > 7) {
fprintf(stderr,
"Error : Invalid board id range\n");
return FALSE;
}
// TARGETとBOARDのID重複チェック
if (targetid == boardid) {
fprintf(stderr,
"Error : Invalid target or board id\n");
return FALSE;
}
// ファイルチェック
if (!file) {
fprintf(stderr,
"Error : Invalid file path\n");
return FALSE;
}
hdsfile.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 << boardid);
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;
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[4] = 0xff;
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;
}
//---------------------------------------------------------------------------
//
// MODE SENSE実行
//
//---------------------------------------------------------------------------
int ModeSense(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] = 0x1a;
cmd[2] = 0x3f;
cmd[4] = 0xff;
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;
}
//---------------------------------------------------------------------------
//
// INQUIRY実行
//
//---------------------------------------------------------------------------
int Inquiry(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] = 0x12;
cmd[4] = 0xff;
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;
}
//---------------------------------------------------------------------------
//
// READ CAPACITY実行
//
//---------------------------------------------------------------------------
int ReadCapacity(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, 10);
cmd[0] = 0x25;
if (!Command(cmd, 10)) {
result = -2;
goto exit;
}
// DATAIN
memset(buf, 0x00, 8);
count = DataIn(buf, 8);
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;
}
//---------------------------------------------------------------------------
//
// READ10実行
//
//---------------------------------------------------------------------------
int Read10(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] = 0x28;
cmd[2] = (BYTE)(bstart >> 24);
cmd[3] = (BYTE)(bstart >> 16);
cmd[4] = (BYTE)(bstart >> 8);
cmd[5] = (BYTE)bstart;
cmd[7] = (BYTE)(blength >> 8);
cmd[8] = (BYTE)blength;
if (!Command(cmd, 10)) {
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;
}
//---------------------------------------------------------------------------
//
// WRITE10実行
//
//---------------------------------------------------------------------------
int Write10(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] = 0x2a;
cmd[2] = (BYTE)(bstart >> 24);
cmd[3] = (BYTE)(bstart >> 16);
cmd[4] = (BYTE)(bstart >> 8);
cmd[5] = (BYTE)bstart;
cmd[7] = (BYTE)(blength >> 8);
cmd[8] = (BYTE)blength;
if (!Command(cmd, 10)) {
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;
char str[32];
DWORD bsiz;
DWORD bnum;
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(hdsfile.GetPath(), omode)) {
fprintf(stderr, "Error : Can't open hds file\n");
// クリーンアップ
Cleanup();
exit(EPERM);
}
// バスフリー
BusFree();
// RESETシグナル発行
bus.SetRST(TRUE);
usleep(1000);
bus.SetRST(FALSE);
// ダンプ開始
printf("TARGET ID : %d\n", targetid);
printf("BORAD ID : %d\n", boardid);
// 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;
}
// INQUIRY
count = Inquiry(targetid, buffer);
if (count < 0) {
fprintf(stderr, "INQUIRY ERROR %d\n", count);
goto cleanup_exit;
}
// INQUIRYの情報を表示
memset(str, 0x00, sizeof(str));
memcpy(str, &buffer[8], 8);
printf("Vendor : %s\n", str);
memset(str, 0x00, sizeof(str));
memcpy(str, &buffer[16], 16);
printf("Product : %s\n", str);
memset(str, 0x00, sizeof(str));
memcpy(str, &buffer[32], 4);
printf("Revison : %s\n", str);
// 容量取得
count = ReadCapacity(targetid, buffer);
if (count < 0) {
fprintf(stderr, "READ CAPACITY ERROR %d\n", count);
goto cleanup_exit;
}
// ブロックサイズとブロック数の表示
bsiz =
(buffer[4] << 24) | (buffer[5] << 16) |
(buffer[6] << 8) | buffer[7];
bnum =
(buffer[0] << 24) | (buffer[1] << 16) |
(buffer[2] << 8) | buffer[3];
bnum++;
printf("Number of blocks : %d Blocks\n", (int)bnum);
printf("Block length : %d Bytes\n", (int)bsiz);
printf("Unit Capacity : %d MBytes %d Bytes\n",
(int)(bsiz * bnum / 1024 / 1024),
(int)(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),
(int)bnum);
fflush(stdout);
if (restore) {
if (fio.Read(buffer, dsiz)) {
if (Write10(targetid, i * duni, duni, dsiz, buffer) >= 0) {
continue;
}
}
} else {
if (Read10(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)) {
Write10(targetid, i * duni, dnum, dsiz, buffer);
}
} else {
if (Read10(targetid, i * duni, dnum, dsiz, buffer) >= 0) {
fio.Write(buffer, dsiz);
}
}
}
// 完了メッセージ
printf("%3d%%(%7d/%7d)\n", 100, (int)bnum, (int)bnum);
cleanup_exit:
// ファイルクローズ
fio.Close();
// クリーンアップ
Cleanup();
// 終了
exit(0);
}