#16 Lots more refactoring.... doesn't work anymore... work in progress

This commit is contained in:
akuker
2020-08-09 20:11:52 -05:00
parent 484030a622
commit 15f36ecd30
15 changed files with 1578 additions and 1110 deletions

27
src/raspberrypi/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/bin/rascsi",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

14
src/raspberrypi/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
"files.associations": {
"cstddef": "cpp",
"array": "cpp",
"chrono": "cpp",
"functional": "cpp",
"istream": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp"
}
}

19
src/raspberrypi/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "g++ build active file",
"command": "make",
"args": ["all", "DEBUG=1"],
"options": {
"cwd": "${workspaceFolder}/"
},
"problemMatcher": ["$gcc"],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@@ -50,12 +50,17 @@ SRC_RASCSI = \
scsi.cpp \
gpiobus.cpp \
filepath.cpp \
fileio.cpp
fileio.cpp\
rascsi_mgr.cpp\
command_thread.cpp\
os.cpp\
rasctl_command.cpp
SRC_RASCSI += $(notdir $(shell find ./controllers -name '*.cpp'))
SRC_RASCSI += $(notdir $(shell find ./disks -name '*.cpp'))
SRC_RASCTL = \
rasctl.cpp
rasctl.cpp\
rasctl_command.cpp
SRC_RASDUMP = \
rasdump.cpp \
@@ -107,9 +112,11 @@ ALL: all
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt
$(BINDIR)/$(RASCSI): $(OBJ_RASCSI) $(BINDIR)
@echo -- Linking $(RASCSI)
$(CXX) -o $@ $(OBJ_RASCSI) -lpthread
$(BINDIR)/$(RASCTL): $(OBJ_RASCTL) $(BINDIR)
@echo -- Linking $(RASCTL)
$(CXX) -o $@ $(OBJ_RASCTL)
$(RASDUMP): $(OBJ_RASDUMP) $(BINDIR)

View File

