diff --git a/src/raspberrypi/.vscode/launch.json b/src/raspberrypi/.vscode/launch.json new file mode 100644 index 0000000..4ac5ab4 --- /dev/null +++ b/src/raspberrypi/.vscode/launch.json @@ -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 + } + ] + } + ] +} \ No newline at end of file diff --git a/src/raspberrypi/.vscode/settings.json b/src/raspberrypi/.vscode/settings.json new file mode 100644 index 0000000..5ba8582 --- /dev/null +++ b/src/raspberrypi/.vscode/settings.json @@ -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" + } +} \ No newline at end of file diff --git a/src/raspberrypi/.vscode/tasks.json b/src/raspberrypi/.vscode/tasks.json new file mode 100644 index 0000000..eeedfe8 --- /dev/null +++ b/src/raspberrypi/.vscode/tasks.json @@ -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 + } + } + ] + } \ No newline at end of file diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index 84a6fe4..d27e816 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -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) diff --git a/src/raspberrypi/command_thread.cpp b/src/raspberrypi/command_thread.cpp new file mode 100644 index 0000000..b43ce2a --- /dev/null +++ b/src/raspberrypi/command_thread.cpp @@ -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); +} + diff --git a/src/raspberrypi/command_thread.h b/src/raspberrypi/command_thread.h new file mode 100644 index 0000000..3887640 --- /dev/null +++ b/src/raspberrypi/command_thread.h @@ -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); + +}; + diff --git a/src/raspberrypi/log.h b/src/raspberrypi/log.h index 617a747..dedbb2a 100644 --- a/src/raspberrypi/log.h +++ b/src/raspberrypi/log.h @@ -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 + + //=========================================================================== // // ログ diff --git a/src/raspberrypi/os.cpp b/src/raspberrypi/os.cpp new file mode 100644 index 0000000..2d4f3c7 --- /dev/null +++ b/src/raspberrypi/os.cpp @@ -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 +} diff --git a/src/raspberrypi/os.h b/src/raspberrypi/os.h index e744559..a85ff10 100644 --- a/src/raspberrypi/os.h +++ b/src/raspberrypi/os.h @@ -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 diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 7b90485..96419ae 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -5,6 +5,8 @@ // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS +// Copyright (C) akuker +// // [ RaSCSI main ] // //--------------------------------------------------------------------------- @@ -15,6 +17,8 @@ #include "fileio.h" #include "disk.h" #include "gpiobus.h" +#include "command_thread.h" +#include "rascsi_mgr.h" #include "sasidev_ctrl.h" #include "scsidev_ctrl.h" @@ -32,31 +36,17 @@ // Constant declarations // //--------------------------------------------------------------------------- -#define CtrlMax 8 // Maximum number of SCSI controllers -#define UnitNum 2 // Number of units around controller -#ifdef BAREMETAL -#define FPRT(fp, ...) printf( __VA_ARGS__ ) -#else -#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ ) -#endif // BAREMETAL //--------------------------------------------------------------------------- // // Variable declarations // //--------------------------------------------------------------------------- -static volatile BOOL running; // Running flag -static volatile BOOL active; // Processing flag -SASIDEV *ctrl[CtrlMax]; // Controller -Disk *disk[CtrlMax * UnitNum]; // Disk -GPIOBUS *bus; // GPIO Bus #ifdef BAREMETAL FATFS fatfs; // FatFS #else -int monsocket; // Monitor Socket -pthread_t monthread; // Monitor Thread -static void *MonThread(void *param); #endif // BAREMETAL +Rascsi_Manager *mgr; #ifndef BAREMETAL //--------------------------------------------------------------------------- @@ -67,7 +57,7 @@ static void *MonThread(void *param); void KillHandler(int sig) { // Stop instruction - running = FALSE; + Command_Thread::Stop(); } #endif // BAREMETAL @@ -123,36 +113,15 @@ void Banner(int argc, char* argv[]) //--------------------------------------------------------------------------- BOOL Init() { - int i; - -#ifndef BAREMETAL - struct sockaddr_in server; - int yes; - - // Create socket for monitor - 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( - monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0){ + if(!Command_Thread::Init()){ return FALSE; } - // Bind - if (bind(monsocket, (struct sockaddr *)&server, - sizeof(struct sockaddr_in)) < 0) { - FPRT(stderr, "Error : Already running?\n"); + mgr = Rascsi_Manager::GetInstance(); + if(!mgr->Init()){ return FALSE; } - // Create Monitor Thread - pthread_create(&monthread, NULL, MonThread, NULL); - // Interrupt handler settings if (signal(SIGINT, KillHandler) == SIG_ERR) { return FALSE; @@ -163,32 +132,6 @@ BOOL Init() if (signal(SIGTERM, KillHandler) == SIG_ERR) { return FALSE; } -#endif // BAREMETAL - - // GPIOBUS creation - bus = new GPIOBUS(); - - // GPIO Initialization - if (!bus->Init()) { - return FALSE; - } - - // Bus Reset - bus->Reset(); - - // Controller initialization - for (i = 0; i < CtrlMax; i++) { - ctrl[i] = NULL; - } - - // Disk Initialization - for (i = 0; i < CtrlMax; i++) { - disk[i] = NULL; - } - - // Other - running = FALSE; - active = FALSE; return TRUE; } @@ -200,848 +143,13 @@ BOOL Init() //--------------------------------------------------------------------------- void Cleanup() { - int i; - - // Delete the disks - for (i = 0; i < CtrlMax * UnitNum; i++) { - if (disk[i]) { - delete disk[i]; - disk[i] = NULL; - } - } - - // Delete the Controllers - for (i = 0; i < CtrlMax; i++) { - if (ctrl[i]) { - delete ctrl[i]; - ctrl[i] = NULL; - } - } - - // Cleanup the Bus - bus->Cleanup(); - - // Discard the GPIOBUS object - delete bus; + mgr->Close(); #ifndef BAREMETAL - // Close the monitor socket - if (monsocket >= 0) { - close(monsocket); - } + Command_Thread::Close(); #endif // BAREMETAL } -//--------------------------------------------------------------------------- -// -// Reset -// -//--------------------------------------------------------------------------- -void Reset() -{ - int i; - - // Reset all of the controllers - for (i = 0; i < CtrlMax; i++) { - if (ctrl[i]) { - ctrl[i]->Reset(); - } - } - - // Reset the bus - bus->Reset(); -} - -//--------------------------------------------------------------------------- -// -// List Devices -// -//--------------------------------------------------------------------------- -void 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 = 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"); -} - -//--------------------------------------------------------------------------- -// -// Controller Mapping -// -//--------------------------------------------------------------------------- -void 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 (disk[unitno] != map[unitno]) { - // Check if the original unit exists - if (disk[unitno]) { - // Disconnect it from the controller - if (ctrl[i]) { - ctrl[i]->SetUnit(j, NULL); - } - - // Free the Unit - delete disk[unitno]; - } - - // Setup a new unit - 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 (disk[unitno]) { - if (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 (ctrl[i]) { - ctrl[i]->SetUnit(j, NULL); - } - } - - // If there are no units connected - if (sasi_num == 0 && scsi_num == 0) { - if (ctrl[i]) { - delete ctrl[i]; - 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 (ctrl[i] && !ctrl[i]->IsSASI()) { - delete ctrl[i]; - ctrl[i] = NULL; - } - - // Create a new SASI controller - if (!ctrl[i]) { - ctrl[i] = new SASIDEV(); - ctrl[i]->Connect(i, bus); - } - } else { - // Only SCSI Unit(s) - - // Release the controller if it is not SCSI - if (ctrl[i] && !ctrl[i]->IsSCSI()) { - delete ctrl[i]; - ctrl[i] = NULL; - } - - // Create a new SCSI controller - if (!ctrl[i]) { - ctrl[i] = new SCSIDEV(); - ctrl[i]->Connect(i, bus); - } - } - - // connect all units - for (j = 0; j < UnitNum; j++) { - unitno = i * UnitNum + j; - if (disk[unitno]) { - // Add the unit connection - ctrl[i]->SetUnit(j, disk[unitno]); - } - } - } -} - -//--------------------------------------------------------------------------- -// -// Command Processing -// -//--------------------------------------------------------------------------- -BOOL ProcessCmd(FILE *fp, int id, int un, int cmd, int type, char *file) -{ - Disk *map[CtrlMax * UnitNum]; - int len; - char *ext; - Filepath filepath; - Disk *pUnit; - - // Copy the Unit List - memcpy(map, disk, sizeof(disk)); - - // Check the Controller Number - if (id < 0 || id >= CtrlMax) { - FPRT(fp, "Error : Invalid ID\n"); - return FALSE; - } - - // Check the Unit Number - if (un < 0 || un >= UnitNum) { - FPRT(fp, "Error : Invalid unit number\n"); - return FALSE; - } - - // Connect Command - if (cmd == 0) { // ATTACH - // Distinguish between SASI and SCSI - ext = NULL; - pUnit = NULL; - 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; - } - } - - // 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 * UnitNum + un] = pUnit; - - // Re-map the controller - 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 (ctrl[id] == NULL) { - FPRT(fp, "Error : No such device\n"); - return FALSE; - } - - // Does the unit exist? - pUnit = disk[id * 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 * UnitNum + un] = NULL; - - // Re-map the controller - 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 has_suffix(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); -} - -//--------------------------------------------------------------------------- -// -// 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 (!ProcessCmd(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 -bool 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 (*end || (id < 0) || (max_id < id)) { - fprintf(stderr, "%s: invalid %s (0-%d)\n", - optarg, is_sasi ? "HD" : "ID", max_id); - return false; - } - continue; - } - - default: - return false; - - case 1: - break; - } - - if (id < 0) { - fprintf(stderr, "%s: ID not specified\n", optarg); - return false; - } else if (disk[id] && !disk[id]->IsNULL()) { - fprintf(stderr, "%d: duplicate ID\n", id); - return false; - } - - char* path = optarg; - int type = -1; - if (has_suffix(path, ".hdf") - || has_suffix(path, ".hds") - || has_suffix(path, ".hdn") - || has_suffix(path, ".hdi") - || has_suffix(path, ".hda") - || has_suffix(path, ".nhd")) { - type = 0; - } else if (has_suffix(path, ".mos")) { - type = 2; - } else if (has_suffix(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 % UnitNum; - id /= UnitNum; - } - - // Execute the command - if (!ProcessCmd(stderr, id, un, 0, type, path)) { - return false; - } - id = -1; - } - - // Display the device list - ListDevice(stdout); - return true; -} -#endif // BAREMETAL - -#ifndef BAREMETAL -//--------------------------------------------------------------------------- -// -// Pin the thread to a specific CPU -// -//--------------------------------------------------------------------------- -void FixCpu(int cpu) -{ - 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); - } -} - -//--------------------------------------------------------------------------- -// -// Monitor Thread -// -//--------------------------------------------------------------------------- -static void *MonThread(void *param) -{ - struct sched_param schedparam; - struct sockaddr_in client; - socklen_t len; - int fd; - FILE *fp; - char buf[BUFSIZ]; - char *p; - int i; - char *argv[5]; - int id; - int un; - int cmd; - int type; - char *file; - - // 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 (!running) { - usleep(1); - } - - // Setup the monitor socket to receive commands - listen(monsocket, 1); - - while (1) { - // Wait for connection - memset(&client, 0, sizeof(client)); - len = sizeof(client); - fd = accept(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) { - ListDevice(fp); - goto next; - } - - // Parameter separation - argv[0] = p; - for (i = 1; i < 5; i++) { - // Skip parameter values - while (*p && (*p != ' ')) { - p++; - } - - // Replace spaces with null characters - while (*p && (*p == ' ')) { - *p++ = 0; - } - - // The parameters were lost - if (!*p) { - break; - } - - // Recognized as a parameter - argv[i] = p; - } - - // Failed to get all parameters - if (i < 5) { - goto next; - } - - // ID, unit, command, type, file - id = atoi(argv[0]); - un = atoi(argv[1]); - cmd = atoi(argv[2]); - type = atoi(argv[3]); - file = argv[4]; - - // Wait until we becom idle - while (active) { - usleep(500 * 1000); - } - - // Execute the command - ProcessCmd(fp, id, un, cmd, type, file); - -next: - // Release the connection - fclose(fp); - close(fd); - } - - return NULL; -} -#endif // BAREMETAL - //--------------------------------------------------------------------------- // // Main processing @@ -1057,12 +165,7 @@ int startrascsi(void) int main(int argc, char* argv[]) { #endif // BAREMETAL - int i; int ret; - int actid; - DWORD now; - BUS::phase_t phase; - BYTE data; #ifndef BAREMETAL struct sched_param schparam; #endif // BAREMETAL @@ -1077,31 +180,24 @@ int main(int argc, char* argv[]) goto init_exit; } - // Reset - Reset(); #ifdef BAREMETAL // BUSY assert (to hold the host side) - bus->SetBSY(TRUE); + Rascsi_Manager::GetInstance()->m_bus->SetBSY(TRUE); // Argument parsing - if (!ParseConfig(argc, argv)) { + if (!Command_Thread::ParseConfig(argc, argv)) { ret = EINVAL; goto err_exit; } -#else - // Argument parsing - if (!ParseArgument(argc, argv)) { - ret = EINVAL; - goto err_exit; - } -#endif - -#ifdef BAREMETAL // Release the busy signal - bus->SetBSY(FALSE); + Rascsi_Manager::GetInstance()->m_bus->SetBSY(FALSE); + #endif + // For non-baremetal versions, we won't process the startup arguments... yet + + #ifndef BAREMETAL // Set the affinity to a specific processor core FixCpu(3); @@ -1114,104 +210,11 @@ int main(int argc, char* argv[]) #endif // BAREMETAL // Start execution - running = TRUE; + Command_Thread::Start(); // Main Loop - while (running) { - // Work initialization - actid = -1; - phase = BUS::busfree; - -#ifdef USE_SEL_EVENT_ENABLE - // SEL signal polling - if (bus->PollSelectEvent() < 0) { - // Stop on interrupt - if (errno == EINTR) { - break; - } - continue; - } - - // Get the bus - bus->Aquire(); -#else - bus->Aquire(); - if (!bus->GetSEL()) { -#if !defined(BAREMETAL) - usleep(0); -#endif // !BAREMETAL - continue; - } -#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 (bus->GetBSY()) { - now = SysTimer::GetTimerLow(); - while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { - bus->Aquire(); - if (!bus->GetBSY()) { - break; - } - } - } - - // Stop because it the bus is busy or another device responded - if (bus->GetBSY() || !bus->GetSEL()) { - continue; - } - - // Notify all controllers - data = bus->GetDAT(); - for (i = 0; i < CtrlMax; i++) { - if (!ctrl[i] || (data & (1 << i)) == 0) { - continue; - } - - // Find the target that has moved to the selection phase - if (ctrl[i]->Process() == BUS::selection) { - // Get the target ID - actid = i; - - // Bus Selection phase - phase = BUS::selection; - break; - } - } - - // Return to bus monitoring if the selection phase has not started - if (phase != BUS::selection) { - continue; - } - - // Start target device - 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 (running) { - // Target drive - phase = ctrl[actid]->Process(); - - // End when the bus is free - if (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 - active = FALSE; + while (Command_Thread::IsRunning()) { + mgr->Step(); } err_exit: diff --git a/src/raspberrypi/rascsi_mgr.cpp b/src/raspberrypi/rascsi_mgr.cpp new file mode 100644 index 0000000..0e70e8e --- /dev/null +++ b/src/raspberrypi/rascsi_mgr.cpp @@ -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; + +} diff --git a/src/raspberrypi/rascsi_mgr.h b/src/raspberrypi/rascsi_mgr.h new file mode 100644 index 0000000..3e19138 --- /dev/null +++ b/src/raspberrypi/rascsi_mgr.h @@ -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 + +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; +}; diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index d381e6e..7fd4550 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -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,122 +142,126 @@ 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) { - fprintf(stderr, "Error : Invalid ID\n"); - exit(EINVAL); - } + // Check the ID number + if (id < 0 || id > 7) { + fprintf(stderr, "Error : Invalid ID\n"); + exit(EINVAL); + } - // Check the unit number - if (un < 0 || un > 1) { - fprintf(stderr, "Error : Invalid UNIT\n"); - exit(EINVAL); - } + // Check the unit number + if (un < 0 || un > 1) { + fprintf(stderr, "Error : Invalid UNIT\n"); + exit(EINVAL); + } - // Command check - if (cmd < 0) { - cmd = 0; // Default command is ATTATCH - } + // Command check + if (cmd == rasctl_cmd_invalid) { + cmd = rasctl_cmd_attach; // Default command is ATTATCH + } - // Type Check - if (cmd == 0 && type < 0) { + // Type Check + 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 || - xstrcasecmp(ext, "hdi") == 0 || - xstrcasecmp(ext, "nhd") == 0 || - xstrcasecmp(ext, "hda") == 0) { - // HD(SASI/SCSI) - type = 0; - } else if (xstrcasecmp(ext, "mos") == 0) { - // MO - type = 2; - } else if (xstrcasecmp(ext, "iso") == 0) { - // CD - type = 3; + // 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){ + type = rasctl_dev_sasi_hd; + }else if(xstrcasecmp(ext, "hds") == 0 || + xstrcasecmp(ext, "hdi") == 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 = rasctl_dev_mo; + }else if (xstrcasecmp(ext, "iso") == 0) { + // CD + type = rasctl_dev_cd; + } + } + + if (type == rasctl_dev_invalid) { + fprintf(stderr, "Error : Invalid type\n"); + exit(EINVAL); } } - if (type < 0) { - fprintf(stderr, "Error : Invalid type\n"); - exit(EINVAL); + // File check (command is ATTACH and type is HD) + if ((cmd == rasctl_cmd_attach) && (Rasctl_Command::rasctl_dev_is_hd(type))){ + if (!file) { + fprintf(stderr, "Error : Invalid file path\n"); + exit(EINVAL); + } + } + + // File check (command is INSERT) + if (cmd == rasctl_cmd_insert) { + if (!file) { + fprintf(stderr, "Error : Invalid file path\n"); + exit(EINVAL); + } + } + + // If we don't know what the type is, default to SCSI HD + if (type == rasctl_dev_invalid) { + type = rasctl_dev_scsi_hd; } } - // File check (command is ATTACH and type is HD) - if (cmd == 0 && type >= 0 && type <= 1) { - if (!file) { - fprintf(stderr, "Error : Invalid file path\n"); - exit(EINVAL); - } - } - - // File check (command is INSERT) - if (cmd == 2) { - if (!file) { - fprintf(stderr, "Error : Invalid file path\n"); - exit(EINVAL); - } - } - - // Set unnecessary type to 0 - if (type < 0) { - type = 0; + 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); } diff --git a/src/raspberrypi/rasctl_command.cpp b/src/raspberrypi/rasctl_command.cpp new file mode 100644 index 0000000..311d139 --- /dev/null +++ b/src/raspberrypi/rasctl_command.cpp @@ -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