first commit

This commit is contained in:
Xavier Rey-Robert
2018-05-03 15:47:57 +02:00
commit 35866cfb84
47 changed files with 27164 additions and 0 deletions

758
src/raspberrypi/rascsi.cpp Normal file
View File

@@ -0,0 +1,758 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2018 GIMONS
// [ メイン ]
//
//---------------------------------------------------------------------------
#include "os.h"
#include "xm6.h"
#include "fileio.h"
#include "filepath.h"
#include "disk.h"
#include "gpiobus.h"
//---------------------------------------------------------------------------
//
// 変数宣言
//
//---------------------------------------------------------------------------
enum {
CtrlMax = 8, // 最大SCSIコントローラ数
};
SASIDEV *ctrl[CtrlMax]; // コントローラ
Disk *disk[CtrlMax]; // ディスク
Filepath image[CtrlMax]; // イメージファイルパス
GPIOBUS bus; // バス
BUS::phase_t phase; // フェーズ
int actid; // アクティブなコントローラID
volatile BOOL bRun; // 実行中フラグ
BOOL bServer; // サーバーモードフラグ
int monfd; // モニター用ソケットFD
pthread_t monthread; // モニタースレッド
static void *MonThread(void *param);
//---------------------------------------------------------------------------
//
// シグナル処理
//
//---------------------------------------------------------------------------
void KillHandler(int sig)
{
// 停止指示
bRun = FALSE;
}
//---------------------------------------------------------------------------
//
// バナー出力
//
//---------------------------------------------------------------------------
BOOL Banner(int argc, char* argv[])
{
printf("SCSI Target Emulator RaSCSI(*^..^*) ");
printf("version %01d.%01d%01d\n",
(int)((VERSION >> 8) & 0xf),
(int)((VERSION >> 4) & 0xf),
(int)((VERSION ) & 0xf));
printf("Powered by XM6 TypeG Technology / ");
printf("Copyright (C) 2016-2018 GIMONS\n");
printf("Connect type : %s\n", CONNECT_DESC);
if (argc > 1 && strcmp(argv[1], "-h") == 0) {
printf("\n");
printf("Usage: %s [-ID{0|1|2|3|4|5|6|7} FILE] ...\n\n", argv[0]);
printf(" IDn is SCSI identification number.\n");
printf(" FILE is disk image file.\n\n");
printf(" Detected images type based on file extension.\n");
printf(" hdf : SASI HD image(XM6 SASI HD image)\n");
printf(" hds : SCSI HD image(XM6 SCSI HD image)\n");
printf(" hdn : SCSI HD image(NEC GENUINE)\n");
printf(" hdi : SCSI HD image(Anex86 HD image)\n");
printf(" nhd : SCSI HD image(T98Next HD image)\n");
printf(" hda : SCSI HD image(APPLE GENUINE)\n");
printf(" mos : SCSI MO image(XM6 SCSI MO image)\n");
printf(" iso : SCSI CD image(ISO 9660 image)\n");
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------------
//
// 初期化
//
//---------------------------------------------------------------------------
BOOL Init()
{
int i;
struct sockaddr_in server;
// モニター用ソケット生成
monfd = socket(PF_INET, SOCK_STREAM, 0);
memset(&server, 0, sizeof(server));
server.sin_family = PF_INET;
server.sin_port = htons(6868);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(monfd, (struct sockaddr *)&server,
sizeof(struct sockaddr_in)) < 0) {
if (errno != EADDRINUSE) {
return FALSE;
}
bServer = FALSE;
return TRUE;
} else {
bServer = TRUE;
}
// 割り込みハンドラ設定
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()) {
return FALSE;
}
// コントローラ初期化
for (i = 0; i < CtrlMax; i++) {
ctrl[i] = NULL;
}
// ディスク初期化
for (i = 0; i < CtrlMax; i++) {
disk[i] = NULL;
}
// イメージパス初期化
for (i = 0; i < CtrlMax; i++) {
image[i].Clear();
}
// アクティブID初期化
actid = -1;
return TRUE;
}
//---------------------------------------------------------------------------
//
// クリーンアップ
//
//---------------------------------------------------------------------------
void Cleanup()
{
int i;
// ディスク削除
for (i = 0; i < CtrlMax; i++) {
if (disk[i]) {
delete disk[i];
disk[i] = NULL;
}
}
// コントローラ削除
for (i = 0; i < CtrlMax; i++) {
if (ctrl[i]) {
delete ctrl[i];
ctrl[i] = NULL;
}
}
// バスをクリーンアップ
bus.Cleanup();
// モニター用ソケットクローズ
if (monfd >= 0) {
close(monfd);
}
}
//---------------------------------------------------------------------------
//
// リセット
//
//---------------------------------------------------------------------------
void Reset()
{
int i;
// アクティブID初期化
actid = -1;
// コントローラリセット
for (i = 0; i < CtrlMax; i++) {
if (ctrl[i]) {
ctrl[i]->Reset();
}
}
// バス信号線をリセット
bus.Reset();
}
//---------------------------------------------------------------------------
//
// デバイス一覧表示
//
//---------------------------------------------------------------------------
void ListDevice(FILE *fp)
{
int i;
Filepath filepath;
BOOL find;
char type[5];
find = FALSE;
type[4] = 0;
for (i = 0; i < 8; i++) {
if (disk[i] == NULL) {
continue;
}
// ヘッダー出力
if (!find) {
fprintf(fp, "\n");
fprintf(fp, "---+------+---------------------------------------\n");
fprintf(fp, "ID | TYPE | DEVICE STATUS\n");
fprintf(fp, "---+------+---------------------------------------\n");
find = TRUE;
}
// ID,タイプ出力
type[0] = (char)(disk[i]->GetID() >> 24);
type[1] = (char)(disk[i]->GetID() >> 16);
type[2] = (char)(disk[i]->GetID() >> 8);
type[3] = (char)(disk[i]->GetID());
fprintf(fp, " %d | %s | ", i, type);
// マウント状態出力
if (disk[i]->GetID() == MAKEID('S', 'C', 'B', 'R')) {
fprintf(fp, "%s", "RaSCSI BRIDGE");
} else {
disk[i]->GetPath(filepath);
fprintf(fp, "%s",
(disk[i]->IsRemovable() && !disk[i]->IsReady()) ?
"NO MEDIA" : filepath.GetPath());
}
// ライトプロテクト状態出力
if (disk[i]->IsRemovable() && disk[i]->IsReady() && disk[i]->IsWriteP()) {
fprintf(fp, "(WRITEPROTECT)");
}
// 次の行へ
fprintf(fp, "\n");
}
// コントローラが無い場合
if (!find) {
fprintf(fp, "No device is installed.\n");
return;
}
fprintf(fp, "---+------+---------------------------------------\n");
}
//---------------------------------------------------------------------------
//
// コマンド処理
//
//---------------------------------------------------------------------------
BOOL ProcessCmd(FILE *fp, int id, int cmd, int type, char *file)
{
int len;
char *ext;
Filepath filepath;
// IDチェック
if (id < 0 || id > 7) {
fprintf(fp, "Error : Invalid ID\n");
return FALSE;
}
// 接続コマンド
if (cmd == 0) { // ATTACH
// コントローラを解放
if (ctrl[id]) {
ctrl[id]->SetUnit(0, NULL);
delete ctrl[id];
ctrl[id] = NULL;
}
// ディスクを解放
if (disk[id]) {
delete disk[id];
disk[id] = NULL;
}
// SASIとSCSIを見分ける
if (type == 0) {
// パスチェック
if (!file) {
return FALSE;
}
// 最低5文字
len = strlen(file);
if (len < 5) {
return FALSE;
}
// 拡張子チェック
if (file[len - 4] != '.') {
return FALSE;
}
// 拡張子がSASIタイプで無ければSCSIに差し替え
ext = &file[len - 3];
if (xstrcasecmp(ext, "hdf") != 0) {
type = 1;
}
}
// タイプ別のインスタンスを生成
switch (type) {
case 0: // HDF
disk[id] = new SASIHD();
break;
case 1: // HDS/HDN/HDI/NHD/HDA
if (xstrcasecmp(ext, "hdn") == 0 ||
xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0) {
disk[id] = new SCSIHD_NEC();
} else if (xstrcasecmp(ext, "hda") == 0) {
disk[id] = new SCSIHD_APPLE();
} else {
disk[id] = new SCSIHD();
}
break;
case 2: // MO
disk[id] = new SCSIMO();
break;
case 3: // CD
disk[id] = new SCSICD();
break;
case 4: // BRIDGE
disk[id] = new SCSIBR();
break;
default:
fprintf(fp, "Error : Invalid device type\n");
return FALSE;
}
// タイプに合わせてコントローラを作る
if (type == 0) {
ctrl[id] = new SASIDEV();
} else {
ctrl[id] = new SCSIDEV();
}
// ドライブはファイルの確認を行う
if (type <= 1 || (type <= 3 && xstrcasecmp(file, "-") != 0)) {
// パスを設定
filepath.SetPath(file);
// オープン
if (!disk[id]->Open(filepath)) {
fprintf(fp, "Error : File open error [%s]\n", file);
delete disk[id];
disk[id] = NULL;
delete ctrl[id];
ctrl[id] = NULL;
return FALSE;
}
}
// ライトスルーに設定
disk[id]->SetCacheWB(FALSE);
// 新たなディスクを接続
ctrl[id]->Connect(id, &bus);
ctrl[id]->SetUnit(0, disk[id]);
return TRUE;
}
// 有効なコマンドか
if (cmd > 4) {
fprintf(fp, "Error : Invalid command\n");
return FALSE;
}
// コントローラが存在するか
if (ctrl[id] == NULL) {
fprintf(fp, "Error : No such device\n");
return FALSE;
}
// ディスクが存在するか
if (disk[id] == NULL) {
fprintf(fp, "Error : No such device\n");
return FALSE;
}
// 切断コマンド
if (cmd == 1) { // DETACH
ctrl[id]->SetUnit(0, NULL);
delete disk[id];
disk[id] = NULL;
delete ctrl[id];
ctrl[id] = NULL;
return TRUE;
}
// MOかCDの場合だけ有効
if (disk[id]->GetID() != MAKEID('S', 'C', 'M', 'O') &&
disk[id]->GetID() != MAKEID('S', 'C', 'C', 'D')) {
fprintf(fp, "Error : Operation denied(Deveice isn't MO or CD)\n");
return FALSE;
}
switch (cmd) {
case 2: // INSERT
// パスを設定
filepath.SetPath(file);
// オープン
if (!disk[id]->Open(filepath)) {
fprintf(fp, "Error : File open error [%s]\n", file);
return FALSE;
}
break;
case 3: // EJECT
disk[id]->Eject(TRUE);
break;
case 4: // PROTECT
if (disk[id]->GetID() != MAKEID('S', 'C', 'M', 'O')) {
fprintf(fp, "Error : Operation denied(Deveice isn't MO)\n");
return FALSE;
}
disk[id]->WriteP(!disk[id]->IsWriteP());
break;
default:
ASSERT(FALSE);
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------------
//
// 引数処理
//
//---------------------------------------------------------------------------
BOOL ParseArgument(int argc, char* argv[])
{
int i;
int id;
int type;
char *argID;
char *argPath;
int len;
char *ext;
// IDとパス指定がなければ処理を中断
if (argc < 3) {
return TRUE;
}
// 引数の解読開始
i = 1;
argc--;
// IDとパスを取得
while (argc >= 2) {
argc -= 2;
argID = argv[i++];
argPath = argv[i++];
// -ID or -idの形式をチェック
if (strlen(argID) != 4 || xstrncasecmp(argID, "-id", 3) != 0) {
fprintf(stderr,
"Error : Invalid argument(-IDn) [%s]\n", argID);
return FALSE;
}
// ID番号をチェック(0-7)
if (argID[3] < '0' || argID[3] > '7') {
fprintf(stderr,
"Error : Invalid argument(-IDn n=0-7) [%c]\n", argID[3]);
return FALSE;
}
// ID確定
id = argID[3] - '0';
// すでにアクティブなデバイスがあるならスキップ
if (disk[id]) {
continue;
}
// デバイスタイプを初期化
type = -1;
// イーサネットとホストブリッジのチェック
if (xstrcasecmp(argPath, "bridge") == 0) {
type = 4;
} else {
// パスの長さをチェック
len = strlen(argPath);
if (len < 5) {
fprintf(stderr,
"Error : Invalid argument(File path is short) [%s]\n",
argPath);
return FALSE;
}
// 拡張子を持っているか?
if (argPath[len - 4] != '.') {
fprintf(stderr,
"Error : Invalid argument(No extension) [%s]\n", argPath);
return FALSE;
}
// タイプを決める
ext = &argPath[len - 3];
if (xstrcasecmp(ext, "hdf") == 0 ||
xstrcasecmp(ext, "hds") == 0 ||
xstrcasecmp(ext, "hdn") == 0 ||
xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0 ||
xstrcasecmp(ext, "hda") == 0) {
// HD(SASI/SCSI)
type = 0;
} else if (strcasecmp(ext, "mos") == 0) {
// MO
type = 2;
} else if (strcasecmp(ext, "iso") == 0) {
// CD
type = 3;
} else {
// タイプが判別できない
fprintf(stderr,
"Error : Invalid argument(file type) [%s]\n", ext);
return FALSE;
}
}
// コマンド実行
if (!ProcessCmd(stderr, id, 0, type, argPath)) {
return FALSE;
}
}
// デバイスリスト表示
ListDevice(stdout);
return TRUE;
}
//---------------------------------------------------------------------------
//
// モニタースレッド
//
//---------------------------------------------------------------------------
static void *MonThread(void *param)
{
struct sockaddr_in client;
socklen_t len;
int fd;
FILE *fp;
BYTE buf[BUFSIZ];
int id;
int cmd;
int type;
char *file;
BOOL list;
// 監視準備
listen(monfd, 1);
while (1) {
// 接続待ち
memset(&client, 0, sizeof(client));
len = sizeof(client);
fd = accept(monfd, (struct sockaddr*)&client, &len);
if (fd < 0) {
break;
}
// コマンド取得
fp = fdopen(fd, "r+");
fgets((char *)buf, BUFSIZ, fp);
buf[strlen((const char*)buf) - 1] = 0;
// デバイスリスト表示なのか制御コマンドかを判断
if (strncasecmp((char*)buf, "list", 4) == 0) {
list = TRUE;
} else {
list = FALSE;
id = (int)(buf[0] - '0');
cmd = (int)(buf[2] - '0');
type = (int)(buf[4] - '0');
file = (char*)&buf[6];
}
// バスフリーのタイミングまで待つ
while (phase != BUS::busfree) {
usleep(500 * 1000);
}
if (list) {
// リスト表示
ListDevice(fp);
} else {
// コマンド実行
ProcessCmd(fp, id, cmd, type, file);
}
// 接続解放
fclose(fp);
close(fd);
}
return NULL;
}
//---------------------------------------------------------------------------
//
// 主処理
//
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
int i;
DWORD dwBusBak;
DWORD dwBusIn;
struct sched_param schparam;
// バナー出力
if (!Banner(argc, argv)) {
exit(0);
}
// 初期化
if (!Init()) {
fprintf(stderr, "Error : Initializing\n");
// 恐らくrootでは無い
exit(EPERM);
}
// 既に起動している?
if (!bServer) {
fprintf(stderr, "Error : Already running RaSCSI\n");
exit(0);
}
// 構築
if (!ParseArgument(argc, argv)) {
// クリーンアップ
Cleanup();
// 引数エラーで終了
exit(EINVAL);
}
// モニタースレッド生成
bRun = TRUE;
pthread_create(&monthread, NULL, MonThread, NULL);
// リセット
Reset();
// メインループ準備
phase = BUS::busfree;
dwBusBak = bus.Aquire();
// メインループ
while(bRun) {
// バスの入力信号変化検出
dwBusIn = bus.Aquire();
// 入力信号変化検出
if ((dwBusIn & GPIO_INEDGE) == (dwBusBak & GPIO_INEDGE)) {
usleep(0);
continue;
}
// バスの入力信号を保存
dwBusBak = dwBusIn;
// そもそもセレクション信号が無ければ無視
if (!bus.GetSEL() || bus.GetBSY()) {
continue;
}
// 全コントローラに通知
for (i = 0; i < CtrlMax; i++) {
if (!ctrl[i]) {
continue;
}
// セレクションフェーズに移行したターゲットを探す
if (ctrl[i]->Process() == BUS::selection) {
// ターゲットのIDを取得
actid = i;
// セレクションフェーズ
phase = BUS::selection;
break;
}
}
// セレクションフェーズが開始されていなければバスの監視へ戻る
if (phase != BUS::selection) {
continue;
}
// ターゲット走行開始
// スケジューリングポリシー設定(最優先)
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
// バスフリーになるまでループ
while (bRun) {
// ターゲット駆動
phase = ctrl[actid]->Process();
// バスフリーになったら終了
if (phase == BUS::busfree) {
break;
}
}
// バスフリーでセッション終了
actid = -1;
phase = BUS::busfree;
bus.Reset();
dwBusBak = bus.Aquire();
// スケジューリングポリシー設定(ノーマル)
schparam.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &schparam);
}
// クリーンアップ
Cleanup();
// 終了
exit(0);
}