@@ -0,0 +1,689 @@
#include "os.h"
#include "rasctl_command.h"
#include "command_thread.h"
#include "rascsi_mgr.h"
#include "sasihd.h"
#include "scsihd.h"
#include "scsihd_nec.h"
#include "scsihd_apple.h"
#include "scsicd.h"
#include "scsimo.h"
#include "scsi_host_bridge.h"
BOOL Command_Thread::m_running = FALSE;
int Command_Thread::m_monsocket = -1; // Monitor Socket
pthread_t Command_Thread::m_monthread; // Monitor Thread
void Command_Thread::Start(){
m_running = TRUE;
}
void Command_Thread::Stop(){
m_running = FALSE;
}
BOOL Command_Thread::IsRunning(){
return m_running;
}
//---------------------------------------------------------------------------
//
// Initialization
//
//---------------------------------------------------------------------------
BOOL Command_Thread::Init()
{
#ifndef BAREMETAL
struct sockaddr_in server;
int yes;
// Create socket for monitor
m_monsocket = 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);
// allow address reuse
yes = 1;
if (setsockopt(
m_monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0){
return FALSE;
}
// Bind
if (bind(m_monsocket, (struct sockaddr *)&server,
sizeof(struct sockaddr_in)) < 0) {
FPRT(stderr, "Error : Already running?\n");
return FALSE;
}
// Create Monitor Thread
pthread_create(&m_monthread, NULL, MonThread, NULL);
#endif // BAREMETAL
return TRUE;
}
void Command_Thread::Close(){
// Close the monitor socket
if (m_monsocket >= 0) {
close(m_monsocket);
}
}
//---------------------------------------------------------------------------
//
// Argument Parsing
//
//---------------------------------------------------------------------------
#ifdef BAREMETAL
BOOL ParseConfig(int argc, char* argv[])
{
FRESULT fr;
FIL fp;
char line[512];
int id;
int un;
int type;
char *argID;
char *argPath;
int len;
char *ext;
// Mount the SD card
fr = f_mount(&fatfs, "", 1);
if (fr != FR_OK) {
FPRT(stderr, "Error : SD card mount failed.\n");
return FALSE;
}
// If there is no setting file, the processing is interrupted
fr = f_open(&fp, "rascsi.ini", FA_READ);
if (fr != FR_OK) {
return FALSE;
}
// Start Decoding
while (TRUE) {
// Get one Line
memset(line, 0x00, sizeof(line));
if (f_gets(line, sizeof(line) -1, &fp) == NULL) {
break;
}
// Delete the CR/LF
len = strlen(line);
while (len > 0) {
if (line[len - 1] != '\r' && line[len - 1] != '\n') {
break;
}
line[len - 1] = '\0';
len--;
}
// Get the ID and Path
argID = &line[0];
argPath = &line[4];
line[3] = '\0';
// Check if the line is an empty string
if (argID[0] == '\0' || argPath[0] == '\0') {
continue;
}
if (strlen(argID) == 3 && xstrncasecmp(argID, "id", 2) == 0) {
// ID or ID Format
// Check that the ID number is valid (0-7)
if (argID[2] < '0' || argID[2] > '7') {
FPRT(stderr,
"Error : Invalid argument(IDn n=0-7) [%c]\n", argID[2]);
goto parse_error;
}
// The ID unit is good
id = argID[2] - '0';
un = 0;
} else if (xstrncasecmp(argID, "hd", 2) == 0) {
// HD or HD format
if (strlen(argID) == 3) {
// Check that the HD number is valid (0-9)
if (argID[2] < '0' || argID[2] > '9') {
FPRT(stderr,
"Error : Invalid argument(HDn n=0-15) [%c]\n", argID[2]);
goto parse_error;
}
// ID was confirmed
id = (argID[2] - '0') / UnitNum;
un = (argID[2] - '0') % UnitNum;
} else if (strlen(argID) == 4) {
// Check that the HD number is valid (10-15)
if (argID[2] != '1' || argID[3] < '0' || argID[3] > '5') {
FPRT(stderr,
"Error : Invalid argument(HDn n=0-15) [%c]\n", argID[2]);
goto parse_error;
}
// The ID unit is good - create the id and unit number
id = ((argID[3] - '0') + 10) / UnitNum;
un = ((argID[3] - '0') + 10) % UnitNum;
argPath++;
} else {
FPRT(stderr,
"Error : Invalid argument(IDn or HDn) [%s]\n", argID);
goto parse_error;
}
} else {
FPRT(stderr,
"Error : Invalid argument(IDn or HDn) [%s]\n", argID);
goto parse_error;
}
// Skip if there is already an active device
if (disk[id * UnitNum + un] &&
!disk[id * UnitNum + un]->IsNULL()) {
continue;
}
// Initialize device type
type = -1;
// Check ethernet and host bridge
if (xstrcasecmp(argPath, "bridge") == 0) {
type = 4;
} else {
// Check the path length
len = strlen(argPath);
if (len < 5) {
FPRT(stderr,
"Error : Invalid argument(File path is short) [%s]\n",
argPath);
goto parse_error;
}
// Does the file have an extension?
if (argPath[len - 4] != '.') {
FPRT(stderr,
"Error : Invalid argument(No extension) [%s]\n", argPath);
goto parse_error;
}
// Figure out what the type is
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 {
// Cannot determine the file type
FPRT(stderr,
"Error : Invalid argument(file type) [%s]\n", ext);
goto parse_error;
}
}
// Execute the command
if (!ExecuteCommand(stderr, id, un, 0, type, argPath)) {
goto parse_error;
}
}
// Close the configuration file
f_close(&fp);
// Display the device list
ListDevice(stdout);
return TRUE;
parse_error:
// Close the configuration file
f_close(&fp);
return FALSE;
}
#else
#endif // BAREMETAL
//---------------------------------------------------------------------------
//
// Monitor Thread
//
//---------------------------------------------------------------------------
void *Command_Thread::MonThread(void *param)
{
#ifndef BAREMETAL
struct sched_param schedparam;
struct sockaddr_in client;
socklen_t len;
int fd;
FILE *fp;
char buf[BUFSIZ];
char *p;
Rasctl_Command *new_command;
// Scheduler Settings
schedparam.sched_priority = 0;
sched_setscheduler(0, SCHED_IDLE, &schedparam);
// Set the affinity to a specific processor core
FixCpu(2);
// Wait for the execution to start
while (!m_running) {
usleep(1);
}
// Setup the monitor socket to receive commands
listen(m_monsocket, 1);
while (1) {
// Wait for connection
memset(&client, 0, sizeof(client));
len = sizeof(client);
fd = accept(m_monsocket, (struct sockaddr*)&client, &len);
if (fd < 0) {
break;
}
// Fetch the command
fp = fdopen(fd, "r+");
p = fgets(buf, BUFSIZ, fp);
// Failed to get the command
if (!p) {
goto next;
}
// Remove the newline character
p[strlen(p) - 1] = 0;
// List all of the devices
if (xstrncasecmp(p, "list", 4) == 0) {
Rascsi_Manager::GetInstance()->ListDevice(fp);
goto next;
}
new_command = Rasctl_Command::DeSerialize((BYTE*)p,BUFSIZ);
// // Wait until we becom idle
// while (m_running) {
// usleep(500 * 1000);
// }
// Execute the command
ExecuteCommand(fp, new_command);
next:
// Release the connection
fclose(fp);
close(fd);
}
#endif // BAREMETAL
return NULL;
}
BOOL ValidateCommand(rasctl_command *cmd){
// BOOL Command_Thread::ParseArgument(int argc, char* argv[])
// {
// int id = -1;
// bool is_sasi = false;
// int max_id = 7;
// int opt;
// while ((opt = getopt(argc, argv, "-IiHhD:d:")) != -1) {
// switch (opt) {
// case 'I':
// case 'i':
// is_sasi = false;
// max_id = 7;
// id = -1;
// continue;
// case 'H':
// case 'h':
// is_sasi = true;
// max_id = 15;
// id = -1;
// continue;
// case 'D':
// case 'd': {
// char* end;
// id = strtol(optarg, &end, 10);
// if ((id < 0) || (max_id < id)) {
// fprintf(stderr, "%s: invalid %s (0-%d)\n",
// optarg, is_sasi ? "HD" : "ID", max_id);
// return false;
// }
// break;
// }
// default:
// return false;
// case 1:
// break;
// }
// if (id < 0) {
// fprintf(stderr, "%s: ID not specified\n", optarg);
// return false;
// } else if (Rascsi_Manager::GetInstance()->m_disk[id] && !Rascsi_Manager::GetInstance()->m_disk[id]->IsNULL()) {
// fprintf(stderr, "%d: duplicate ID\n", id);
// return false;
// }
// char* path = optarg;
// int type = -1;
// if (HasSuffix(path, ".hdf")
// || HasSuffix(path, ".hds")
// || HasSuffix(path, ".hdn")
// || HasSuffix(path, ".hdi")
// || HasSuffix(path, ".hda")
// || HasSuffix(path, ".nhd")) {
// type = 0;
// } else if (HasSuffix(path, ".mos")) {
// type = 2;
// } else if (HasSuffix(path, ".iso")) {
// type = 3;
// } else if (xstrcasecmp(path, "bridge") == 0) {
// type = 4;
// } else {
// // Cannot determine the file type
// fprintf(stderr,
// "%s: unknown file extension\n", path);
// return false;
// }
// int un = 0;
// if (is_sasi) {
// un = id % Rascsi_Manager::UnitNum;
// id /= Rascsi_Manager::UnitNum;
// }
// // Execute the command
// if (!ExecuteCommand(stderr, id, un, 0, type, path)) {
// return false;
// }
// id = -1;
// }
// // Display the device list
// Rascsi_Manager::GetInstance()->ListDevice(stdout);
// return true;
// }
// if (type == 0) {
// // Passed the check
// if (!file) {
// return FALSE;
// }
// // Check that command is at least 5 characters long
// len = strlen(file);
// if (len < 5) {
// return FALSE;
// }
// // Check the extension
// if (file[len - 4] != '.') {
// return FALSE;
// }
// // If the extension is not SASI type, replace with SCSI
// ext = &file[len - 3];
// if (xstrcasecmp(ext, "hdf") != 0) {
// type = 1;
// }
// // Check the Controller Number
// if (cmd->id < 0 || id >= Rascsi_Manager::GetInstance()->CtrlMax) {
// FPRT(fp, "Error : Invalid ID\n");
// return FALSE;
// }
// // Check the Unit Number
// if (cmd->un < 0 || un >= Rascsi_Manager::GetInstance()->UnitNum) {
// FPRT(fp, "Error : Invalid unit number\n");
// return FALSE;
// }
return FALSE;
}
//---------------------------------------------------------------------------
//
// Command Processing
//
//---------------------------------------------------------------------------
BOOL Command_Thread::ExecuteCommand(FILE *fp, Rasctl_Command *incoming_command)
{
Filepath filepath;
BOOL result = FALSE;
switch(incoming_command->cmd){
case rasctl_cmd_shutdown:
result = DoShutdown(fp,incoming_command);
break;
case rasctl_cmd_list:
result = DoList(fp,incoming_command);
break;
case rasctl_cmd_attach:
result = DoAttach(fp,incoming_command);
break;
case rasctl_cmd_insert:
result = DoInsert(fp,incoming_command);
break;
case rasctl_cmd_eject:
result = DoEject(fp,incoming_command);
break;
case rasctl_cmd_protect:
result = DoProtect(fp,incoming_command);
break;
case rasctl_cmd_detach:
result = DoDetach(fp,incoming_command);
break;
default:
printf("UNKNOWN COMMAND RECEIVED\n");
}
if(!result){
fputs("Unknown server error", fp);
}
return result;
}
BOOL Command_Thread::DoShutdown(FILE *fp, Rasctl_Command *incoming_command){return FALSE;}
BOOL Command_Thread::DoList(FILE *fp, Rasctl_Command *incoming_command){return FALSE;}
BOOL Command_Thread::DoAttach(FILE *fp, Rasctl_Command *incoming_command){return FALSE;}
BOOL Command_Thread::DoDetach(FILE *fp, Rasctl_Command *incoming_command){return FALSE;}
BOOL Command_Thread::DoInsert(FILE *fp, Rasctl_Command *incoming_command){return FALSE;}
BOOL Command_Thread::DoEject(FILE *fp, Rasctl_Command *incoming_command){return FALSE;}
BOOL Command_Thread::DoProtect(FILE *fp, Rasctl_Command *incoming_command){return FALSE;}
// BOOL Command_Thread::DoAttach(File *fp, Rasctl_Command *incoming_command){
// // Connect Command
// if (cmd == rasctl_cmd_attach) { // ATTACH
// // Distinguish between SASI and SCSI
// ext = NULL;
// pUnit = NULL;
// }
// // Create a new drive, based upon type
// switch (type) {
// case 0: // HDF
// pUnit = new SASIHD();
// break;
// case 1: // HDS/HDN/HDI/NHD/HDA
// if (ext == NULL) {
// break;
// }
// if (xstrcasecmp(ext, "hdn") == 0 ||
// xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0) {
// pUnit = new SCSIHD_NEC();
// } else if (xstrcasecmp(ext, "hda") == 0) {
// pUnit = new SCSIHD_APPLE();
// } else {
// pUnit = new SCSIHD();
// }
// break;
// case 2: // MO
// pUnit = new SCSIMO();
// break;
// case 3: // CD
// pUnit = new SCSICD();
// break;
// case 4: // BRIDGE
// pUnit = new SCSIBR();
// break;
// default:
// FPRT(fp, "Error : Invalid device type\n");
// return FALSE;
// }
// // drive checks files
// if (type <= 1 || (type <= 3 && xstrcasecmp(file, "-") != 0)) {
// // Set the Path
// filepath.SetPath(file);
// // Open the file path
// if (!pUnit->Open(filepath)) {
// FPRT(fp, "Error : File open error [%s]\n", file);
// delete pUnit;
// return FALSE;
// }
// }
// // Set the cache to write-through
// pUnit->SetCacheWB(FALSE);
// // Replace with the newly created unit
// map[id * Rascsi_Manager::GetInstance()->UnitNum + un] = pUnit;
// // Re-map the controller
// Rascsi_Manager::GetInstance()->MapControler(fp, map);
// return TRUE;
// }
// // Is this a valid command?
// if (cmd > 4) {
// FPRT(fp, "Error : Invalid command\n");
// return FALSE;
// }
// // Does the controller exist?
// if (Rascsi_Manager::GetInstance()->m_ctrl[id] == NULL) {
// FPRT(fp, "Error : No such device\n");
// return FALSE;
// }
// // Does the unit exist?
// pUnit = Rascsi_Manager::GetInstance()->m_disk[id * Rascsi_Manager::GetInstance()->UnitNum + un];
// if (pUnit == NULL) {
// FPRT(fp, "Error : No such device\n");
// return FALSE;
// }
// // Disconnect Command
// if (cmd == 1) { // DETACH
// // Free the existing unit
// map[id * Rascsi_Manager::GetInstance()->UnitNum + un] = NULL;
// // Re-map the controller
// Rascsi_Manager::GetInstance()->MapControler(fp, map);
// return TRUE;
// }
// // Valid only for MO or CD
// if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O') &&
// pUnit->GetID() != MAKEID('S', 'C', 'C', 'D')) {
// FPRT(fp, "Error : Operation denied(Deveice isn't removable)\n");
// return FALSE;
// }
// switch (cmd) {
// case 2: // INSERT
// // Set the file path
// filepath.SetPath(file);
// // Open the file
// if (!pUnit->Open(filepath)) {
// FPRT(fp, "Error : File open error [%s]\n", file);
// return FALSE;
// }
// break;
// case 3: // EJECT
// pUnit->Eject(TRUE);
// break;
// case 4: // PROTECT
// if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O')) {
// FPRT(fp, "Error : Operation denied(Deveice isn't MO)\n");
// return FALSE;
// }
// pUnit->WriteP(!pUnit->IsWriteP());
// break;
// default:
// ASSERT(FALSE);
// return FALSE;
// }
// return TRUE;
// }
BOOL Command_Thread::HasSuffix(const char* string, const char* suffix) {
int string_len = strlen(string);
int suffix_len = strlen(suffix);
return (string_len >= suffix_len)
&& (xstrcasecmp(string + (string_len - suffix_len), suffix) == 0);
}

