RASCSI/src/raspberrypi/rascsi.cpp
2020-07-18 13:34:29 -05:00

1460 lines
32 KiB
C++

//---------------------------------------------------------------------------
//
// 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 <sys/timespec_util.h>
#include <sys/time.h>
//---------------------------------------------------------------------------
//
// 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
}