diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index 71c0f07c..a5e72663 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -28,7 +28,7 @@ RASCSI = rascsi RASCTL = rasctl RASDUMP = rasdump SASIDUMP = sasidump -SCSIMON = scsimon +SCSISHARK = scsishark USR_LOCAL_BIN = /usr/local/bin MAN_PAGE_DIR = /usr/share/man/man1 @@ -37,7 +37,7 @@ DOC_DIR = ../../doc #BIN_ALL = $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON) # Temporarily remove the RASDUMP and RASDUMP tools, since they're not needed # for my specific use case. If you need them - add them back in! -BIN_ALL = $(RASCSI) $(RASCTL) +BIN_ALL = $(RASCSI) $(RASCTL) $(SCSISHARK) SRC_RASCSI = \ @@ -50,6 +50,16 @@ SRC_RASCSI = \ filepath.cpp \ fileio.cpp +SRC_SCSISHARK = \ + scsishark.cpp \ + scsi.cpp \ + disk.cpp \ + gpiobus.cpp \ + ctapdriver.cpp \ + cfilesystem.cpp \ + filepath.cpp \ + fileio.cpp + SRC_RASCTL = \ rasctl.cpp @@ -71,9 +81,9 @@ OBJ_RASCSI := $(SRC_RASCSI:%.cpp=%.o) OBJ_RASCTL := $(SRC_RASCTL:%.cpp=%.o) OBJ_RASDUMP := $(SRC_RASDUMP:%.cpp=%.o) OBJ_SASIDUMP := $(SRC_SASIDUMP:%.cpp=%.o) -OBJ_SCSIMON := $(SRC_SCSIMON:%.cpp=%.o) +OBJ_SCSISHARK := $(SRC_SCSISHARK:%.cpp=%.o) #OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON) -OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) +OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSISHARK) %.o: %.cpp $(CXX) $(CXXFLAGS) -c $< -o $@ @@ -88,19 +98,19 @@ ALL: all docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(RASCSI): $(OBJ_RASCSI) - $(CXX) -o $@ $(OBJ_RASCSI) -lpthread + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread $(RASCTL): $(OBJ_RASCTL) - $(CXX) -o $@ $(OBJ_RASCTL) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) $(RASDUMP): $(OBJ_RASDUMP) - $(CXX) -o $@ $(OBJ_RASDUMP) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) $(SASIDUMP): $(OBJ_SASIDUMP) - $(CXX) -o $@ $(OBJ_SASIDUMP) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SASIDUMP) -$(SCSIMON): $(OBJ_SCSIMON) - $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread +$(SCSISHARK): $(OBJ_SCSISHARK) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSISHARK) -lpthread clean: rm -f $(OBJ_ALL) $(BIN_ALL) diff --git a/src/raspberrypi/scsishark.cpp b/src/raspberrypi/scsishark.cpp new file mode 100644 index 00000000..1aca3f6e --- /dev/null +++ b/src/raspberrypi/scsishark.cpp @@ -0,0 +1,1459 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Powered by XM6 TypeG Technology. +// Copyright (C) 2016-2020 GIMONS +// [ RaSCSI main ] +// +//--------------------------------------------------------------------------- + +#include "os.h" +#include "xm6.h" +#include "filepath.h" +#include "fileio.h" +#include "disk.h" +#include "gpiobus.h" +#include "spdlog/spdlog.h" +//#include +#include + +//--------------------------------------------------------------------------- +// +// 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 +typedef struct data_capture{ + DWORD data; + timeval timestamp; +} data_capture_t; + + +#define MAX_BUFF_SIZE 1000000 + +data_capture data_buffer[MAX_BUFF_SIZE]; +int data_idx = 0; + + + +#define SECONDS_1 (1000 * 1000) +#define SECONDS_3 (3 * 1000 * 1000) +#define WAIT_FOR_EQUAL(x,y,timeout) { DWORD now = SysTimer::GetTimerLow(); while ((SysTimer::GetTimerLow() - now) < timeout) { bus->Aquire();if (x == y) {break;}}} + + +#ifndef BAREMETAL +//--------------------------------------------------------------------------- +// +// Signal Processing +// +//--------------------------------------------------------------------------- +void KillHandler(int sig) +{ + // Stop instruction + running = FALSE; +} +#endif // BAREMETAL + +//--------------------------------------------------------------------------- +// +// Banner Output +// +//--------------------------------------------------------------------------- +void Banner(int argc, char* argv[]) +{ + FPRT(stdout,"SCSI Target Emulator RaSCSI(*^..^*) "); + FPRT(stdout,"version %01d.%01d%01d(%s, %s)\n", + (int)((VERSION >> 8) & 0xf), + (int)((VERSION >> 4) & 0xf), + (int)((VERSION ) & 0xf), + __DATE__, + __TIME__); + FPRT(stdout,"Powered by XM6 TypeG Technology / "); + FPRT(stdout,"Copyright (C) 2016-2020 GIMONS\n"); + FPRT(stdout,"Connect type : %s\n", CONNECT_DESC); + + if ((argc > 1 && strcmp(argv[1], "-h") == 0) || + (argc > 1 && strcmp(argv[1], "--help") == 0)){ + FPRT(stdout,"\n"); + FPRT(stdout,"Usage: %s [-IDn FILE] ...\n\n", argv[0]); + FPRT(stdout," n is SCSI identification number(0-7).\n"); + FPRT(stdout," FILE is disk image file.\n\n"); + FPRT(stdout,"Usage: %s [-HDn FILE] ...\n\n", argv[0]); + FPRT(stdout," n is X68000 SASI HD number(0-15).\n"); + FPRT(stdout," FILE is disk image file.\n\n"); + FPRT(stdout," Image type is detected based on file extension.\n"); + FPRT(stdout," hdf : SASI HD image(XM6 SASI HD image)\n"); + FPRT(stdout," hds : SCSI HD image(XM6 SCSI HD image)\n"); + FPRT(stdout," hdn : SCSI HD image(NEC GENUINE)\n"); + FPRT(stdout," hdi : SCSI HD image(Anex86 HD image)\n"); + FPRT(stdout," nhd : SCSI HD image(T98Next HD image)\n"); + FPRT(stdout," hda : SCSI HD image(APPLE GENUINE)\n"); + FPRT(stdout," mos : SCSI MO image(XM6 SCSI MO image)\n"); + FPRT(stdout," iso : SCSI CD image(ISO 9660 image)\n"); + +#ifndef BAREMETAL + exit(0); +#endif // BAREMETAL + } +} + +//--------------------------------------------------------------------------- +// +// Initialization +// +//--------------------------------------------------------------------------- +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){ + return FALSE; + } + + // Bind + if (bind(monsocket, (struct sockaddr *)&server, + sizeof(struct sockaddr_in)) < 0) { + FPRT(stderr, "Error : Already running?\n"); + return FALSE; + } + + // Create Monitor Thread + pthread_create(&monthread, NULL, MonThread, NULL); + + // Interrupt handler settings + if (signal(SIGINT, KillHandler) == SIG_ERR) { + return FALSE; + } + if (signal(SIGHUP, KillHandler) == SIG_ERR) { + return FALSE; + } + 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; +} + +#define PIN_ACT 4 // ACTIVE +#define PIN_ENB 5 // ENABLE +#define PIN_IND -1 // INITIATOR CTRL DIRECTION +#define PIN_TAD -1 // TARGET CTRL DIRECTION +#define PIN_DTD -1 // DATA DIRECTION + +// Control signal output logic +#define ACT_ON TRUE // ACTIVE SIGNAL ON +#define ENB_ON TRUE // ENABLE SIGNAL ON +#define IND_IN FALSE // INITIATOR SIGNAL INPUT +#define TAD_IN FALSE // TARGET SIGNAL INPUT +#define DTD_IN TRUE // DATA SIGNAL INPUT + +// SCSI signal pin assignment +#define PIN_DT0 10 // Data 0 +#define PIN_DT1 11 // Data 1 +#define PIN_DT2 12 // Data 2 +#define PIN_DT3 13 // Data 3 +#define PIN_DT4 14 // Data 4 +#define PIN_DT5 15 // Data 5 +#define PIN_DT6 16 // Data 6 +#define PIN_DT7 17 // Data 7 +#define PIN_DP 18 // Data parity +#define PIN_ATN 19 // ATN +#define PIN_RST 20 // RST +#define PIN_ACK 21 // ACK +#define PIN_REQ 22 // REQ +#define PIN_MSG 23 // MSG +#define PIN_CD 24 // CD +#define PIN_IO 25 // IO +#define PIN_BSY 26 // BSY +#define PIN_SEL 27 // SEL + + +BOOL get_pin_value(DWORD data, int pin) +{ + return (data >> pin) & 1; +} + +BYTE get_data_field(DWORD data) +{ + DWORD data_out; + data_out = + ((data >> (PIN_DT0 - 0)) & (1 << 0)) | + ((data >> (PIN_DT1 - 1)) & (1 << 1)) | + ((data >> (PIN_DT2 - 2)) & (1 << 2)) | + ((data >> (PIN_DT3 - 3)) & (1 << 3)) | + ((data >> (PIN_DT4 - 4)) & (1 << 4)) | + ((data >> (PIN_DT5 - 5)) & (1 << 5)) | + ((data >> (PIN_DT6 - 6)) & (1 << 6)) | + ((data >> (PIN_DT7 - 7)) & (1 << 7)); + + return (BYTE)data_out; +} + + +int pin_nums[] = {PIN_BSY,PIN_SEL,PIN_CD,PIN_IO,PIN_MSG,PIN_REQ,PIN_ACK,PIN_ATN,PIN_RST,PIN_DT0}; + +char* pin_names[] = {"BSY","SEL","CD","IO","MSG","REQ","ACK","ATN","RST","DAT"}; + + +void dump_data() +{ + char outstr[1024]; + int i = 0; + timeval time_diff; + FILE *fp; + timeval start_time = data_buffer[0].timestamp; + fp = fopen("log.txt","w"); + + + fprintf(fp, "idx\traw\ttimestamp\tBSY\tSEL\tC/D\tI/O\tMSG\tREQ\tACK\tATN\tRST\tData..\n"); + + while(i < data_idx) + { + timersub(&(data_buffer[i].timestamp), &start_time, &time_diff); + //timediff = difftime(data_buffer[i].timestamp, start_time); + fprintf(fp, "%d\t%08lX\t%d:%d\t",data_idx, data_buffer[i].data, time_diff.tv_sec, time_diff.tv_usec); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_BSY)); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_SEL)); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_CD)); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_IO)); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_MSG)); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_REQ)); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_ACK)); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_ATN)); + fprintf(fp, "%d\t", get_pin_value(data_buffer[i].data, PIN_RST)); + fprintf(fp, "%02X\t", get_data_field(data_buffer[i].data)); + fprintf(fp, "\n"); + i++; + } + fclose(fp); + + + i=0; + printf("Creating timing_drawer.txt\n"); + fp = fopen("timing_drawer.txt","w"); + while(i < data_idx) + { + timersub(&(data_buffer[i].timestamp), &start_time, &time_diff); + //timediff = difftime(data_buffer[i].timestamp, start_time); + fprintf(fp, "TIME=%d:%d;", time_diff.tv_sec, time_diff.tv_usec); + fprintf(fp, "BSY=%d;", get_pin_value(data_buffer[i].data, PIN_BSY)); + fprintf(fp, "SEL=%d;", get_pin_value(data_buffer[i].data, PIN_SEL)); + fprintf(fp, "CD=%d;", get_pin_value(data_buffer[i].data, PIN_CD)); + fprintf(fp, "IO=%d;", get_pin_value(data_buffer[i].data, PIN_IO)); + fprintf(fp, "MSG=%d;", get_pin_value(data_buffer[i].data, PIN_MSG)); + fprintf(fp, "REQ=%d;", get_pin_value(data_buffer[i].data, PIN_REQ)); + fprintf(fp, "ACK=%d;", get_pin_value(data_buffer[i].data, PIN_ACK)); + fprintf(fp, "ATN=%d;", get_pin_value(data_buffer[i].data, PIN_ATN)); + fprintf(fp, "RST=%d;", get_pin_value(data_buffer[i].data, PIN_RST)); + fprintf(fp, "DATA=%02X.", get_data_field(data_buffer[i].data)); + fprintf(fp, "\n"); + i++; + } + fclose(fp); + + + +// +// fp = fopen("log2.txt","w"); +// +// for(int pin=0; pin < ARRAY_SIZE(pin_names); pin++) +// { +// i=0; +// while(i < data_idx) +// { +// char this_point = ((get_pin_value(data_buffer[i].data), pin_nums[pin]) == TRUE) ? "-", "_"; +// fprintf(fp, this_point) +// } +// +// +// +// +// } + + + + +} + + + +//--------------------------------------------------------------------------- +// +// Cleanup +// +//--------------------------------------------------------------------------- +void Cleanup() +{ + int i; + + printf("In cleanup....\n"); + + + + dump_data(); + + + + // 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; + +#ifndef BAREMETAL + // Close the monitor socket + if (monsocket >= 0) { + close(monsocket); + } +#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; +} + +//--------------------------------------------------------------------------- +// +// Argument Parsing +// +//--------------------------------------------------------------------------- +BOOL ParseArgument(int argc, char* argv[]) +{ +#ifdef BAREMETAL + FRESULT fr; + FIL fp; + char line[512]; +#else + int i; +#endif // BAREMETAL + int id; + int un; + int type; + char *argID; + char *argPath; + int len; + char *ext; + +#ifdef BAREMETAL + // 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; + } +#else + // If the ID and path are not specified, the processing is interrupted + if (argc < 3) { + return TRUE; + } + i = 1; + argc--; +#endif // BAREMETAL + + // Start Decoding + + while (TRUE) { +#ifdef BAREMETAL + // 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--; + } +#else + if (argc < 2) { + break; + } + + argc -= 2; +#endif // BAREMETAL + + // Get the ID and Path +#ifdef BAREMETAL + 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; + } +#else + argID = argv[i++]; + argPath = argv[i++]; + + // Check if the argument is invalid + if (argID[0] != '-') { + FPRT(stderr, + "Error : Invalid argument(-IDn or -HDn) [%s]\n", argID); + goto parse_error; + } + argID++; +#endif // BAREMETAL + + 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; +#ifdef BAREMETAL + argPath++; +#endif // BAREMETAL + } 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; + } + } + +#ifdef BAREMETAL + // Close the configuration file + f_close(&fp); +#endif // BAREMETAL + + // Display the device list + ListDevice(stdout); + + return TRUE; + +parse_error: + +#ifdef BAREMETAL + // Close the configuration file + f_close(&fp); +#endif // BAREMETAL + + return FALSE; +} + +#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 +// +//--------------------------------------------------------------------------- +#ifdef BAREMETAL +extern "C" +int startrascsi(void) +{ + int argc = 0; + char** argv = NULL; +#else +int main(int argc, char* argv[]) +{ +#endif // BAREMETAL + DWORD prev_sample = 0xFFFFFFFF; + DWORD this_sample = 0; + //int i; + int ret; +// int actid; + //DWORD now; + //BUS::phase_t phase; +// BYTE data; +#ifndef BAREMETAL + struct sched_param schparam; +#endif // BAREMETAL + + spdlog::set_level(spdlog::level::trace); + spdlog::trace("Entering the function with {0:x}{1:X} arguments", argc,20); + // Output the Banner + Banner(argc, argv); + memset(data_buffer,0,sizeof(data_buffer)); + + // Initialize + ret = 0; + if (!Init()) { + ret = EPERM; + goto init_exit; + } + + // Reset + Reset(); + +#ifdef BAREMETAL + // BUSY assert (to hold the host side) + bus->SetBSY(TRUE); +#endif + + // Argument parsing + if (!ParseArgument(argc, argv)) { + ret = EINVAL; + goto err_exit; + } + +#ifdef BAREMETAL + // Release the busy signal + bus->SetBSY(FALSE); +#endif + +#ifndef BAREMETAL + // Set the affinity to a specific processor core + FixCpu(3); + +#ifdef USE_SEL_EVENT_ENABLE + // 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 +#endif // BAREMETAL + + // Start execution + running = TRUE; + bus->SetAct(FALSE); + + + + spdlog::trace("Going into running mode {}", 1); + // Main Loop + while (running) { + // Work initialization + this_sample = bus->Aquire(); + + if(this_sample != prev_sample) + { + //printf("%d Sample %08lX\n", data_idx, this_sample); + data_buffer[data_idx].data = this_sample; + (void)gettimeofday(&(data_buffer[data_idx].timestamp), NULL); + data_idx = (data_idx + 1) % MAX_BUFF_SIZE; + prev_sample = this_sample; + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + continue; +//////// +//////// // Target sending data +//////// if(!bus->GetIO() && bus->GetREQ() && bus->GetACK()) +//////// { +//////// BYTE data = bus->GetDAT(); +//////// printf("+%02X ",data); +//////// +//////// +//////// +//////// DWORD now = SysTimer::GetTimerLow(); +//////// while ((SysTimer::GetTimerLow() - now) < SECONDS_1/100) +//////// { +//////// bus->Aquire(); +//////// if (bus->GetACK() == FALSE) { +//////// break; +//////// } +//////// } +//////// +//////// if(bus->GetACK() != FALSE) +//////// { +//////// spdlog::warn("got an invalid req/ack sequence for target sending data"); +//////// } +//////// } +//////// +//////// +//////// +//////// if(bus->GetIO() && bus->GetREQ() && !bus->GetACK()) +//////// { +//////// BYTE data = bus->GetDAT(); +//////// printf("-%02X ",data); +//////// +//////// +//////// DWORD now = SysTimer::GetTimerLow(); +//////// while ((SysTimer::GetTimerLow() - now) < SECONDS_1/100) +//////// { +//////// bus->Aquire(); +//////// if (bus->GetREQ() == FALSE) { +//////// break; +//////// } +//////// } +//////// +//////// if(bus->GetREQ() != TRUE) +//////// { +//////// spdlog::warn("REQ didn't de-assert when I wanted it to."); +//////// } +//////// +//////// +//////// } +//////// +//////// continue; +//////// +//////// // 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->GetSEL() ) { +//////// BYTE data = bus->GetDAT(); +//////// printf("SEL is asserted. Data: %02X, BSY: %d, SEL %d\n", data, bus->GetBSY(), bus->GetSEL()); +//////// now = SysTimer::GetTimerLow(); +//////// while ((SysTimer::GetTimerLow() - now) < 3 * 1000 * 1000) { +//////// bus->Aquire(); +//////// if (!bus->GetSEL()) { +//////// printf("SEL is clear. Data: %02X, BSY: %d, SEL %d\n", data, bus->GetBSY(), bus->GetSEL()); +//////// break; +//////// } +//////// } +//////// } +//////// else{ +//////// +//////// continue; +//////// } +////////// spdlog::trace("Busy: {}",bus->GetBSY()); +//////// +//////// +//////// // For monitor mode, we just want to make sure the initiator +//////// // released the BSY signal within 3 seconds. If it hasn't +//////// // the initiator is misbehaving +////////// if (bus->GetBSY()) { +////////// spdlog::warn("The initiator (%d) did not release the BSY signal after 3 seconds", bus->GetDAT()); +////////// continue; +////////// } +//////// +////////////////////// +////////////////////// // Stop because it the bus is busy or another device responded +////////////////////// if (bus->GetBSY() || !bus->GetSEL()) { +////////////////////// continue; +////////////////////// } +// +// // Notify all controllers +// data = bus->GetDAT(); +//// spdlog::trace("Data is {x}",data); +// for (i = 0; i < CtrlMax; i++) { +// if (!ctrl[i] || (data & (1 << i)) == 0) { +// continue; +// } +//// spdlog::trace("Found an active controller! Let's do some selection {}", i); +// // 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; +// spdlog::trace("Found a target device {} ID:{}",actid, ctrl[actid]->GetID()); +// +//#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; + } + +err_exit: + // Cleanup + Cleanup(); + +init_exit: +#if !defined(BAREMETAL) + exit(ret); +#else + return ret; +#endif // BAREMETAL +}