View File

@@ -0,0 +1,55 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ Command thread ]
//
// This is a background thread that runs, listening for commands on port
// 6868 for commands from the rasctl utility. This also includes the logic
// for handling the initialization commands that are specified at startup.
//
//---------------------------------------------------------------------------
#pragma once
#include "xm6.h"
#include "os.h"
#include "rasctl_command.h"
class Command_Thread{
public:
static BOOL Init();
static void Start();
static void Stop();
static BOOL IsRunning();
static void Close();
static void KillHandler();
static BOOL ParseArgument(int argc, char* argv[]);
static BOOL ParseConfig(int argc, char* argv[]);
static BOOL ExecuteCommand(FILE *fp, Rasctl_Command *new_command);
static void *MonThread(void *param);
static BOOL Parse_Rasctl_Message(char *buf, Rasctl_Command *cmd);
private:
static int m_monsocket; // Monitor Socket
static pthread_t m_monthread; // Monitor Thread
static BOOL m_running;
static BOOL HasSuffix(const char* string, const char* suffix);
static BOOL DoShutdown(FILE *fp, Rasctl_Command *incoming_command);
static BOOL DoList(FILE *fp, Rasctl_Command *incoming_command);
static BOOL DoAttach(FILE *fp, Rasctl_Command *incoming_command);
static BOOL DoDetach(FILE *fp, Rasctl_Command *incoming_command);
static BOOL DoInsert(FILE *fp, Rasctl_Command *incoming_command);
static BOOL DoEject(FILE *fp, Rasctl_Command *incoming_command);
static BOOL DoProtect(FILE *fp, Rasctl_Command *incoming_command);
};

View File

@@ -12,6 +12,13 @@
#if !defined(log_h)
#define log_h
#ifdef BAREMETAL
#define FPRT(fp, ...) printf( __VA_ARGS__ )
#else
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
#endif // BAREMETAL
//===========================================================================
//
// ログ

28
src/raspberrypi/os.cpp Normal file
View File

@@ -0,0 +1,28 @@
#include "os.h"
//---------------------------------------------------------------------------
//
// Pin the thread to a specific CPU
//
//---------------------------------------------------------------------------
void FixCpu(int cpu)
{
#ifndef BAREMETAL
cpu_set_t cpuset;
int cpus;
// Get the number of CPUs
CPU_ZERO(&cpuset);
sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
cpus = CPU_COUNT(&cpuset);
// Set the thread affinity
if (cpu < cpus) {
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
#else
return;
#endif
}

View File

@@ -153,4 +153,11 @@ typedef const char *LPCSTR;
#define xstrcasecmp strcasecmp
#define xstrncasecmp strncasecmp
//---------------------------------------------------------------------------
//
// Pin the thread to a specific CPU
//
//---------------------------------------------------------------------------
void FixCpu(int cpu);
#endif // os_h

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,406 @@
#include "rascsi_mgr.h"
#include "log.h"
#include "sasidev_ctrl.h"
#include "scsidev_ctrl.h"
Rascsi_Manager *Rascsi_Manager::m_instance = (Rascsi_Manager*)nullptr;
SASIDEV *Rascsi_Manager::m_ctrl[CtrlMax]; // Controller
Disk *Rascsi_Manager::m_disk[CtrlMax * UnitNum]; // Disk
GPIOBUS *Rascsi_Manager::m_bus;
int Rascsi_Manager::m_actid = -1;
BUS::phase_t Rascsi_Manager::m_phase = BUS::busfree;
BYTE Rascsi_Manager::m_data = 0;
DWORD Rascsi_Manager::m_now = 0;
BOOL Rascsi_Manager::m_active = FALSE;
BOOL Rascsi_Manager::m_running = FALSE;
std::timed_mutex Rascsi_Manager::m_locked;
Rascsi_Manager* Rascsi_Manager::GetInstance(){
if(m_instance != nullptr){
m_instance = new Rascsi_Manager();
}
return m_instance;
}
//---------------------------------------------------------------------------
//
// Controller Mapping
//
//---------------------------------------------------------------------------
void Rascsi_Manager::MapControler(FILE *fp, Disk **map)
{
int i;
int j;
int unitno;
int sasi_num;
int scsi_num;
// Replace the changed unit
for (i = 0; i < CtrlMax; i++) {
for (j = 0; j < UnitNum; j++) {
unitno = i * UnitNum + j;
if (m_disk[unitno] != map[unitno]) {
// Check if the original unit exists
if (m_disk[unitno]) {
// Disconnect it from the controller
if (m_ctrl[i]) {
m_ctrl[i]->SetUnit(j, NULL);
}
// Free the Unit
delete m_disk[unitno];
}
// Setup a new unit
m_disk[unitno] = map[unitno];
}
}
}
// Reconfigure all of the controllers
for (i = 0; i < CtrlMax; i++) {
// Examine the unit configuration
sasi_num = 0;
scsi_num = 0;
for (j = 0; j < UnitNum; j++) {
unitno = i * UnitNum + j;
// branch by unit type
if (m_disk[unitno]) {
if (m_disk[unitno]->IsSASI()) {
// Drive is SASI, so increment SASI count
sasi_num++;
} else {
// Drive is SCSI, so increment SCSI count
scsi_num++;
}
}
// Remove the unit
if (m_ctrl[i]) {
m_ctrl[i]->SetUnit(j, NULL);
}
}
// If there are no units connected
if (sasi_num == 0 && scsi_num == 0) {
if (m_ctrl[i]) {
delete m_ctrl[i];
m_ctrl[i] = NULL;
continue;
}
}
// Mixture of SCSI and SASI
if (sasi_num > 0 && scsi_num > 0) {
FPRT(fp, "Error : SASI and SCSI can't be mixed\n");
continue;
}
if (sasi_num > 0) {
// Only SASI Unit(s)
// Release the controller if it is not SASI
if (m_ctrl[i] && !m_ctrl[i]->IsSASI()) {
delete m_ctrl[i];
m_ctrl[i] = NULL;
}
// Create a new SASI controller
if (!m_ctrl[i]) {
m_ctrl[i] = new SASIDEV();
m_ctrl[i]->Connect(i, m_bus);
}
} else {
// Only SCSI Unit(s)
// Release the controller if it is not SCSI
if (m_ctrl[i] && !m_ctrl[i]->IsSCSI()) {
delete m_ctrl[i];
m_ctrl[i] = NULL;
}
// Create a new SCSI controller
if (!m_ctrl[i]) {
m_ctrl[i] = new SCSIDEV();
m_ctrl[i]->Connect(i, m_bus);
}
}
// connect all units
for (j = 0; j < UnitNum; j++) {
unitno = i * UnitNum + j;
if (m_disk[unitno]) {
// Add the unit connection
m_ctrl[i]->SetUnit(j, m_disk[unitno]);
}
}
}
}
//---------------------------------------------------------------------------
//
// Reset
//
//---------------------------------------------------------------------------
void Rascsi_Manager::Reset()
{
int i;
// Reset all of the controllers
for (i = 0; i < CtrlMax; i++) {
if (m_ctrl[i]) {
m_ctrl[i]->Reset();
}
}
// Reset the bus
m_bus->Reset();
}
//---------------------------------------------------------------------------
//
// List Devices
//
//---------------------------------------------------------------------------
void Rascsi_Manager::ListDevice(FILE *fp)
{
int i;
int id;
int un;
Disk *pUnit;
Filepath filepath;
BOOL find;
char type[5];
find = FALSE;
type[4] = 0;
for (i = 0; i < CtrlMax * UnitNum; i++) {
// Initialize ID and unit number
id = i / UnitNum;
un = i % UnitNum;
pUnit = m_disk[i];
// skip if unit does not exist or null disk
if (pUnit == NULL || pUnit->IsNULL()) {
continue;
}
// Output the header
if (!find) {
FPRT(fp, "\n");
FPRT(fp, "+----+----+------+-------------------------------------\n");
FPRT(fp, "| ID | UN | TYPE | DEVICE STATUS\n");
FPRT(fp, "+----+----+------+-------------------------------------\n");
find = TRUE;
}
// ID,UNIT,Type,Device Status
type[0] = (char)(pUnit->GetID() >> 24);
type[1] = (char)(pUnit->GetID() >> 16);
type[2] = (char)(pUnit->GetID() >> 8);
type[3] = (char)(pUnit->GetID());
FPRT(fp, "| %d | %d | %s | ", id, un, type);
// mount status output
if (pUnit->GetID() == MAKEID('S', 'C', 'B', 'R')) {
FPRT(fp, "%s", "HOST BRIDGE");
} else {
pUnit->GetPath(filepath);
FPRT(fp, "%s",
(pUnit->IsRemovable() && !pUnit->IsReady()) ?
"NO MEDIA" : filepath.GetPath());
}
// Write protection status
if (pUnit->IsRemovable() && pUnit->IsReady() && pUnit->IsWriteP()) {
FPRT(fp, "(WRITEPROTECT)");
}
// Goto the next line
FPRT(fp, "\n");
}
// If there is no controller, find will be null
if (!find) {
FPRT(fp, "No device is installed.\n");
return;
}
FPRT(fp, "+----+----+------+-------------------------------------\n");
}
void Rascsi_Manager::Close(){
// Delete the disks
for (int i = 0; i < (CtrlMax * UnitNum); i++) {
if (m_disk[i]) {
delete m_disk[i];
m_disk[i] = NULL;
}
}
// Delete the Controllers
for (int i = 0; i < CtrlMax; i++) {
if (m_ctrl[i]) {
delete m_ctrl[i];
m_ctrl[i] = NULL;
}
}
// Cleanup the Bus
m_bus->Cleanup();
// Discard the GPIOBUS object
delete m_bus;
}
BOOL Rascsi_Manager::Init(){
if(m_instance != nullptr){
printf("Can not initialize Rascsi_Manager twice!!!\n");
return FALSE;
}
m_instance = new Rascsi_Manager();
// GPIOBUS creation
m_bus = new GPIOBUS();
// GPIO Initialization
if (!m_bus->Init()) {
return FALSE;
}
// Bus Reset
m_bus->Reset();
// Controller initialization
for (int i = 0; i < CtrlMax; i++) {
m_ctrl[i] = NULL;
}
// Disk Initialization
for (int i = 0; i < CtrlMax; i++) {
m_disk[i] = NULL;
}
// Reset
Reset();
return TRUE;
}
BOOL Rascsi_Manager::Step()
{
m_locked.lock();
// Work initialization
m_actid = -1;
m_phase = BUS::busfree;
#ifdef USE_SEL_EVENT_ENABLE
// SEL signal polling
if (m_bus->PollSelectEvent() < 0) {
// Stop on interrupt
if (errno == EINTR) {
m_locked.unlock();
return FALSE;
}
m_locked.unlock();
return FALSE;
}
// Get the bus
m_bus->Aquire();
#else
m_bus->Aquire();
if (!m_bus->GetSEL()) {
#if !defined(BAREMETAL)
usleep(0);
#endif // !BAREMETAL
m_locked.unlock();
return FALSE;
}
#endif // USE_SEL_EVENT_ENABLE
// Wait until BSY is released as there is a possibility for the
// initiator to assert it while setting the ID (for up to 3 seconds)
if (m_bus->GetBSY()) {
m_now = SysTimer::GetTimerLow();
while ((SysTimer::GetTimerLow() - m_now) < 3 * 1000 * 1000) {
m_bus->Aquire();
if (!m_bus->GetBSY()) {
break;
}
}
}
// Stop because it the bus is busy or another device responded
if (m_bus->GetBSY() || !m_bus->GetSEL()) {
m_locked.unlock();
return FALSE;
}
// Notify all controllers
m_data = m_bus->GetDAT();
for (int i = 0; i < CtrlMax; i++) {
if (!m_ctrl[i] || (m_data & (1 << i)) == 0) {
continue;
}
// Find the target that has moved to the selection phase
if (m_ctrl[i]->Process() == BUS::selection) {
// Get the target ID
m_actid = i;
// Bus Selection phase
m_phase = BUS::selection;
break;
}
}
// Return to bus monitoring if the selection phase has not started
if (m_phase != BUS::selection) {
m_locked.unlock();
return FALSE;
}
// Start target device
m_active = TRUE;
#if !defined(USE_SEL_EVENT_ENABLE) && !defined(BAREMETAL)
// Scheduling policy setting (highest priority)
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
sched_setscheduler(0, SCHED_FIFO, &schparam);
#endif // !USE_SEL_EVENT_ENABLE && !BAREMETAL
// Loop until the bus is free
while (m_running) {
// Target drive
m_phase = m_ctrl[m_actid]->Process();
// End when the bus is free
if (m_phase == BUS::busfree) {
break;
}
}
#if !defined(USE_SEL_EVENT_ENABLE) && !defined(BAREMETAL)
// Set the scheduling priority back to normal
schparam.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &schparam);
#endif // !USE_SEL_EVENT_ENABLE && !BAREMETAL
// End the target travel
m_active = FALSE;
m_locked.unlock();
return TRUE;
}

View File

@@ -0,0 +1,62 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ RaSCSI Manager ]
//
// Any meaningful logic should be included in this file so that it can be
// tested using unit tests. The main "rascsi.cpp" file should contain very
// little functional code.
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
#include "scsi.h"
#include "fileio.h"
#include "disk.h"
#include "log.h"
#include "xm6.h"
#include "gpiobus.h"
#include "sasidev_ctrl.h"
#include <mutex>
class Rascsi_Manager{
public:
static Rascsi_Manager* GetInstance();
void MapControler(FILE *fp, Disk **map);
void ListDevice(FILE *fp);
BOOL Init();
void Close();
void Reset();
BOOL Step();
static const int CtrlMax = 8; // Maximum number of SCSI controllers
static const int UnitNum=2; // Number of units around controller
// TODO: These need to be made private. All of the functionality that is using
// them directly should be updated to be issolated.
// The Command_Thread class is WAAAAY too tightly coupled to this class.
//
static SASIDEV *m_ctrl[CtrlMax]; // Controller
static Disk *m_disk[CtrlMax * UnitNum]; // Disk
static GPIOBUS *m_bus; // GPIO Bus
private:
static int m_actid;
static BUS::phase_t m_phase;
static BYTE m_data;
static DWORD m_now;
static Rascsi_Manager *m_instance;
static BOOL m_active;
static BOOL m_running;
static std::timed_mutex m_locked;
};

View File

@@ -10,13 +10,14 @@
//---------------------------------------------------------------------------
#include "os.h"
#include "rasctl_command.h"
//---------------------------------------------------------------------------
//
// Send Command
//
//---------------------------------------------------------------------------
BOOL SendCommand(char *buf)
BOOL SendCommand(BYTE *buf)
{
int fd;
struct sockaddr_in server;
@@ -39,7 +40,7 @@ BOOL SendCommand(char *buf)
// Send the command
fp = fdopen(fd, "r+");
setvbuf(fp, NULL, _IONBF, 0);
fputs(buf, fp);
fputs((char*)buf, fp);
// Receive the message
while (1) {
@@ -66,20 +67,17 @@ int main(int argc, char* argv[])
int opt;
int id;
int un;
int cmd;
int type;
char *file;
BOOL list;
int len;
char *ext;
char buf[BUFSIZ];
BYTE buf[BUFSIZ];
rasctl_command cmd = rasctl_cmd_invalid;
rasctl_dev_type type = rasctl_dev_invalid;
Rasctl_Command *rasctl_cmd;
id = -1;
un = 0;
cmd = -1;
type = -1;
file = NULL;
list = FALSE;
// Display help
if (argc < 2) {
@@ -115,23 +113,27 @@ int main(int argc, char* argv[])
switch (optarg[0]) {
case 'a': // ATTACH
case 'A':
cmd = 0;
cmd = rasctl_cmd_attach;
break;
case 'd': // DETACH
case 'D':
cmd = 1;
cmd = rasctl_cmd_detach;
break;
case 'i': // INSERT
case 'I':
cmd = 2;
cmd = rasctl_cmd_insert;
break;
case 'e': // EJECT
case 'E':
cmd = 3;
cmd = rasctl_cmd_eject;
break;
case 'p': // PROTECT
case 'P':
cmd = 4;
cmd = rasctl_cmd_protect;
break;
case 's': // Shutdown the rasci service
case 'S':
cmd = rasctl_cmd_shutdown;
break;
}
break;
@@ -140,41 +142,36 @@ int main(int argc, char* argv[])
switch (optarg[0]) {
case 's': // HD(SASI)
case 'S':
type = rasctl_dev_sasi_hd;
break;
case 'h': // HD(SCSI)
case 'H':
type = 0;
type = rasctl_dev_scsi_hd;
break;
case 'm': // MO
case 'M':
type = 2;
type = rasctl_dev_mo;
break;
case 'c': // CD
case 'C':
type = 3;
type = rasctl_dev_cd;
break;
case 'b': // BRIDGE
case 'B':
type = 4;
type = rasctl_dev_br;
break;
}
break;
case 'f':
file = optarg;
break;
case 'l':
list = TRUE;
cmd = rasctl_cmd_list;
break;
}
}
// List display only
if (id < 0 && cmd < 0 && type < 0 && file == NULL && list) {
sprintf(buf, "list\n");
SendCommand(buf);
exit(0);
}
if((cmd != rasctl_cmd_list) && (cmd != rasctl_cmd_shutdown)){
// Check the ID number
if (id < 0 || id > 7) {
@@ -189,42 +186,46 @@ int main(int argc, char* argv[])
}
// Command check
if (cmd < 0) {
cmd = 0; // Default command is ATTATCH
if (cmd == rasctl_cmd_invalid) {
cmd = rasctl_cmd_attach; // Default command is ATTATCH
}
// Type Check
if (cmd == 0 && type < 0) {
if (cmd == rasctl_cmd_attach && type == rasctl_dev_invalid) {
// Try to determine the file type from the extension
len = file ? strlen(file) : 0;
if (len > 4 && file[len - 4] == '.') {
ext = &file[len - 3];
if (xstrcasecmp(ext, "hdf") == 0 ||
xstrcasecmp(ext, "hds") == 0 ||
xstrcasecmp(ext, "hdn") == 0 ||
if (xstrcasecmp(ext, "hdf") == 0){
type = rasctl_dev_sasi_hd;
}else if(xstrcasecmp(ext, "hds") == 0 ||
xstrcasecmp(ext, "hdi") == 0 ||
xstrcasecmp(ext, "nhd") == 0 ||
xstrcasecmp(ext, "hda") == 0) {
// HD(SASI/SCSI)
type = 0;
xstrcasecmp(ext, "nhd") == 0){
type = rasctl_dev_scsi_hd;
}else if(xstrcasecmp(ext, "hdn") == 0) {
// NEC HD(SASI/SCSI)
type = rasctl_dev_scsi_hd_appl;
}else if(xstrcasecmp(ext, "hda") == 0) {
// Apple HD(SASI/SCSI)
type = rasctl_dev_scsi_hd_appl;
}else if (xstrcasecmp(ext, "mos") == 0) {
// MO
type = 2;
type = rasctl_dev_mo;
}else if (xstrcasecmp(ext, "iso") == 0) {
// CD
type = 3;
type = rasctl_dev_cd;
}
}
if (type < 0) {
if (type == rasctl_dev_invalid) {
fprintf(stderr, "Error : Invalid type\n");
exit(EINVAL);
}
}
// File check (command is ATTACH and type is HD)
if (cmd == 0 && type >= 0 && type <= 1) {
if ((cmd == rasctl_cmd_attach) && (Rasctl_Command::rasctl_dev_is_hd(type))){
if (!file) {
fprintf(stderr, "Error : Invalid file path\n");
exit(EINVAL);
@@ -232,30 +233,35 @@ int main(int argc, char* argv[])
}
// File check (command is INSERT)
if (cmd == 2) {
if (cmd == rasctl_cmd_insert) {
if (!file) {
fprintf(stderr, "Error : Invalid file path\n");
exit(EINVAL);
}
}
// Set unnecessary type to 0
if (type < 0) {
type = 0;
// If we don't know what the type is, default to SCSI HD
if (type == rasctl_dev_invalid) {
type = rasctl_dev_scsi_hd;
}
}
rasctl_cmd = new Rasctl_Command();
rasctl_cmd->type = type;
rasctl_cmd->un = un;
rasctl_cmd->id = id;
rasctl_cmd->cmd = cmd;
if(file){
strncpy(rasctl_cmd->file,file,_MAX_PATH);
}
// Generate the command and send it
sprintf(buf, "%d %d %d %d %s\n", id, un, cmd, type, file ? file : "-");
rasctl_cmd->Serialize(buf,BUFSIZ);
if (!SendCommand(buf)) {
exit(ENOTCONN);
}
// Display the list
if (list) {
sprintf(buf, "list\n");
SendCommand(buf);
}
// All done!
exit(0);
}

View File

@@ -0,0 +1,72 @@
#include "rasctl_command.h"
//const char* Rasctl_Command::m_delimiter = "\x1E"; // Record separator charater
const char* Rasctl_Command::m_delimiter = " "; // Record separator charater
void Rasctl_Command::Serialize(BYTE *buff, int max_buff_size){
snprintf((char*)buff, max_buff_size, "%d%s%d%s%d%s%d%s%s%s",
this->id, this->m_delimiter,
this->un, this->m_delimiter,
this->cmd, this->m_delimiter,
this->type, this->m_delimiter,
this->file, this->m_delimiter);
}
Rasctl_Command* Rasctl_Command::DeSerialize(BYTE* buff, int size){
Rasctl_Command *return_command = new Rasctl_Command();
char err_message[256];
char *cur_token;
char *command = (char*)buff;
serial_token_order cur_token_idx = serial_token_first_token;
cur_token = strtok(command, m_delimiter);
// Loop through all of the tokens in the message
while (cur_token != NULL){
switch(cur_token_idx){
case serial_token_cmd:
return_command->cmd = (rasctl_command)atoi(cur_token);
break;
case serial_token_type:
return_command->type = (rasctl_dev_type)atoi(cur_token);
break;
case serial_token_file_name:
strncpy(return_command->file,cur_token,_MAX_PATH);
break;
case serial_token_id:
return_command->id = atoi(cur_token);
break;
case serial_token_un:
return_command->un = atoi(cur_token);
break;
default:
for(int i=0; i<size; i++)
{
snprintf(&err_message[i*2], sizeof(err_message), "%02X", buff[i]);
}
printf("Received too many tokens: %s", err_message);
free(return_command);
return nullptr;
break;
}
cur_token_idx = (serial_token_order)((int)cur_token_idx + 1);
}
if(cur_token_idx != serial_token_last_token)
{
for(int i=0; i<size; i++)
{
snprintf(&err_message[i*2], sizeof(err_message), "%02X", buff[i]);
}
printf("Received too few tokens: %s", err_message);
free(return_command);
}
return return_command;
}
BOOL Rasctl_Command::rasctl_dev_is_hd(rasctl_dev_type type)
{
return ((type == rasctl_dev_scsi_hd) || (type == rasctl_dev_sasi_hd) ||
(type == rasctl_dev_scsi_hd_appl) || (type == rasctl_dev_scsi_hd_nec));
}

View File

@@ -0,0 +1,66 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) akuker
//
// [ Send Control Command ]
//
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
enum rasctl_command : int {
rasctl_cmd_invalid = -1,
rasctl_cmd_attach = 0,
rasctl_cmd_detach = 1,
rasctl_cmd_insert = 2,
rasctl_cmd_eject = 3,
rasctl_cmd_protect = 4,
rasctl_cmd_list = 5,
rasctl_cmd_op = 6,
rasctl_cmd_shutdown = 7,
};
enum rasctl_dev_type : int {
rasctl_dev_invalid = -1,
rasctl_dev_scsi_hd = 0,
rasctl_dev_sasi_hd = 0,
rasctl_dev_mo = 2,
rasctl_dev_cd = 3,
rasctl_dev_br = 4,
rasctl_dev_scsi_hd_appl = 5,
rasctl_dev_scsi_hd_nec = 6,
};
class Rasctl_Command{
public:
void Serialize(BYTE *buff, int max_buff_size);
static Rasctl_Command* DeSerialize(BYTE* buff, int size);
rasctl_command cmd = rasctl_cmd_invalid;
rasctl_dev_type type = rasctl_dev_invalid;
int id = -1;
int un = -1;
char file[_MAX_PATH];
static BOOL rasctl_dev_is_hd(rasctl_dev_type dev);
private:
static const char* m_delimiter; // Record separator charater
enum serial_token_order : int {
serial_token_first_token = 0,
serial_token_id = 0,
serial_token_un = 1,
serial_token_cmd = 2,
serial_token_type = 3,
serial_token_file_name = 4,
serial_token_last_token = 5,
};
};