rasdump and monitor updates, made rasdump work, improved bus abstraction (#973)

* Moved rasdump and monitor to sub-folders, cleaned up code

* Fixes rasdump issues and added additional features like device type check, LUN support and configurable buffer size
This commit is contained in:
Uwe Seimet 2022-11-09 08:40:26 +01:00 committed by GitHub
parent e8b72c48de
commit 1c0179e7e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 2348 additions and 4280 deletions

View File

@ -56,5 +56,5 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
cd $SOURCES | sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.projectKey=akuker_RASCSI --define sonar.organization=rascsi --define sonar.cfamily.gcov.reportsPath=. --define sonar.cfamily.cache.enabled=false --define sonar.coverage.exclusions="**/test/**" --define sonar.cpd.exclusions="**test/**" --define sonar.python.version=3
cd $SOURCES | sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.projectKey=akuker_RASCSI --define sonar.organization=rascsi --define sonar.cfamily.gcov.reportsPath=. --define sonar.coverage.exclusions="**/test/**" --define sonar.cpd.exclusions="**test/**" --define sonar.python.version=3

View File

@ -115,7 +115,8 @@ SRC_RASDUMP += $(shell find ./rasdump -name '*.cpp')
SRC_RASDUMP += $(shell find ./hal -name '*.cpp')
SRC_RASCSI_TEST = $(shell find ./test -name '*.cpp')
SRC_RASCSI_TEST += $(shell find ./rasdump -name '*.cpp')
SRC_RASCSI_TEST += $(shell find ./monitor -name '*.cpp')
vpath %.h ./ ./controllers ./devices ./monitor ./hal ./rascsi ./rasctl ./rasdump
vpath %.cpp ./ ./controllers ./devices ./monitor ./hal ./rascsi ./rasctl ./rasdump ./test
@ -177,7 +178,7 @@ lcov: test
lcov -q -c -d . --include '*/cpp/*' -o $(COVERAGE_FILE) --exclude '*/test/*' --exclude '*/interfaces/*' --exclude '*/rascsi_interface.pb*'
genhtml -q -o $(COVERAGE_DIR) --legend $(COVERAGE_FILE)
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt $(DOC_DIR)/rasdump_man_page.txt
$(SRC_SHARED) $(SRC_RASCSI_CORE) $(SRC_RASCTL_CORE): $(OBJ_PROTOBUF)

View File

@ -211,8 +211,8 @@ void ScsiController::Command()
// If not able to receive all, move to the status phase
if (actual_count != command_byte_count) {
LOGERROR("%s Command byte count mismatch: expected %d bytes, received %d byte(s)", __PRETTY_FUNCTION__,
command_byte_count, actual_count)
LOGERROR("Command byte count mismatch for command $%02X: expected %d bytes, received %d byte(s)",
GetBuffer()[0], command_byte_count, actual_count)
Error(sense_key::ABORTED_COMMAND);
return;
}
@ -584,12 +584,12 @@ void ScsiController::Receive()
// Length != 0 if received
if (HasValidLength()) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, GetLength())
LOGTRACE("%s Length is %d byte(s)", __PRETTY_FUNCTION__, GetLength())
// If not able to receive all, move to status phase
if (int len = GetBus().ReceiveHandShake(GetBuffer().data() + GetOffset(), GetLength());
len != static_cast<int>(GetLength())) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d",__PRETTY_FUNCTION__, GetLength(), len)
LOGERROR("%s Not able to receive %d byte(s) of data, only received %d",__PRETTY_FUNCTION__, GetLength(), len)
Error(sense_key::ABORTED_COMMAND);
return;
}
@ -692,7 +692,7 @@ void ScsiController::ReceiveBytes()
// If not able to receive all, move to status phase
if (uint32_t len = GetBus().ReceiveHandShake(GetBuffer().data() + GetOffset(), GetLength()); len != GetLength()) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d",
LOGERROR("%s Not able to receive %d byte(s) of data, only received %d",
__PRETTY_FUNCTION__, GetLength(), len)
Error(sense_key::ABORTED_COMMAND);
return;

View File

@ -156,7 +156,7 @@ void SCSICD::CreateDataTrack()
auto track = make_unique<CDTrack>();
track->Init(1, 0, static_cast<int>(GetBlockCount()) - 1);
track->SetPath(false, GetFilename());
tracks.push_back(move(track));
tracks.push_back(std::move(track));
dataindex = 0;
}

View File

@ -9,6 +9,7 @@
#pragma once
#include "config.h"
#include "scsi.h"
#include <cstdint>
#include <array>
@ -102,6 +103,14 @@ public:
virtual int ReceiveHandShake(uint8_t *buf, int count) = 0;
virtual int SendHandShake(uint8_t *buf, int count, int delay_after_bytes) = 0;
#ifdef USE_SEL_EVENT_ENABLE
// SEL signal event polling
virtual bool PollSelectEvent() = 0;
// Clear SEL signal event
virtual void ClearSelectEvent() = 0;
#endif
virtual bool GetSignal(int pin) const = 0;
// Get SCSI input signal value
virtual void SetSignal(int pin, bool ast) = 0;

View File

@ -10,75 +10,287 @@
//
//---------------------------------------------------------------------------
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "hal/gpiobus.h"
#include "config.h"
#include "hal/sbc_version.h"
#include "hal/systimer.h"
#include "config.h"
#include "log.h"
#include <array>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
#ifdef __linux__
#include <sys/epoll.h>
#endif
#if defined CONNECT_TYPE_STANDARD
#include "hal/gpiobus_standard.h"
#elif defined CONNECT_TYPE_FULLSPEC
#include "hal/gpiobus_fullspec.h"
#elif defined CONNECT_TYPE_AIBOM
#include "hal/gpiobus_aibom.h"
#elif defined CONNECT_TYPE_GAMERNIUM
#include "hal/gpiobus_gamernium.h"
#else
#error Invalid connection type or none specified
#endif
//---------------------------------------------------------------------------
//
// Constant declarations (bus control timing)
//
//---------------------------------------------------------------------------
// SCSI Bus timings taken from:
// https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-05.html
[[maybe_unused]] const static int SCSI_DELAY_ARBITRATION_DELAY_NS = 2400;
[[maybe_unused]] const static int SCSI_DELAY_ASSERTION_PERIOD_NS = 90;
[[maybe_unused]] const static int SCSI_DELAY_BUS_CLEAR_DELAY_NS = 800;
[[maybe_unused]] const static int SCSI_DELAY_BUS_FREE_DELAY_NS = 800;
[[maybe_unused]] const static int SCSI_DELAY_BUS_SET_DELAY_NS = 1800;
[[maybe_unused]] const static int SCSI_DELAY_BUS_SETTLE_DELAY_NS = 400;
[[maybe_unused]] const static int SCSI_DELAY_CABLE_SKEW_DELAY_NS = 10;
[[maybe_unused]] const static int SCSI_DELAY_DATA_RELEASE_DELAY_NS = 400;
[[maybe_unused]] const static int SCSI_DELAY_DESKEW_DELAY_NS = 45;
[[maybe_unused]] const static int SCSI_DELAY_DISCONNECTION_DELAY_US = 200;
[[maybe_unused]] const static int SCSI_DELAY_HOLD_TIME_NS = 45;
[[maybe_unused]] const static int SCSI_DELAY_NEGATION_PERIOD_NS = 90;
[[maybe_unused]] const static int SCSI_DELAY_POWER_ON_TO_SELECTION_TIME_S = 10; // (recommended)
[[maybe_unused]] const static int SCSI_DELAY_RESET_TO_SELECTION_TIME_US = 250 * 1000; // (recommended)
[[maybe_unused]] const static int SCSI_DELAY_RESET_HOLD_TIME_US = 25;
[[maybe_unused]] const static int SCSI_DELAY_SELECTION_ABORT_TIME_US = 200;
[[maybe_unused]] const static int SCSI_DELAY_SELECTION_TIMEOUT_DELAY_NS = 250 * 1000; // (recommended)
[[maybe_unused]] const static int SCSI_DELAY_FAST_ASSERTION_PERIOD_NS = 30;
[[maybe_unused]] const static int SCSI_DELAY_FAST_CABLE_SKEW_DELAY_NS = 5;
[[maybe_unused]] const static int SCSI_DELAY_FAST_DESKEW_DELAY_NS = 20;
[[maybe_unused]] const static int SCSI_DELAY_FAST_HOLD_TIME_NS = 10;
[[maybe_unused]] const static int SCSI_DELAY_FAST_NEGATION_PERIOD_NS = 30;
// The DaynaPort SCSI Link do a short delay in the middle of transfering
// a packet. This is the number of uS that will be delayed between the
// header and the actual data.
const static int SCSI_DELAY_SEND_DATA_DAYNAPORT_US = 100;
using namespace std;
// Nothing SBC hardware specific should be done in this function
#ifdef __linux__
//---------------------------------------------------------------------------
//
// imported from bcm_host.c
//
//---------------------------------------------------------------------------
static uint32_t get_dt_ranges(const char *filename, uint32_t offset)
{
uint32_t address = ~0;
if (FILE *fp = fopen(filename, "rb"); fp) {
fseek(fp, offset, SEEK_SET);
if (array<uint8_t, 4> buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) {
address = (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0;
}
fclose(fp);
}
return address;
}
uint32_t bcm_host_get_peripheral_address()
{
uint32_t address = get_dt_ranges("/proc/device-tree/soc/ranges", 4);
if (address == 0) {
address = get_dt_ranges("/proc/device-tree/soc/ranges", 8);
}
address = (address == (uint32_t)~0) ? 0x20000000 : address;
return address;
}
#endif
#ifdef __NetBSD__
// Assume the Raspberry Pi series and estimate the address from CPU
uint32_t bcm_host_get_peripheral_address()
{
array<char, 1024> buf;
size_t len = buf.size();
uint32_t address;
if (sysctlbyname("hw.model", buf.data(), &len, NULL, 0) ||
strstr(buf, "ARM1176JZ-S") != buf.data()) {
// Failed to get CPU model || Not BCM2835
// use the address of BCM283[67]
address = 0x3f000000;
} else {
// Use BCM2835 address
address = 0x20000000;
}
printf("Peripheral address : 0x%lx\n", address);
return address;
}
#endif
bool GPIOBUS::Init(mode_e mode)
{
GPIO_FUNCTION_TRACE
// Save operation mode
actmode = mode;
#if defined(__x86_64__) || defined(__X86__)
return true;
#else
int i;
#ifdef USE_SEL_EVENT_ENABLE
epoll_event ev = {};
#endif
// Get the base address
baseaddr = (uint32_t)bcm_host_get_peripheral_address();
// Open /dev/mem
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd == -1) {
LOGERROR("Error: Unable to open /dev/mem. Are you running as root?")
return false;
}
// Map peripheral region memory
void *map = mmap(NULL, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr);
if (map == MAP_FAILED) {
LOGERROR("Error: Unable to map memory")
close(fd);
return false;
}
// Determine the type of raspberry pi from the base address
if (baseaddr == 0xfe000000) {
rpitype = 4;
} else if (baseaddr == 0x3f000000) {
rpitype = 2;
} else {
rpitype = 1;
}
// GPIO
gpio = (uint32_t *)map;
gpio += GPIO_OFFSET / sizeof(uint32_t);
level = &gpio[GPIO_LEV_0];
// PADS
pads = (uint32_t *)map;
pads += PADS_OFFSET / sizeof(uint32_t);
// System timer
SysTimer::Init(
(uint32_t *)map + SYST_OFFSET / sizeof(uint32_t),
(uint32_t *)map + ARMT_OFFSET / sizeof(uint32_t));
// Interrupt controller
irpctl = (uint32_t *)map;
irpctl += IRPT_OFFSET / sizeof(uint32_t);
// Quad-A7 control
qa7regs = (uint32_t *)map;
qa7regs += QA7_OFFSET / sizeof(uint32_t);
// Map GIC memory
if (rpitype == 4) {
map = mmap(NULL, 8192,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, ARM_GICD_BASE);
if (map == MAP_FAILED) {
close(fd);
return false;
}
gicd = (uint32_t *)map;
gicc = (uint32_t *)map;
gicc += (ARM_GICC_BASE - ARM_GICD_BASE) / sizeof(uint32_t);
} else {
gicd = NULL;
gicc = NULL;
}
close(fd);
// Set Drive Strength to 16mA
DrvConfig(7);
// Set pull up/pull down
#if SIGNAL_CONTROL_MODE == 0
int pullmode = GPIO_PULLNONE;
#elif SIGNAL_CONTROL_MODE == 1
int pullmode = GPIO_PULLUP;
#else
int pullmode = GPIO_PULLDOWN;
#endif
// Initialize all signals
for (i = 0; SignalTable[i] >= 0; i++) {
int j = SignalTable[i];
PinSetSignal(j, OFF);
PinConfig(j, GPIO_INPUT);
PullConfig(j, pullmode);
}
// Set control signals
PinSetSignal(PIN_ACT, OFF);
PinSetSignal(PIN_TAD, OFF);
PinSetSignal(PIN_IND, OFF);
PinSetSignal(PIN_DTD, OFF);
PinConfig(PIN_ACT, GPIO_OUTPUT);
PinConfig(PIN_TAD, GPIO_OUTPUT);
PinConfig(PIN_IND, GPIO_OUTPUT);
PinConfig(PIN_DTD, GPIO_OUTPUT);
// Set the ENABLE signal
// This is used to show that the application is running
PinSetSignal(PIN_ENB, ENB_OFF);
PinConfig(PIN_ENB, GPIO_OUTPUT);
// GPFSEL backup
gpfsel[0] = gpio[GPIO_FSEL_0];
gpfsel[1] = gpio[GPIO_FSEL_1];
gpfsel[2] = gpio[GPIO_FSEL_2];
gpfsel[3] = gpio[GPIO_FSEL_3];
// Initialize SEL signal interrupt
#ifdef USE_SEL_EVENT_ENABLE
// GPIO chip open
fd = open("/dev/gpiochip0", 0);
if (fd == -1) {
LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?")
return false;
}
// Event request setting
strcpy(selevreq.consumer_label, "RaSCSI");
selevreq.lineoffset = PIN_SEL;
selevreq.handleflags = GPIOHANDLE_REQUEST_INPUT;
#if SIGNAL_CONTROL_MODE < 2
selevreq.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE;
#else
selevreq.eventflags = GPIOEVENT_REQUEST_RISING_EDGE;
#endif // SIGNAL_CONTROL_MODE
//Get event request
if (ioctl(fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) {
LOGERROR("Unable to register event request. Is RaSCSI already running?")
close(fd);
return false;
}
// Close GPIO chip file handle
close(fd);
// epoll initialization
epfd = epoll_create(1);
ev.events = EPOLLIN | EPOLLPRI;
ev.data.fd = selevreq.fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, selevreq.fd, &ev);
#else
// Edge detection setting
#if SIGNAL_CONTROL_MODE == 2
gpio[GPIO_AREN_0] = 1 << PIN_SEL;
#else
gpio[GPIO_AFEN_0] = 1 << PIN_SEL;
#endif // SIGNAL_CONTROL_MODE
// Clear event
gpio[GPIO_EDS_0] = 1 << PIN_SEL;
// Register interrupt handler
setIrqFuncAddress(IrqHandler);
// GPIO interrupt setting
if (rpitype == 4) {
// GIC Invalid
gicd[GICD_CTLR] = 0;
// Route all interupts to core 0
for (i = 0; i < 8; i++) {
gicd[GICD_ICENABLER0 + i] = 0xffffffff;
gicd[GICD_ICPENDR0 + i] = 0xffffffff;
gicd[GICD_ICACTIVER0 + i] = 0xffffffff;
}
for (i = 0; i < 64; i++) {
gicd[GICD_IPRIORITYR0 + i] = 0xa0a0a0a0;
gicd[GICD_ITARGETSR0 + i] = 0x01010101;
}
// Set all interrupts as level triggers
for (i = 0; i < 16; i++) {
gicd[GICD_ICFGR0 + i] = 0;
}
// GIC Invalid
gicd[GICD_CTLR] = 1;
// Enable CPU interface for core 0
gicc[GICC_PMR] = 0xf0;
gicc[GICC_CTLR] = 1;
// Enable interrupts
gicd[GICD_ISENABLER0 + (GIC_GPIO_IRQ / 32)] =
1 << (GIC_GPIO_IRQ % 32);
} else {
// Enable interrupts
irpctl[IRPT_ENB_IRQ_2] = (1 << (GPIO_IRQ % 32));
}
#endif // USE_SEL_EVENT_ENABLE
// Create work table
MakeTable();
// Finally, enable ENABLE
// Show the user that this app is running
SetControl(PIN_ENB, ENB_ON);
return true;
#endif // ifdef __x86_64__ || __X86__
}
void GPIOBUS::Cleanup()
@ -117,7 +329,6 @@ void GPIOBUS::Cleanup()
void GPIOBUS::Reset()
{
GPIO_FUNCTION_TRACE
#if defined(__x86_64__) || defined(__X86__)
return;
#else
@ -204,19 +415,16 @@ void GPIOBUS::Reset()
void GPIOBUS::SetENB(bool ast)
{
GPIO_FUNCTION_TRACE
PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF);
}
bool GPIOBUS::GetBSY() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_BSY);
}
void GPIOBUS::SetBSY(bool ast)
{
GPIO_FUNCTION_TRACE
// Set BSY signal
SetSignal(PIN_BSY, ast);
@ -251,13 +459,11 @@ void GPIOBUS::SetBSY(bool ast)
bool GPIOBUS::GetSEL() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_SEL);
}
void GPIOBUS::SetSEL(bool ast)
{
GPIO_FUNCTION_TRACE
if (actmode == mode_e::INITIATOR && ast) {
// Turn on ACTIVE signal
SetControl(PIN_ACT, ACT_ON);
@ -269,79 +475,66 @@ void GPIOBUS::SetSEL(bool ast)
bool GPIOBUS::GetATN() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_ATN);
}
void GPIOBUS::SetATN(bool ast)
{
GPIO_FUNCTION_TRACE
SetSignal(PIN_ATN, ast);
}
bool GPIOBUS::GetACK() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_ACK);
}
void GPIOBUS::SetACK(bool ast)
{
GPIO_FUNCTION_TRACE
SetSignal(PIN_ACK, ast);
}
bool GPIOBUS::GetACT() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_ACT);
}
void GPIOBUS::SetACT(bool ast)
{
GPIO_FUNCTION_TRACE
SetSignal(PIN_ACT, ast);
}
bool GPIOBUS::GetRST() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_RST);
}
void GPIOBUS::SetRST(bool ast)
{
GPIO_FUNCTION_TRACE
SetSignal(PIN_RST, ast);
}
bool GPIOBUS::GetMSG() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_MSG);
}
void GPIOBUS::SetMSG(bool ast)
{
GPIO_FUNCTION_TRACE
SetSignal(PIN_MSG, ast);
}
bool GPIOBUS::GetCD() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_CD);
}
void GPIOBUS::SetCD(bool ast)
{
GPIO_FUNCTION_TRACE
SetSignal(PIN_CD, ast);
}
bool GPIOBUS::GetIO()
{
GPIO_FUNCTION_TRACE
bool ast = GetSignal(PIN_IO);
if (actmode == mode_e::INITIATOR) {
@ -376,7 +569,6 @@ bool GPIOBUS::GetIO()
void GPIOBUS::SetIO(bool ast)
{
GPIO_FUNCTION_TRACE
SetSignal(PIN_IO, ast);
if (actmode == mode_e::TARGET) {
@ -410,19 +602,75 @@ void GPIOBUS::SetIO(bool ast)
bool GPIOBUS::GetREQ() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_REQ);
}
void GPIOBUS::SetREQ(bool ast)
{
GPIO_FUNCTION_TRACE
SetSignal(PIN_REQ, ast);
}
//---------------------------------------------------------------------------
//
// Get data signals
//
//---------------------------------------------------------------------------
uint8_t GPIOBUS::GetDAT()
{
uint32_t data = Acquire();
data =
((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 (uint8_t)data;
}
//---------------------------------------------------------------------------
//
// Set data signals
//
//---------------------------------------------------------------------------
void GPIOBUS::SetDAT(uint8_t dat)
{
// Write to port
#if SIGNAL_CONTROL_MODE == 0
uint32_t fsel = gpfsel[0];
fsel &= tblDatMsk[0][dat];
fsel |= tblDatSet[0][dat];
if (fsel != gpfsel[0]) {
gpfsel[0] = fsel;
gpio[GPIO_FSEL_0] = fsel;
}
fsel = gpfsel[1];
fsel &= tblDatMsk[1][dat];
fsel |= tblDatSet[1][dat];
if (fsel != gpfsel[1]) {
gpfsel[1] = fsel;
gpio[GPIO_FSEL_1] = fsel;
}
fsel = gpfsel[2];
fsel &= tblDatMsk[2][dat];
fsel |= tblDatSet[2][dat];
if (fsel != gpfsel[2]) {
gpfsel[2] = fsel;
gpio[GPIO_FSEL_2] = fsel;
}
#else
gpio[GPIO_CLR_0] = tblDatMsk[dat];
gpio[GPIO_SET_0] = tblDatSet[dat];
#endif // SIGNAL_CONTROL_MODE
}
bool GPIOBUS::GetDP() const
{
GPIO_FUNCTION_TRACE
return GetSignal(PIN_DP);
}
@ -433,7 +681,6 @@ bool GPIOBUS::GetDP() const
//---------------------------------------------------------------------------
int GPIOBUS::CommandHandShake(vector<uint8_t>& buf)
{
GPIO_FUNCTION_TRACE
// Only works in TARGET mode
if (actmode != mode_e::TARGET) {
return 0;
@ -559,8 +806,6 @@ int GPIOBUS::CommandHandShake(vector<uint8_t>& buf)
//---------------------------------------------------------------------------
int GPIOBUS::ReceiveHandShake(uint8_t *buf, int count)
{
GPIO_FUNCTION_TRACE
int i;
// Disable IRQs
@ -661,8 +906,6 @@ int GPIOBUS::ReceiveHandShake(uint8_t *buf, int count)
//---------------------------------------------------------------------------
int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes)
{
GPIO_FUNCTION_TRACE
int i;
// Disable IRQs
@ -671,8 +914,7 @@ int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes)
if (actmode == mode_e::TARGET) {
for (i = 0; i < count; i++) {
if(i==delay_after_bytes){
LOGTRACE("%s DELAYING for %dus after %d bytes", __PRETTY_FUNCTION__, SCSI_DELAY_SEND_DATA_DAYNAPORT_US,
(int)delay_after_bytes)
LOGTRACE("%s DELAYING for %dus after %d bytes", __PRETTY_FUNCTION__, SCSI_DELAY_SEND_DATA_DAYNAPORT_US, (int)delay_after_bytes)
SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US);
}
@ -715,8 +957,7 @@ int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes)
for (i = 0; i < count; i++) {
if(i==delay_after_bytes){
LOGTRACE("%s DELAYING for %dus after %d bytes", __PRETTY_FUNCTION__, SCSI_DELAY_SEND_DATA_DAYNAPORT_US,
(int)delay_after_bytes)
LOGTRACE("%s DELAYING for %dus after %d bytes", __PRETTY_FUNCTION__, SCSI_DELAY_SEND_DATA_DAYNAPORT_US, (int)delay_after_bytes)
SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US);
}
@ -777,18 +1018,15 @@ int GPIOBUS::SendHandShake(uint8_t *buf, int count, int delay_after_bytes)
//---------------------------------------------------------------------------
bool GPIOBUS::PollSelectEvent()
{
GPIO_FUNCTION_TRACE
errno = 0;
if (epoll_event epev; epoll_wait(epfd, &epev, 1, -1) <= 0) {
LOGWARN("%s epoll_wait failed", __PRETTY_FUNCTION__)
LOGWARN("[%08X] %s", errno, strerror(errno))
return false;
}
if (gpioevent_data gpev; read(selevreq.fd, &gpev, sizeof(gpev)) < 0) {
LOGWARN("%s read failed", __PRETTY_FUNCTION__)
LOGWARN("[%08X] %s", errno, strerror(errno))
return false;
}
@ -802,7 +1040,6 @@ bool GPIOBUS::PollSelectEvent()
//---------------------------------------------------------------------------
void GPIOBUS::ClearSelectEvent()
{
GPIO_FUNCTION_TRACE
}
#endif // USE_SEL_EVENT_ENABLE
@ -811,9 +1048,13 @@ void GPIOBUS::ClearSelectEvent()
// Signal table
//
//---------------------------------------------------------------------------
const array<int, 19> GPIOBUS::SignalTable = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6,
PIN_DT7, PIN_DP, PIN_SEL, PIN_ATN, PIN_RST, PIN_ACK, PIN_BSY,
PIN_MSG, PIN_CD, PIN_IO, PIN_REQ, -1};
const array<int, 19> GPIOBUS::SignalTable = {
PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3,
PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP,
PIN_SEL,PIN_ATN, PIN_RST, PIN_ACK,
PIN_BSY, PIN_MSG, PIN_CD, PIN_IO, PIN_REQ,
-1
};
//---------------------------------------------------------------------------
//
@ -822,9 +1063,10 @@ const array<int, 19> GPIOBUS::SignalTable = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3,
//---------------------------------------------------------------------------
void GPIOBUS::MakeTable(void)
{
GPIO_FUNCTION_TRACE
const array<int, 9> pintbl = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP};
const array<int, 9> pintbl = {
PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4,
PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP
};
array<bool, 256> tblParity;
@ -908,6 +1150,83 @@ void GPIOBUS::MakeTable(void)
#endif
}
//---------------------------------------------------------------------------
//
// Control signal setting
//
//---------------------------------------------------------------------------
void GPIOBUS::SetControl(int pin, bool ast)
{
PinSetSignal(pin, ast);
}
//---------------------------------------------------------------------------
//
// Input/output mode setting
//
//---------------------------------------------------------------------------
void GPIOBUS::SetMode(int pin, int mode)
{
#if SIGNAL_CONTROL_MODE == 0
if (mode == OUT) {
return;
}
#endif // SIGNAL_CONTROL_MODE
int index = pin / 10;
int shift = (pin % 10) * 3;
uint32_t data = gpfsel[index];
data &= ~(0x7 << shift);
if (mode == OUT) {
data |= (1 << shift);
}
gpio[index] = data;
gpfsel[index] = data;
}
//---------------------------------------------------------------------------
//
// Get input signal value
//
//---------------------------------------------------------------------------
bool GPIOBUS::GetSignal(int pin) const
{
return (signals >> pin) & 1;
}
//---------------------------------------------------------------------------
//
// Set output signal value
//
//---------------------------------------------------------------------------
void GPIOBUS::SetSignal(int pin, bool ast)
{
#if SIGNAL_CONTROL_MODE == 0
int index = pin / 10;
int shift = (pin % 10) * 3;
uint32_t data = gpfsel[index];
if (ast) {
data |= (1 << shift);
} else {
data &= ~(0x7 << shift);
}
gpio[index] = data;
gpfsel[index] = data;
#elif SIGNAL_CONTROL_MODE == 1
if (ast) {
gpio[GPIO_CLR_0] = 0x1 << pin;
} else {
gpio[GPIO_SET_0] = 0x1 << pin;
}
#elif SIGNAL_CONTROL_MODE == 2
if (ast) {
gpio[GPIO_SET_0] = 0x1 << pin;
} else {
gpio[GPIO_CLR_0] = 0x1 << pin;
}
#endif // SIGNAL_CONTROL_MODE
}
//---------------------------------------------------------------------------
//
// Wait for signal change
@ -915,8 +1234,6 @@ void GPIOBUS::MakeTable(void)
//---------------------------------------------------------------------------
bool GPIOBUS::WaitSignal(int pin, int ast)
{
GPIO_FUNCTION_TRACE
// Get current time
uint32_t now = SysTimer::GetTimerLow();
@ -941,6 +1258,136 @@ bool GPIOBUS::WaitSignal(int pin, int ast)
return false;
}
void GPIOBUS::DisableIRQ()
{
#ifdef __linux__
if (rpitype == 4) {
// RPI4 is disabled by GICC
giccpmr = gicc[GICC_PMR];
gicc[GICC_PMR] = 0;
} else if (rpitype == 2) {
// RPI2,3 disable core timer IRQ
tintcore = sched_getcpu() + QA7_CORE0_TINTC;
tintctl = qa7regs[tintcore];
qa7regs[tintcore] = 0;
} else {
// Stop system timer interrupt with interrupt controller
irptenb = irpctl[IRPT_ENB_IRQ_1];
irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf;
}
#else
(void)0;
#endif
}
void GPIOBUS::EnableIRQ()
{
if (rpitype == 4) {
// RPI4 enables interrupts via the GICC
gicc[GICC_PMR] = giccpmr;
} else if (rpitype == 2) {
// RPI2,3 re-enable core timer IRQ
qa7regs[tintcore] = tintctl;
} else {
// Restart the system timer interrupt with the interrupt controller
irpctl[IRPT_ENB_IRQ_1] = irptenb & 0xf;
}
}
//---------------------------------------------------------------------------
//
// Pin direction setting (input/output)
//
//---------------------------------------------------------------------------
void GPIOBUS::PinConfig(int pin, int mode)
{
// Check for invalid pin
if (pin < 0) {
return;
}
int index = pin / 10;
uint32_t mask = ~(0x7 << ((pin % 10) * 3));
gpio[index] = (gpio[index] & mask) | ((mode & 0x7) << ((pin % 10) * 3));
}
//---------------------------------------------------------------------------
//
// Pin pull-up/pull-down setting
//
//---------------------------------------------------------------------------
void GPIOBUS::PullConfig(int pin, int mode)
{
uint32_t pull;
// Check for invalid pin
if (pin < 0) {
return;
}
if (rpitype == 4) {
switch (mode) {
case GPIO_PULLNONE:
pull = 0;
break;
case GPIO_PULLUP:
pull = 1;
break;
case GPIO_PULLDOWN:
pull = 2;
break;
default:
return;
}
pin &= 0x1f;
int shift = (pin & 0xf) << 1;
uint32_t bits = gpio[GPIO_PUPPDN0 + (pin >> 4)];
bits &= ~(3 << shift);
bits |= (pull << shift);
gpio[GPIO_PUPPDN0 + (pin >> 4)] = bits;
} else {
pin &= 0x1f;
gpio[GPIO_PUD] = mode & 0x3;
SysTimer::SleepUsec(2);
gpio[GPIO_CLK_0] = 0x1 << pin;
SysTimer::SleepUsec(2);
gpio[GPIO_PUD] = 0;
gpio[GPIO_CLK_0] = 0;
}
}
//---------------------------------------------------------------------------
//
// Set output pin
//
//---------------------------------------------------------------------------
void GPIOBUS::PinSetSignal(int pin, bool ast)
{
// Check for invalid pin
if (pin < 0) {
return;
}
if (ast) {
gpio[GPIO_SET_0] = 0x1 << pin;
} else {
gpio[GPIO_CLR_0] = 0x1 << pin;
}
}
//---------------------------------------------------------------------------
//
// Set the signal drive strength
//
//---------------------------------------------------------------------------
void GPIOBUS::DrvConfig(uint32_t drive)
{
uint32_t data = pads[PAD_0_27];
pads[PAD_0_27] = (0xFFFFFFF8 & data) | drive | 0x5a000000;
}
//---------------------------------------------------------------------------
//
// Generic Phase Acquisition (Doesn't read GPIO)
@ -948,7 +1395,6 @@ bool GPIOBUS::WaitSignal(int pin, int ast)
//---------------------------------------------------------------------------
BUS::phase_t GPIOBUS::GetPhaseRaw(uint32_t raw_data)
{
GPIO_FUNCTION_TRACE
// Selection Phase
if (GetPinRaw(raw_data, PIN_SEL)) {
if(GetPinRaw(raw_data, PIN_IO)) {

View File

@ -15,7 +15,6 @@
#include "scsi.h"
#include "bus.h"
#include <array>
#include <vector>
#ifdef __linux__
#include <linux/gpio.h>
@ -43,13 +42,7 @@
#error Invalid connection type or none specified
#endif
#ifdef ENABLE_GPIO_TRACE
#define GPIO_FUNCTION_TRACE LOGTRACE("%s", __PRETTY_FUNCTION__)
#else
#define GPIO_FUNCTION_TRACE
#endif
using namespace std;
using namespace std; //NOSONAR Not relevant for rascsi
//---------------------------------------------------------------------------
//
@ -126,9 +119,24 @@ using namespace std;
//---------------------------------------------------------------------------
#define ALL_SCSI_PINS \
((1 << PIN_DT0) | (1 << PIN_DT1) | (1 << PIN_DT2) | (1 << PIN_DT3) | (1 << PIN_DT4) | (1 << PIN_DT5) | \
(1 << PIN_DT6) | (1 << PIN_DT7) | (1 << PIN_DP) | (1 << PIN_ATN) | (1 << PIN_RST) | (1 << PIN_ACK) | \
(1 << PIN_REQ) | (1 << PIN_MSG) | (1 << PIN_CD) | (1 << PIN_IO) | (1 << PIN_BSY) | (1 << PIN_SEL))
((1<<PIN_DT0)|\
(1<<PIN_DT1)|\
(1<<PIN_DT2)|\
(1<<PIN_DT3)|\
(1<<PIN_DT4)|\
(1<<PIN_DT5)|\
(1<<PIN_DT6)|\
(1<<PIN_DT7)|\
(1<<PIN_DP)|\
(1<<PIN_ATN)|\
(1<<PIN_RST)|\
(1<<PIN_ACK)|\
(1<<PIN_REQ)|\
(1<<PIN_MSG)|\
(1<<PIN_CD)|\
(1<<PIN_IO)|\
(1<<PIN_BSY)|\
(1<<PIN_SEL))
//---------------------------------------------------------------------------
//
@ -198,9 +206,15 @@ const static int IRPT_DIS_IRQ_B = 9;
const static int QA7_CORE0_TINTC = 16;
const static int GPIO_IRQ = (32 + 20); // GPIO3
#define GPIO_INEDGE ((1 << PIN_BSY) | (1 << PIN_SEL) | (1 << PIN_ATN) | (1 << PIN_ACK) | (1 << PIN_RST))
#define GPIO_INEDGE ((1 << PIN_BSY) | \
(1 << PIN_SEL) | \
(1 << PIN_ATN) | \
(1 << PIN_ACK) | \
(1 << PIN_RST))
#define GPIO_MCI ((1 << PIN_MSG) | (1 << PIN_CD) | (1 << PIN_IO))
#define GPIO_MCI ((1 << PIN_MSG) | \
(1 << PIN_CD) | \
(1 << PIN_IO))
//---------------------------------------------------------------------------
//
@ -256,16 +270,49 @@ const static int GIC_GPIO_IRQ = (32 + 116); // GPIO3
const static int ON = 1;
const static int OFF = 0;
//---------------------------------------------------------------------------
//
// Constant declarations (bus control timing)
//
//---------------------------------------------------------------------------
// SCSI Bus timings taken from:
// https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-05.html
const static int SCSI_DELAY_ARBITRATION_DELAY_NS = 2400;
const static int SCSI_DELAY_ASSERTION_PERIOD_NS = 90;
const static int SCSI_DELAY_BUS_CLEAR_DELAY_NS = 800;
const static int SCSI_DELAY_BUS_FREE_DELAY_NS = 800;
const static int SCSI_DELAY_BUS_SET_DELAY_NS = 1800;
const static int SCSI_DELAY_BUS_SETTLE_DELAY_NS = 400;
const static int SCSI_DELAY_CABLE_SKEW_DELAY_NS = 10;
const static int SCSI_DELAY_DATA_RELEASE_DELAY_NS = 400;
const static int SCSI_DELAY_DESKEW_DELAY_NS = 45;
const static int SCSI_DELAY_DISCONNECTION_DELAY_US = 200;
const static int SCSI_DELAY_HOLD_TIME_NS = 45;
const static int SCSI_DELAY_NEGATION_PERIOD_NS = 90;
const static int SCSI_DELAY_POWER_ON_TO_SELECTION_TIME_S = 10; // (recommended)
const static int SCSI_DELAY_RESET_TO_SELECTION_TIME_US = 250*1000; // (recommended)
const static int SCSI_DELAY_RESET_HOLD_TIME_US = 25;
const static int SCSI_DELAY_SELECTION_ABORT_TIME_US = 200;
const static int SCSI_DELAY_SELECTION_TIMEOUT_DELAY_NS = 250*1000; // (recommended)
const static int SCSI_DELAY_FAST_ASSERTION_PERIOD_NS = 30;
const static int SCSI_DELAY_FAST_CABLE_SKEW_DELAY_NS = 5;
const static int SCSI_DELAY_FAST_DESKEW_DELAY_NS = 20;
const static int SCSI_DELAY_FAST_HOLD_TIME_NS = 10;
const static int SCSI_DELAY_FAST_NEGATION_PERIOD_NS = 30;
// The DaynaPort SCSI Link do a short delay in the middle of transfering
// a packet. This is the number of uS that will be delayed between the
// header and the actual data.
const static int SCSI_DELAY_SEND_DATA_DAYNAPORT_US = 100;
//---------------------------------------------------------------------------
//
// Class definition
//
//---------------------------------------------------------------------------
class GPIOBUS : public BUS
class GPIOBUS final : public BUS
{
public:
static GPIOBUS *create();
// Basic Functions
GPIOBUS()= default;
~GPIOBUS() override = default;
@ -277,7 +324,28 @@ class GPIOBUS : public BUS
void Cleanup() override;
// Cleanup
uint32_t Acquire() override = 0;
//---------------------------------------------------------------------------
//
// Bus signal acquisition
//
//---------------------------------------------------------------------------
inline uint32_t Acquire() override
{
#if defined(__x86_64__) || defined(__X86__)
// Only used for development/debugging purposes. Isn't really applicable
// to any real-world RaSCSI application
return 0;
#else
signals = *level;
#if SIGNAL_CONTROL_MODE < 2
// Invert if negative logic (internal processing is unified to positive logic)
signals = ~signals;
#endif // SIGNAL_CONTROL_MODE
return signals;
#endif // ifdef __x86_64__ || __X86__
}
void SetENB(bool ast);
// Set ENB signal
@ -331,6 +399,11 @@ class GPIOBUS : public BUS
// Get REQ signal
void SetREQ(bool ast) override;
// Set REQ signal
uint8_t GetDAT() override;
// Get DAT signal
void SetDAT(uint8_t dat) override;
// Set DAT signal
bool GetDP() const override;
// Get Data parity signal
int CommandHandShake(vector<uint8_t>&) override;
@ -345,57 +418,49 @@ class GPIOBUS : public BUS
#ifdef USE_SEL_EVENT_ENABLE
// SEL signal interrupt
bool PollSelectEvent();
bool PollSelectEvent() override;
// SEL signal event polling
void ClearSelectEvent();
void ClearSelectEvent() override;
// Clear SEL signal event
#endif // USE_SEL_EVENT_ENABLE
#endif
protected:
private:
// SCSI I/O signal control
virtual void MakeTable() = 0;
void MakeTable();
// Create work data
virtual void SetControl(int pin, bool ast) = 0;
void SetControl(int pin, bool ast);
// Set Control Signal
virtual void SetMode(int pin, int mode) = 0;
void SetMode(int pin, int mode);
// Set SCSI I/O mode
bool GetSignal(int pin) const override = 0;
// Set Control Signal
void SetSignal(int pin, bool ast) override = 0;
// Set SCSI I/O mode
virtual bool WaitSignal(int pin, int ast) = 0;
bool GetSignal(int pin) const override;
// Get SCSI input signal value
void SetSignal(int pin, bool ast) override;
// Set SCSI output signal value
bool WaitSignal(int pin, int ast);
// Wait for a signal to change
// Interrupt control
virtual void DisableIRQ() = 0;
void DisableIRQ();
// IRQ Disabled
virtual void EnableIRQ() = 0;
void EnableIRQ();
// IRQ Enabled
// GPIO pin functionality settings
virtual void PinConfig(int pin, int mode) = 0;
void PinConfig(int pin, int mode);
// GPIO pin direction setting
virtual void PullConfig(int pin, int mode) = 0;
void PullConfig(int pin, int mode);
// GPIO pin pull up/down resistor setting
virtual void PinSetSignal(int pin, bool ast) = 0;
void PinSetSignal(int pin, bool ast);
// Set GPIO output signal
virtual void DrvConfig(uint32_t drive) = 0;
void DrvConfig(uint32_t drive);
// Set GPIO drive strength
mode_e actmode = mode_e::TARGET; // Operation mode
#if !defined(__x86_64__) && !defined(__X86__)
uint32_t baseaddr = 0; // Base address
#endif
static const array<int, 19> SignalTable; // signal table
#ifdef USE_SEL_EVENT_ENABLE
struct gpioevent_request selevreq = {}; // SEL signal event request
int epfd; // epoll file descriptor
#endif // USE_SEL_EVENT_ENABLE
private:
int rpitype = 0; // Type of Raspberry Pi
volatile uint32_t *gpio = nullptr; // GPIO register
@ -428,6 +493,12 @@ class GPIOBUS : public BUS
uint32_t signals = 0; // All bus signals
#ifdef USE_SEL_EVENT_ENABLE
struct gpioevent_request selevreq = {}; // SEL signal event request
int epfd; // epoll file descriptor
#endif // USE_SEL_EVENT_ENABLE
#if SIGNAL_CONTROL_MODE == 0
array<array<uint32_t, 256>, 3> tblDatMsk; // Data mask table
@ -437,4 +508,7 @@ class GPIOBUS : public BUS
array<uint32_t, 256> tblDatSet = {}; // Table setting table
#endif
static const array<int, 19> SignalTable; // signal table
};

View File

@ -1,120 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
//
// [ GPIO-SCSI bus ]
//
//---------------------------------------------------------------------------
#include "hal/gpiobus_allwinner.h"
#include "hal/gpiobus.h"
#include "log.h"
extern int wiringPiMode;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wunused-parameter"
bool GPIOBUS_Allwinner::Init(mode_e mode)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
return false;
}
void GPIOBUS_Allwinner::Cleanup()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
}
void GPIOBUS_Allwinner::Reset(){LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)}
uint8_t GPIOBUS_Allwinner::GetDAT()
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
return 0;
}
void GPIOBUS_Allwinner::SetDAT(uint8_t dat)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
}
void GPIOBUS_Allwinner::MakeTable(void)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
}
void GPIOBUS_Allwinner::SetControl(int pin, bool ast)
{
GPIOBUS_Allwinner::SetSignal(pin, ast);
}
void GPIOBUS_Allwinner::SetMode(int pin, int mode)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
}
bool GPIOBUS_Allwinner::GetSignal(int pin) const
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
return false;
// return (digitalRead(pin) != 0);
}
void GPIOBUS_Allwinner::SetSignal(int pin, bool ast)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
}
bool GPIOBUS_Allwinner::WaitSignal(int pin, int ast)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
return false;
}
void GPIOBUS_Allwinner::DisableIRQ()
{
LOGERROR("%s not implemented!!", __PRETTY_FUNCTION__)
}
void GPIOBUS_Allwinner::EnableIRQ()
{
LOGERROR("%s not implemented!!", __PRETTY_FUNCTION__)
}
void GPIOBUS_Allwinner::PinConfig(int pin, int mode)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
}
void GPIOBUS_Allwinner::PullConfig(int pin, int mode)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
}
void GPIOBUS_Allwinner::PinSetSignal(int pin, bool ast)
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
}
void GPIOBUS_Allwinner::DrvConfig(uint32_t drive)
{
(void)drive;
LOGERROR("%s not implemented!!", __PRETTY_FUNCTION__)
}
uint32_t GPIOBUS_Allwinner::Acquire()
{
LOGWARN("%s NOT IMPLEMENTED", __PRETTY_FUNCTION__)
// Only used for development/debugging purposes. Isn't really applicable
// to any real-world RaSCSI application
return 0;
}
#pragma GCC diagnostic pop

View File

@ -1,128 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ GPIO-SCSI bus ]
//
//---------------------------------------------------------------------------
#pragma once
#include "config.h"
#include "hal/gpiobus.h"
#include "log.h"
#include "scsi.h"
//---------------------------------------------------------------------------
//
// Class definition
//
//---------------------------------------------------------------------------
class GPIOBUS_Allwinner : public GPIOBUS
{
public:
// Basic Functions
GPIOBUS_Allwinner() = default;
~GPIOBUS_Allwinner() override = default;
// Destructor
bool Init(mode_e mode = mode_e::TARGET) override;
// Initialization
void Reset() override;
// Reset
void Cleanup() override;
// Cleanup
//---------------------------------------------------------------------------
//
// Bus signal acquisition
//
//---------------------------------------------------------------------------
uint32_t Acquire() override;
uint8_t GetDAT() override;
// Get DAT signal
void SetDAT(uint8_t dat) override;
// Set DAT signal
protected:
// SCSI I/O signal control
void MakeTable() override;
// Create work data
void SetControl(int pin, bool ast) override;
// Set Control Signal
void SetMode(int pin, int mode) override;
// Set SCSI I/O mode
bool GetSignal(int pin) const override;
// Get SCSI input signal value
void SetSignal(int pin, bool ast) override;
// Set SCSI output signal value
bool WaitSignal(int pin, int ast) override;
// Wait for a signal to change
// Interrupt control
void DisableIRQ() override;
// IRQ Disabled
void EnableIRQ() override;
// IRQ Enabled
// GPIO pin functionality settings
void PinConfig(int pin, int mode) override;
// GPIO pin direction setting
void PullConfig(int pin, int mode) override;
// GPIO pin pull up/down resistor setting
void PinSetSignal(int pin, bool ast) override;
// Set GPIO output signal
void DrvConfig(uint32_t drive) override;
// Set GPIO drive strength
#if !defined(__x86_64__) && !defined(__X86__)
uint32_t baseaddr = 0; // Base address
#endif
volatile uint32_t *gpio = nullptr; // GPIO register
volatile uint32_t *pads = nullptr; // PADS register
#if !defined(__x86_64__) && !defined(__X86__)
volatile uint32_t *level = nullptr; // GPIO input level
#endif
volatile uint32_t *irpctl = nullptr; // Interrupt control register
volatile uint32_t irptenb; // Interrupt enabled state
volatile uint32_t *qa7regs = nullptr; // QA7 register
volatile int tintcore; // Interupt control target CPU.
volatile uint32_t tintctl; // Interupt control
volatile uint32_t giccpmr; // GICC priority setting
#if !defined(__x86_64__) && !defined(__X86__)
volatile uint32_t *gicd = nullptr; // GIC Interrupt distributor register
#endif
volatile uint32_t *gicc = nullptr; // GIC CPU interface register
array<uint32_t, 4> gpfsel; // GPFSEL0-4 backup values
uint32_t signals = 0; // All bus signals
#ifdef USE_SEL_EVENT_ENABLE
struct gpioevent_request selevreq = {}; // SEL signal event request
int epfd; // epoll file descriptor
#endif // USE_SEL_EVENT_ENABLE
#if SIGNAL_CONTROL_MODE == 0
array<array<uint32_t, 256>, 3> tblDatMsk; // Data mask table
array<array<uint32_t, 256>, 3> tblDatSet; // Data setting table
#else
array<uint32_t, 256> tblDatMsk = {}; // Data mask table
array<uint32_t, 256> tblDatSet = {}; // Table setting table
#endif
};

View File

@ -4,27 +4,24 @@
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
// [ GPIO bus factory ]
//
//---------------------------------------------------------------------------
#include <memory>
#include "hal/gpiobus_allwinner.h"
#include "hal/gpiobus_factory.h"
#include "hal/gpiobus_raspberry.h"
#include "hal/sbc_version.h"
#include "log.h"
#include "hal/gpiobus.h"
using namespace std;
unique_ptr<GPIOBUS> GPIOBUS_Factory::Create()
unique_ptr<BUS> GPIOBUS_Factory::Create(BUS::mode_e mode)
{
if (SBC_Version::IsBananaPi()) {
LOGTRACE("Creating GPIOBUS_Allwinner")
return make_unique<GPIOBUS_Allwinner>();
} else {
LOGTRACE("Creating GPIOBUS_Raspberry")
return make_unique<GPIOBUS_Raspberry>();
// TODO Make the factory a friend of GPIOBUS and make the GPIOBUS constructor private
// so that clients cannot use it anymore but have to use the factory.
// Also make Init() private.
if (auto bus = make_unique<GPIOBUS>(); bus->Init(mode)) {
bus->Reset();
return bus;
}
return nullptr;
}

View File

@ -3,22 +3,20 @@
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ GPIO-SCSI bus ]
// Copyright (C) 2022 akuker
//
//---------------------------------------------------------------------------
#pragma once
#include "bus.h"
#include <memory>
#include "gpiobus.h"
using namespace std;
class GPIOBUS_Factory
{
public:
static unique_ptr<GPIOBUS> Create();
static unique_ptr<BUS> Create(BUS::mode_e);
};

View File

@ -54,3 +54,4 @@ const static int PIN_CD = 18; // CD
const static int PIN_IO = 4; // IO
const static int PIN_BSY = 27; // BSY
const static int PIN_SEL = 23; // SEL

View File

@ -1,856 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
//
// [ GPIO-SCSI bus ]
//
//---------------------------------------------------------------------------
#include <sys/mman.h>
#include "config.h"
#include "hal/gpiobus.h"
#include "hal/gpiobus_raspberry.h"
#include "hal/systimer.h"
#include "log.h"
#include <string.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/time.h>
#ifdef __linux__
//---------------------------------------------------------------------------
//
// imported from bcm_host.c
//
//---------------------------------------------------------------------------
static uint32_t get_dt_ranges(const char *filename, uint32_t offset)
{
GPIO_FUNCTION_TRACE
uint32_t address = ~0;
if (FILE *fp = fopen(filename, "rb"); fp) {
fseek(fp, offset, SEEK_SET);
if (array<uint8_t, 4> buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) {
address = (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0;
}
fclose(fp);
}
return address;
}
uint32_t bcm_host_get_peripheral_address()
{
GPIO_FUNCTION_TRACE
uint32_t address = get_dt_ranges("/proc/device-tree/soc/ranges", 4);
if (address == 0) {
address = get_dt_ranges("/proc/device-tree/soc/ranges", 8);
}
address = (address == (uint32_t)~0) ? 0x20000000 : address;
return address;
}
#endif // __linux__
#ifdef __NetBSD__
// Assume the Raspberry Pi series and estimate the address from CPU
uint32_t bcm_host_get_peripheral_address()
{
GPIO_FUNCTION_TRACE
array<char, 1024> buf;
size_t len = buf.size();
uint32_t address;
if (sysctlbyname("hw.model", buf.data(), &len, NULL, 0) || strstr(buf, "ARM1176JZ-S") != buf.data()) {
// Failed to get CPU model || Not BCM2835
// use the address of BCM283[67]
address = 0x3f000000;
} else {
// Use BCM2835 address
address = 0x20000000;
}
LOGDEBUG("Peripheral address : 0x%lx\n", address);
return address;
}
#endif // __NetBSD__
bool GPIOBUS_Raspberry::Init(mode_e mode)
{
GPIO_FUNCTION_TRACE
GPIOBUS::Init(mode);
#if defined(__x86_64__) || defined(__X86__)
// When we're running on x86, there is no hardware to talk to, so just return.
return true;
#else
#ifdef USE_SEL_EVENT_ENABLE
epoll_event ev = {};
#endif
// Get the base address
baseaddr = (uint32_t)bcm_host_get_peripheral_address();
LOGTRACE("%s Base addr: %08X", __PRETTY_FUNCTION__, baseaddr);
// Open /dev/mem
int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd == -1) {
LOGERROR("Error: Unable to open /dev/mem. Are you running as root?")
return false;
}
LOGTRACE("%s Mapping Memory....", __PRETTY_FUNCTION__);
// Map peripheral region memory
void *map = mmap(NULL, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr);
if (map == MAP_FAILED) {
LOGERROR("Error: Unable to map memory")
close(fd);
return false;
}
LOGTRACE("%s Done!", __PRETTY_FUNCTION__);
// Determine the type of raspberry pi from the base address
if (baseaddr == 0xfe000000) {
rpitype = 4;
LOGINFO("%s I'm a pi 4", __PRETTY_FUNCTION__);
} else if (baseaddr == 0x3f000000) {
rpitype = 2;
} else {
rpitype = 1;
}
// GPIO
gpio = (uint32_t *)map;
gpio += GPIO_OFFSET / sizeof(uint32_t);
level = &gpio[GPIO_LEV_0];
// PADS
pads = (uint32_t *)map;
pads += PADS_OFFSET / sizeof(uint32_t);
// // System timer
// SysTimer::Init(
// (uint32_t *)map + SYST_OFFSET / sizeof(uint32_t),
// (uint32_t *)map + ARMT_OFFSET / sizeof(uint32_t));
SysTimer::Init();
// Interrupt controller
irpctl = (uint32_t *)map;
irpctl += IRPT_OFFSET / sizeof(uint32_t);
// Quad-A7 control
qa7regs = (uint32_t *)map;
qa7regs += QA7_OFFSET / sizeof(uint32_t);
LOGTRACE("%s Mapping GIC Memory....", __PRETTY_FUNCTION__);
// Map GIC memory
if (rpitype == 4) {
map = mmap(NULL, 8192, PROT_READ | PROT_WRITE, MAP_SHARED, fd, ARM_GICD_BASE);
if (map == MAP_FAILED) {
close(fd);
return false;
}
gicd = (uint32_t *)map;
gicc = (uint32_t *)map;
gicc += (ARM_GICC_BASE - ARM_GICD_BASE) / sizeof(uint32_t);
} else {
gicd = NULL;
gicc = NULL;
}
close(fd);
LOGTRACE("%s Set Drive Strength", __PRETTY_FUNCTION__);
// Set Drive Strength to 16mA
DrvConfig(7);
// Set pull up/pull down
LOGTRACE("%s Set pull up/down....", __PRETTY_FUNCTION__);
#if SIGNAL_CONTROL_MODE == 0
LOGTRACE("%s GPIO_PULLNONE", __PRETTY_FUNCTION__);
int pullmode = GPIO_PULLNONE;
#elif SIGNAL_CONTROL_MODE == 1
LOGTRACE("%s GPIO_PULLUP", __PRETTY_FUNCTION__);
int pullmode = GPIO_PULLUP;
#else
LOGTRACE("%s GPIO_PULLDOWN", __PRETTY_FUNCTION__);
int pullmode = GPIO_PULLDOWN;
#endif
// Initialize all signals
LOGTRACE("%s Initialize all signals....", __PRETTY_FUNCTION__);
for (int i = 0; SignalTable[i] >= 0; i++) {
int j = SignalTable[i];
PinSetSignal(j, OFF);
PinConfig(j, GPIO_INPUT);
PullConfig(j, pullmode);
}
// Set control signals
LOGTRACE("%s Set control signals....", __PRETTY_FUNCTION__);
PinSetSignal(PIN_ACT, OFF);
PinSetSignal(PIN_TAD, OFF);
PinSetSignal(PIN_IND, OFF);
PinSetSignal(PIN_DTD, OFF);
PinConfig(PIN_ACT, GPIO_OUTPUT);
PinConfig(PIN_TAD, GPIO_OUTPUT);
PinConfig(PIN_IND, GPIO_OUTPUT);
PinConfig(PIN_DTD, GPIO_OUTPUT);
// Set the ENABLE signal
// This is used to show that the application is running
PinSetSignal(PIN_ENB, ENB_OFF);
PinConfig(PIN_ENB, GPIO_OUTPUT);
// GPFSEL backup
LOGTRACE("%s GPFSEL backup....", __PRETTY_FUNCTION__);
gpfsel[0] = gpio[GPIO_FSEL_0];
gpfsel[1] = gpio[GPIO_FSEL_1];
gpfsel[2] = gpio[GPIO_FSEL_2];
gpfsel[3] = gpio[GPIO_FSEL_3];
// Initialize SEL signal interrupt
#ifdef USE_SEL_EVENT_ENABLE
// GPIO chip open
LOGTRACE("%s GPIO chip open", __PRETTY_FUNCTION__);
int gpio_fd = open("/dev/gpiochip0", 0);
if (gpio_fd == -1) {
LOGERROR("Unable to open /dev/gpiochip0. Is RaSCSI already running?")
return false;
}
// Event request setting
LOGTRACE("%s Event request setting (pin sel: %d)", __PRETTY_FUNCTION__, PIN_SEL);
strcpy(selevreq.consumer_label, "RaSCSI");
selevreq.lineoffset = PIN_SEL;
selevreq.handleflags = GPIOHANDLE_REQUEST_INPUT;
#if SIGNAL_CONTROL_MODE < 2
selevreq.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE;
LOGTRACE("%s eventflags = GPIOEVENT_REQUEST_FALLING_EDGE", __PRETTY_FUNCTION__);
#else
selevreq.eventflags = GPIOEVENT_REQUEST_RISING_EDGE;
LOGTRACE("%s eventflags = GPIOEVENT_REQUEST_RISING_EDGE", __PRETTY_FUNCTION__);
#endif // SIGNAL_CONTROL_MODE
// Get event request
if (ioctl(gpio_fd, GPIO_GET_LINEEVENT_IOCTL, &selevreq) == -1) {
LOGERROR("selevreq.fd = %d %08X", selevreq.fd, (unsigned int)selevreq.fd);
LOGERROR("Unable to register event request. Is RaSCSI already running?")
LOGERROR("[%08X] %s", errno, strerror(errno));
close(gpio_fd);
return false;
}
// Close GPIO chip file handle
LOGTRACE("%s Close GPIO chip file handle", __PRETTY_FUNCTION__);
close(gpio_fd);
// epoll initialization
LOGTRACE("%s epoll initialization", __PRETTY_FUNCTION__);
epfd = epoll_create(1);
if (epfd == -1) {
LOGERROR("Unable to create the epoll event");
return false;
}
memset(&ev, 0, sizeof(ev));
ev.events = EPOLLIN | EPOLLPRI;
ev.data.fd = selevreq.fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, selevreq.fd, &ev);
#else
// Edge detection setting
#if SIGNAL_CONTROL_MODE == 2
gpio[GPIO_AREN_0] = 1 << PIN_SEL;
#else
gpio[GPIO_AFEN_0] = 1 << PIN_SEL;
#endif // SIGNAL_CONTROL_MODE
// Clear event
gpio[GPIO_EDS_0] = 1 << PIN_SEL;
// Register interrupt handler
setIrqFuncAddress(IrqHandler);
// GPIO interrupt setting
if (rpitype == 4) {
// GIC Invalid
gicd[GICD_CTLR] = 0;
// Route all interupts to core 0
for (i = 0; i < 8; i++) {
gicd[GICD_ICENABLER0 + i] = 0xffffffff;
gicd[GICD_ICPENDR0 + i] = 0xffffffff;
gicd[GICD_ICACTIVER0 + i] = 0xffffffff;
}
for (i = 0; i < 64; i++) {
gicd[GICD_IPRIORITYR0 + i] = 0xa0a0a0a0;
gicd[GICD_ITARGETSR0 + i] = 0x01010101;
}
// Set all interrupts as level triggers
for (i = 0; i < 16; i++) {
gicd[GICD_ICFGR0 + i] = 0;
}
// GIC Invalid
gicd[GICD_CTLR] = 1;
// Enable CPU interface for core 0
gicc[GICC_PMR] = 0xf0;
gicc[GICC_CTLR] = 1;
// Enable interrupts
gicd[GICD_ISENABLER0 + (GIC_GPIO_IRQ / 32)] = 1 << (GIC_GPIO_IRQ % 32);
} else {
// Enable interrupts
irpctl[IRPT_ENB_IRQ_2] = (1 << (GPIO_IRQ % 32));
}
#endif // USE_SEL_EVENT_ENABLE
// Create work table
MakeTable();
// Finally, enable ENABLE
LOGTRACE("%s Finally, enable ENABLE....", __PRETTY_FUNCTION__);
// Show the user that this app is running
SetControl(PIN_ENB, ENB_ON);
return true;
#endif // ifdef __x86_64__ || __X86__
}
void GPIOBUS_Raspberry::Cleanup()
{
GPIO_FUNCTION_TRACE
#if defined(__x86_64__) || defined(__X86__)
return;
#else
// Release SEL signal interrupt
#ifdef USE_SEL_EVENT_ENABLE
close(selevreq.fd);
#endif // USE_SEL_EVENT_ENABLE
// Set control signals
PinSetSignal(PIN_ENB, OFF);
PinSetSignal(PIN_ACT, OFF);
PinSetSignal(PIN_TAD, OFF);
PinSetSignal(PIN_IND, OFF);
PinSetSignal(PIN_DTD, OFF);
PinConfig(PIN_ACT, GPIO_INPUT);
PinConfig(PIN_TAD, GPIO_INPUT);
PinConfig(PIN_IND, GPIO_INPUT);
PinConfig(PIN_DTD, GPIO_INPUT);
// Initialize all signals
for (int i = 0; SignalTable[i] >= 0; i++) {
int pin = SignalTable[i];
PinSetSignal(pin, OFF);
PinConfig(pin, GPIO_INPUT);
PullConfig(pin, GPIO_PULLNONE);
}
// Set drive strength back to 8mA
DrvConfig(3);
#endif // ifdef __x86_64__ || __X86__
}
void GPIOBUS_Raspberry::Reset()
{
GPIO_FUNCTION_TRACE
#if defined(__x86_64__) || defined(__X86__)
return;
#else
int i;
int j;
// Turn off active signal
SetControl(PIN_ACT, ACT_OFF);
// Set all signals to off
for (i = 0;; i++) {
j = SignalTable[i];
if (j < 0) {
break;
}
SetSignal(j, OFF);
}
if (actmode == mode_e::TARGET) {
// Target mode
// Set target signal to input
SetControl(PIN_TAD, TAD_IN);
SetMode(PIN_BSY, IN);
SetMode(PIN_MSG, IN);
SetMode(PIN_CD, IN);
SetMode(PIN_REQ, IN);
SetMode(PIN_IO, IN);
// Set the initiator signal to input
SetControl(PIN_IND, IND_IN);
SetMode(PIN_SEL, IN);
SetMode(PIN_ATN, IN);
SetMode(PIN_ACK, IN);
SetMode(PIN_RST, IN);
// Set data bus signals to input
SetControl(PIN_DTD, DTD_IN);
SetMode(PIN_DT0, IN);
SetMode(PIN_DT1, IN);
SetMode(PIN_DT2, IN);
SetMode(PIN_DT3, IN);
SetMode(PIN_DT4, IN);
SetMode(PIN_DT5, IN);
SetMode(PIN_DT6, IN);
SetMode(PIN_DT7, IN);
SetMode(PIN_DP, IN);
} else {
// Initiator mode
// Set target signal to input
SetControl(PIN_TAD, TAD_IN);
SetMode(PIN_BSY, IN);
SetMode(PIN_MSG, IN);
SetMode(PIN_CD, IN);
SetMode(PIN_REQ, IN);
SetMode(PIN_IO, IN);
// Set the initiator signal to output
SetControl(PIN_IND, IND_OUT);
SetMode(PIN_SEL, OUT);
SetMode(PIN_ATN, OUT);
SetMode(PIN_ACK, OUT);
SetMode(PIN_RST, OUT);
// Set the data bus signals to output
SetControl(PIN_DTD, DTD_OUT);
SetMode(PIN_DT0, OUT);
SetMode(PIN_DT1, OUT);
SetMode(PIN_DT2, OUT);
SetMode(PIN_DT3, OUT);
SetMode(PIN_DT4, OUT);
SetMode(PIN_DT5, OUT);
SetMode(PIN_DT6, OUT);
SetMode(PIN_DT7, OUT);
SetMode(PIN_DP, OUT);
}
// Initialize all signals
signals = 0;
#endif // ifdef __x86_64__ || __X86__
}
//---------------------------------------------------------------------------
//
// Get data signals
//
//---------------------------------------------------------------------------
uint8_t GPIOBUS_Raspberry::GetDAT()
{
GPIO_FUNCTION_TRACE
uint32_t data = Acquire();
data = ((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 (uint8_t)data;
}
//---------------------------------------------------------------------------
//
// Set data signals
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::SetDAT(uint8_t dat)
{
GPIO_FUNCTION_TRACE
// Write to port
#if SIGNAL_CONTROL_MODE == 0
uint32_t fsel = gpfsel[0];
fsel &= tblDatMsk[0][dat];
fsel |= tblDatSet[0][dat];
if (fsel != gpfsel[0]) {
gpfsel[0] = fsel;
gpio[GPIO_FSEL_0] = fsel;
}
fsel = gpfsel[1];
fsel &= tblDatMsk[1][dat];
fsel |= tblDatSet[1][dat];
if (fsel != gpfsel[1]) {
gpfsel[1] = fsel;
gpio[GPIO_FSEL_1] = fsel;
}
fsel = gpfsel[2];
fsel &= tblDatMsk[2][dat];
fsel |= tblDatSet[2][dat];
if (fsel != gpfsel[2]) {
gpfsel[2] = fsel;
gpio[GPIO_FSEL_2] = fsel;
}
#else
gpio[GPIO_CLR_0] = tblDatMsk[dat];
gpio[GPIO_SET_0] = tblDatSet[dat];
#endif // SIGNAL_CONTROL_MODE
}
//---------------------------------------------------------------------------
//
// Create work table
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::MakeTable(void)
{
GPIO_FUNCTION_TRACE
const array<int, 9> pintbl = {PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP};
array<bool, 256> tblParity;
// Create parity table
for (uint32_t i = 0; i < 0x100; i++) {
uint32_t bits = i;
uint32_t parity = 0;
for (int j = 0; j < 8; j++) {
parity ^= bits & 1;
bits >>= 1;
}
parity = ~parity;
tblParity[i] = parity & 1;
}
#if SIGNAL_CONTROL_MODE == 0
// Mask and setting data generation
for (auto &tbl : tblDatMsk) {
tbl.fill(-1);
}
for (auto &tbl : tblDatSet) {
tbl.fill(0);
}
for (uint32_t i = 0; i < 0x100; i++) {
// Bit string for inspection
uint32_t bits = i;
// Get parity
if (tblParity[i]) {
bits |= (1 << 8);
}
// Bit check
for (int j = 0; j < 9; j++) {
// Index and shift amount calculation
int index = pintbl[j] / 10;
int shift = (pintbl[j] % 10) * 3;
// Mask data
tblDatMsk[index][i] &= ~(0x7 << shift);
// Setting data
if (bits & 1) {
tblDatSet[index][i] |= (1 << shift);
}
bits >>= 1;
}
}
#else
for (uint32_t i = 0; i < 0x100; i++) {
// Bit string for inspection
uint32_t bits = i;
// Get parity
if (tblParity[i]) {
bits |= (1 << 8);
}
#if SIGNAL_CONTROL_MODE == 1
// Negative logic is inverted
bits = ~bits;
#endif
// Create GPIO register information
uint32_t gpclr = 0;
uint32_t gpset = 0;
for (int j = 0; j < 9; j++) {
if (bits & 1) {
gpset |= (1 << pintbl[j]);
} else {
gpclr |= (1 << pintbl[j]);
}
bits >>= 1;
}
tblDatMsk[i] = gpclr;
tblDatSet[i] = gpset;
}
#endif
}
//---------------------------------------------------------------------------
//
// Control signal setting
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::SetControl(int pin, bool ast)
{
PinSetSignal(pin, ast);
}
//---------------------------------------------------------------------------
//
// Input/output mode setting
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::SetMode(int pin, int mode)
{
#if SIGNAL_CONTROL_MODE == 0
if (mode == OUT) {
return;
}
#endif // SIGNAL_CONTROL_MODE
int index = pin / 10;
int shift = (pin % 10) * 3;
uint32_t data = gpfsel[index];
data &= ~(0x7 << shift);
if (mode == OUT) {
data |= (1 << shift);
}
gpio[index] = data;
gpfsel[index] = data;
}
//---------------------------------------------------------------------------
//
// Get input signal value
//
//---------------------------------------------------------------------------
bool GPIOBUS_Raspberry::GetSignal(int pin) const
{
return (signals >> pin) & 1;
}
//---------------------------------------------------------------------------
//
// Set output signal value
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::SetSignal(int pin, bool ast)
{
#if SIGNAL_CONTROL_MODE == 0
int index = pin / 10;
int shift = (pin % 10) * 3;
uint32_t data = gpfsel[index];
if (ast) {
data |= (1 << shift);
} else {
data &= ~(0x7 << shift);
}
gpio[index] = data;
gpfsel[index] = data;
#elif SIGNAL_CONTROL_MODE == 1
if (ast) {
gpio[GPIO_CLR_0] = 0x1 << pin;
} else {
gpio[GPIO_SET_0] = 0x1 << pin;
}
#elif SIGNAL_CONTROL_MODE == 2
if (ast) {
gpio[GPIO_SET_0] = 0x1 << pin;
} else {
gpio[GPIO_CLR_0] = 0x1 << pin;
}
#endif // SIGNAL_CONTROL_MODE
}
//---------------------------------------------------------------------------
//
// Wait for signal change
//
// TODO: maybe this should be moved to SCSI_Bus?
//---------------------------------------------------------------------------
bool GPIOBUS_Raspberry::WaitSignal(int pin, int ast)
{
// Get current time
uint32_t now = SysTimer::GetTimerLow();
// Calculate timeout (3000ms)
uint32_t timeout = 3000 * 1000;
// end immediately if the signal has changed
do {
// Immediately upon receiving a reset
Acquire();
if (GetRST()) {
return false;
}
// Check for the signal edge
if (((signals >> pin) ^ ~ast) & 1) {
return true;
}
} while ((SysTimer::GetTimerLow() - now) < timeout);
// We timed out waiting for the signal
return false;
}
void GPIOBUS_Raspberry::DisableIRQ()
{
GPIO_FUNCTION_TRACE
#ifdef __linux
if (rpitype == 4) {
// RPI4 is disabled by GICC
giccpmr = gicc[GICC_PMR];
gicc[GICC_PMR] = 0;
} else if (rpitype == 2) {
// RPI2,3 disable core timer IRQ
tintcore = sched_getcpu() + QA7_CORE0_TINTC;
tintctl = qa7regs[tintcore];
qa7regs[tintcore] = 0;
} else {
// Stop system timer interrupt with interrupt controller
irptenb = irpctl[IRPT_ENB_IRQ_1];
irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf;
}
#else
(void)
0;
#endif
}
void GPIOBUS_Raspberry::EnableIRQ()
{
GPIO_FUNCTION_TRACE
if (rpitype == 4) {
// RPI4 enables interrupts via the GICC
gicc[GICC_PMR] = giccpmr;
} else if (rpitype == 2) {
// RPI2,3 re-enable core timer IRQ
qa7regs[tintcore] = tintctl;
} else {
// Restart the system timer interrupt with the interrupt controller
irpctl[IRPT_ENB_IRQ_1] = irptenb & 0xf;
}
}
//---------------------------------------------------------------------------
//
// Pin direction setting (input/output)
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::PinConfig(int pin, int mode)
{
// Check for invalid pin
if (pin < 0) {
return;
}
int index = pin / 10;
uint32_t mask = ~(0x7 << ((pin % 10) * 3));
gpio[index] = (gpio[index] & mask) | ((mode & 0x7) << ((pin % 10) * 3));
}
//---------------------------------------------------------------------------
//
// Pin pull-up/pull-down setting
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::PullConfig(int pin, int mode)
{
uint32_t pull;
// Check for invalid pin
if (pin < 0) {
return;
}
if (rpitype == 4) {
LOGTRACE("%s I'm a Pi 4", __PRETTY_FUNCTION__)
switch (mode) {
case GPIO_PULLNONE:
pull = 0;
break;
case GPIO_PULLUP:
pull = 1;
break;
case GPIO_PULLDOWN:
pull = 2;
break;
default:
return;
}
pin &= 0x1f;
int shift = (pin & 0xf) << 1;
uint32_t bits = gpio[GPIO_PUPPDN0 + (pin >> 4)];
bits &= ~(3 << shift);
bits |= (pull << shift);
gpio[GPIO_PUPPDN0 + (pin >> 4)] = bits;
} else {
pin &= 0x1f;
gpio[GPIO_PUD] = mode & 0x3;
SysTimer::SleepUsec(2);
gpio[GPIO_CLK_0] = 0x1 << pin;
SysTimer::SleepUsec(2);
gpio[GPIO_PUD] = 0;
gpio[GPIO_CLK_0] = 0;
}
}
//---------------------------------------------------------------------------
//
// Set output pin
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::PinSetSignal(int pin, bool ast)
{
// Check for invalid pin
if (pin < 0) {
return;
}
if (ast) {
gpio[GPIO_SET_0] = 0x1 << pin;
} else {
gpio[GPIO_CLR_0] = 0x1 << pin;
}
}
//---------------------------------------------------------------------------
//
// Set the signal drive strength
//
//---------------------------------------------------------------------------
void GPIOBUS_Raspberry::DrvConfig(uint32_t drive)
{
uint32_t data = pads[PAD_0_27];
pads[PAD_0_27] = (0xFFFFFFF8 & data) | drive | 0x5a000000;
}
uint32_t GPIOBUS_Raspberry::Acquire()
{
GPIO_FUNCTION_TRACE;
#if defined(__x86_64__) || defined(__X86__)
// Only used for development/debugging purposes. Isn't really applicable
// to any real-world RaSCSI application
return 0;
#else
signals = *level;
#if SIGNAL_CONTROL_MODE < 2
// Invert if negative logic (internal processing is unified to positive logic)
signals = ~signals;
#endif // SIGNAL_CONTROL_MODE
return signals;
#endif // ifdef __x86_64__ || __X86__
}

View File

@ -1,124 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// [ GPIO-SCSI bus ]
//
//---------------------------------------------------------------------------
#pragma once
#include "config.h"
#include "hal/gpiobus.h"
#include "log.h"
#include "scsi.h"
//---------------------------------------------------------------------------
//
// Class definition
//
//---------------------------------------------------------------------------
class GPIOBUS_Raspberry final : public GPIOBUS
{
public:
// Basic Functions
GPIOBUS_Raspberry() = default;
~GPIOBUS_Raspberry() override = default;
// Destructor
bool Init(mode_e mode = mode_e::TARGET) override;
// Initialization
void Reset() override;
// Reset
void Cleanup() override;
// Cleanup
//---------------------------------------------------------------------------
//
// Bus signal acquisition
//
//---------------------------------------------------------------------------
uint32_t Acquire() override;
uint8_t GetDAT() override;
// Get DAT signal
void SetDAT(uint8_t dat) override;
// Set DAT signal
private:
// SCSI I/O signal control
void MakeTable() override;
// Create work data
void SetControl(int pin, bool ast) override;
// Set Control Signal
void SetMode(int pin, int mode) override;
// Set SCSI I/O mode
bool GetSignal(int pin) const override;
// Get SCSI input signal value
void SetSignal(int pin, bool ast) override;
// Set SCSI output signal value
bool WaitSignal(int pin, int ast) override;
// Wait for a signal to change
// Interrupt control
void DisableIRQ() override;
// IRQ Disabled
void EnableIRQ() override;
// IRQ Enabled
// GPIO pin functionality settings
void PinConfig(int pin, int mode) override;
// GPIO pin direction setting
void PullConfig(int pin, int mode) override;
// GPIO pin pull up/down resistor setting
void PinSetSignal(int pin, bool ast) override;
// Set GPIO output signal
void DrvConfig(uint32_t drive) override;
// Set GPIO drive strength
#if !defined(__x86_64__) && !defined(__X86__)
uint32_t baseaddr = 0; // Base address
#endif
int rpitype = 0; // Type of Raspberry Pi
volatile uint32_t *gpio = nullptr; // GPIO register
volatile uint32_t *pads = nullptr; // PADS register
#if !defined(__x86_64__) && !defined(__X86__)
volatile uint32_t *level = nullptr; // GPIO input level
#endif
volatile uint32_t *irpctl = nullptr; // Interrupt control register
volatile uint32_t irptenb; // Interrupt enabled state
volatile uint32_t *qa7regs = nullptr; // QA7 register
volatile int tintcore; // Interupt control target CPU.
volatile uint32_t tintctl; // Interupt control
volatile uint32_t giccpmr; // GICC priority setting
#if !defined(__x86_64__) && !defined(__X86__)
volatile uint32_t *gicd = nullptr; // GIC Interrupt distributor register
#endif
volatile uint32_t *gicc = nullptr; // GIC CPU interface register
array<uint32_t, 4> gpfsel; // GPFSEL0-4 backup values
uint32_t signals = 0; // All bus signals
#if SIGNAL_CONTROL_MODE == 0
array<array<uint32_t, 256>, 3> tblDatMsk; // Data mask table
array<array<uint32_t, 256>, 3> tblDatSet; // Data setting table
#else
array<uint32_t, 256> tblDatMsk = {}; // Data mask table
array<uint32_t, 256> tblDatSet = {}; // Table setting table
#endif
};

View File

@ -1,215 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
//
// [ Hardware version detection routines ]
//
//---------------------------------------------------------------------------
#include "sbc_version.h"
#include "log.h"
#include <fstream>
#include <iostream>
#include <sstream>
SBC_Version::sbc_version_type SBC_Version::m_sbc_version = sbc_version_type::sbc_unknown;
const std::string SBC_Version::m_str_raspberry_pi_1 = "Raspberry Pi 1";
const std::string SBC_Version::m_str_raspberry_pi_2_3 = "Raspberry Pi 2/3";
const std::string SBC_Version::m_str_raspberry_pi_4 = "Raspberry Pi 4";
const std::string SBC_Version::m_str_bananapi_m2_berry = "Banana Pi M2 Berry/Ultra";
const std::string SBC_Version::m_str_bananapi_m2_zero = "Banana Pi M2 Zero";
const std::string SBC_Version::m_str_bananapi_m2_plus = "Banana Pi BPI-M2-Plus H3";
const std::string SBC_Version::m_str_bananapi_m3 = "Banana Pi M3";
const std::string SBC_Version::m_str_bananapi_m4 = "Banana Pi M4";
const std::string SBC_Version::m_str_unknown_sbc = "Unknown SBC";
// The strings in this table should align with the 'model' embedded
// in the device tree. This can be aquired by running:
// cat /proc/device-tree/model
// Only the first part of the string is checked. Anything following
// will be ignored. For example:
// "Raspberry Pi 4 Model B" will match with both of the following:
// - Raspberry Pi 4 Model B Rev 1.4
// - Raspberry Pi 4 Model B Rev 1.3
const std::map<std::string, SBC_Version::sbc_version_type, std::less<>> SBC_Version::m_proc_device_tree_mapping = {
{"Raspberry Pi 1 Model ", sbc_version_type::sbc_raspberry_pi_1},
{"Raspberry Pi 2 Model ", sbc_version_type::sbc_raspberry_pi_2_3},
{"Raspberry Pi 3 Model ", sbc_version_type::sbc_raspberry_pi_2_3},
{"Raspberry Pi 4 Model ", sbc_version_type::sbc_raspberry_pi_4},
{"Raspberry Pi 400 ", sbc_version_type::sbc_raspberry_pi_4},
{"Raspberry Pi Zero W", sbc_version_type::sbc_raspberry_pi_1},
{"Raspberry Pi Zero", sbc_version_type::sbc_raspberry_pi_1},
{"Banana Pi BPI-M2-Zero ", sbc_version_type::sbc_bananapi_m2_zero},
{"Banana Pi BPI-M2-Ultra ", sbc_version_type::sbc_bananapi_m2_berry},
{"Banana Pi BPI-M2-Plus H3", sbc_version_type::sbc_bananapi_m2_plus},
{"Banana Pi M2 Berry ", sbc_version_type::sbc_bananapi_m2_berry},
// sbc_bananapi_m3, TBD....
// sbc_bananapi_m4,
};
const std::string SBC_Version::m_device_tree_model_path = "/proc/device-tree/model";
//---------------------------------------------------------------------------
//
// Convert the SBC Version to a printable string
//
//---------------------------------------------------------------------------
const std::string *SBC_Version::GetString()
{
switch (m_sbc_version) {
case sbc_version_type::sbc_raspberry_pi_1:
return &m_str_raspberry_pi_1;
case sbc_version_type::sbc_raspberry_pi_2_3:
return &m_str_raspberry_pi_2_3;
case sbc_version_type::sbc_raspberry_pi_4:
return &m_str_raspberry_pi_4;
case sbc_version_type::sbc_bananapi_m2_berry:
return &m_str_bananapi_m2_berry;
case sbc_version_type::sbc_bananapi_m2_zero:
return &m_str_bananapi_m2_zero;
case sbc_version_type::sbc_bananapi_m2_plus:
return &m_str_bananapi_m2_plus;
case sbc_version_type::sbc_bananapi_m3:
return &m_str_bananapi_m3;
case sbc_version_type::sbc_bananapi_m4:
return &m_str_bananapi_m4;
default:
LOGERROR("Unknown type of sbc detected: %d", static_cast<int>(m_sbc_version))
return &m_str_unknown_sbc;
}
}
SBC_Version::sbc_version_type SBC_Version::GetSbcVersion()
{
return m_sbc_version;
}
//---------------------------------------------------------------------------
//
// Determine which version of single board computer (Pi) is being used
// based upon the device tree model string.
//
//---------------------------------------------------------------------------
void SBC_Version::Init()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
std::string device_tree_model;
const std::ifstream input_stream(SBC_Version::m_device_tree_model_path);
if (input_stream.fail()) {
LOGERROR("Failed to open %s. Are you running as root?", SBC_Version::m_device_tree_model_path.c_str())
throw std::invalid_argument("Failed to open /proc/device-tree/model");
}
std::stringstream str_buffer;
str_buffer << input_stream.rdbuf();
device_tree_model = str_buffer.str();
for (const auto &[key, value] : m_proc_device_tree_mapping) {
if (device_tree_model.rfind(key, 0) == 0) {
m_sbc_version = value;
LOGINFO("Detected device %s", GetString()->c_str())
return;
}
}
LOGERROR("%s Unable to determine single board computer type. Defaulting to Raspberry Pi 4", __PRETTY_FUNCTION__)
m_sbc_version = sbc_version_type::sbc_raspberry_pi_4;
}
bool SBC_Version::IsRaspberryPi()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
switch (m_sbc_version) {
case sbc_version_type::sbc_raspberry_pi_1:
case sbc_version_type::sbc_raspberry_pi_2_3:
case sbc_version_type::sbc_raspberry_pi_4:
return true;
case sbc_version_type::sbc_bananapi_m2_berry:
case sbc_version_type::sbc_bananapi_m2_zero:
case sbc_version_type::sbc_bananapi_m2_plus:
case sbc_version_type::sbc_bananapi_m3:
case sbc_version_type::sbc_bananapi_m4:
return false;
default:
return false;
}
}
bool SBC_Version::IsBananaPi()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
switch (m_sbc_version) {
case sbc_version_type::sbc_raspberry_pi_1:
case sbc_version_type::sbc_raspberry_pi_2_3:
case sbc_version_type::sbc_raspberry_pi_4:
return false;
case sbc_version_type::sbc_bananapi_m2_berry:
case sbc_version_type::sbc_bananapi_m2_zero:
case sbc_version_type::sbc_bananapi_m2_plus:
case sbc_version_type::sbc_bananapi_m3:
case sbc_version_type::sbc_bananapi_m4:
return true;
default:
return false;
}
}
// The following functions are only used on the Raspberry Pi
// (imported from bcm_host.c)
uint32_t SBC_Version::GetDeviceTreeRanges(const char *filename, uint32_t offset)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
uint32_t address = ~0;
if (FILE *fp = fopen(filename, "rb"); fp) {
fseek(fp, offset, SEEK_SET);
if (std::array<uint8_t, 4> buf; fread(buf.data(), 1, buf.size(), fp) == buf.size()) {
address = (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0;
}
fclose(fp);
}
return address;
}
#if defined __linux__
uint32_t SBC_Version::GetPeripheralAddress(void)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
uint32_t address = GetDeviceTreeRanges("/proc/device-tree/soc/ranges", 4);
if (address == 0) {
address = GetDeviceTreeRanges("/proc/device-tree/soc/ranges", 8);
}
address = (address == (uint32_t)~0) ? 0x20000000 : address;
LOGDEBUG("Peripheral address : 0x%8x\n", address)
return address;
}
#elif defined __NetBSD__
uint32_t SBC_Version::GetPeripheralAddress(void)
{
char buf[1024];
size_t len = sizeof(buf);
uint32_t address;
if (sysctlbyname("hw.model", buf, &len, NULL, 0) || strstr(buf, "ARM1176JZ-S") != buf) {
// Failed to get CPU model || Not BCM2835
// use the address of BCM283[67]
address = 0x3f000000;
} else {
// Use BCM2835 address
address = 0x20000000;
}
LOGDEBUG("Peripheral address : 0x%lx\n", address);
return address;
}
#else
uint32_t SBC_Version::GetPeripheralAddress(void)
{
return 0;
}
#endif

View File

@ -1,70 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
//
// [ Hardware version detection routines ]
//
//---------------------------------------------------------------------------
#pragma once
#include <map>
#include <string>
//===========================================================================
//
// Single Board Computer Versions
//
//===========================================================================
class SBC_Version
{
public:
// Type of Single Board Computer
enum class sbc_version_type : uint8_t {
sbc_unknown = 0,
sbc_raspberry_pi_1,
sbc_raspberry_pi_2_3,
sbc_raspberry_pi_4,
sbc_bananapi_m2_berry,
sbc_bananapi_m2_zero,
sbc_bananapi_m2_plus,
sbc_bananapi_m3,
sbc_bananapi_m4,
};
SBC_Version() = delete;
~SBC_Version() = delete;
static void Init();
static sbc_version_type GetSbcVersion();
static bool IsRaspberryPi();
static bool IsBananaPi();
static const std::string *GetString();
static uint32_t GetPeripheralAddress();
private:
static sbc_version_type m_sbc_version;
static const std::string m_str_raspberry_pi_1;
static const std::string m_str_raspberry_pi_2_3;
static const std::string m_str_raspberry_pi_4;
static const std::string m_str_bananapi_m2_berry;
static const std::string m_str_bananapi_m2_zero;
static const std::string m_str_bananapi_m2_plus;
static const std::string m_str_bananapi_m3;
static const std::string m_str_bananapi_m4;
static const std::string m_str_unknown_sbc;
static const std::map<std::string, sbc_version_type, std::less<>> m_proc_device_tree_mapping;
static const std::string m_device_tree_model_path;
static uint32_t GetDeviceTreeRanges(const char *filename, uint32_t offset);
};

View File

@ -12,56 +12,129 @@
//---------------------------------------------------------------------------
#include "hal/systimer.h"
#include "hal/systimer_allwinner.h"
#include "hal/systimer_raspberry.h"
#include <sys/mman.h>
#include "hal/gpiobus.h"
#include "hal/sbc_version.h"
#include "config.h"
#include "log.h"
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <array>
bool SysTimer::initialized = false;
bool SysTimer::is_allwinnner = false;
bool SysTimer::is_raspberry = false;
//---------------------------------------------------------------------------
//
// System timer address
//
//---------------------------------------------------------------------------
volatile uint32_t* SysTimer::systaddr;
std::unique_ptr<PlatformSpecificTimer> SysTimer::systimer_ptr;
//---------------------------------------------------------------------------
//
// ARM timer address
//
//---------------------------------------------------------------------------
volatile uint32_t* SysTimer::armtaddr;
void SysTimer::Init()
//---------------------------------------------------------------------------
//
// Core frequency
//
//---------------------------------------------------------------------------
volatile uint32_t SysTimer::corefreq;
//---------------------------------------------------------------------------
//
// Initialize the system timer
//
//---------------------------------------------------------------------------
void SysTimer::Init(uint32_t *syst, uint32_t *armt)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
// RPI Mailbox property interface
// Get max clock rate
// Tag: 0x00030004
//
// Request: Length: 4
// Value: u32: clock id
// Response: Length: 8
// Value: u32: clock id, u32: rate (in Hz)
//
// Clock id
// 0x000000004: CORE
array<uint32_t, 32> maxclock = { 32, 0, 0x00030004, 8, 0, 4, 0, 0 };
if (!initialized) {
if (SBC_Version::IsRaspberryPi()) {
systimer_ptr = make_unique<SysTimer_Raspberry>();
is_raspberry = true;
} else if (SBC_Version::IsBananaPi()) {
systimer_ptr = make_unique<SysTimer_AllWinner>();
is_allwinnner = true;
}
systimer_ptr->Init();
initialized = true;
// Save the base address
systaddr = syst;
armtaddr = armt;
// Change the ARM timer to free run mode
armtaddr[ARMT_CTRL] = 0x00000282;
// Get the core frequency
corefreq = 0;
int fd = open("/dev/vcio", O_RDONLY);
if (fd >= 0) {
ioctl(fd, _IOWR(100, 0, char *), maxclock);
corefreq = maxclock[6] / 1000000;
}
close(fd);
}
//---------------------------------------------------------------------------
//
// Get system timer low byte
uint32_t SysTimer::GetTimerLow()
{
return systimer_ptr->GetTimerLow();
//
//---------------------------------------------------------------------------
uint32_t SysTimer::GetTimerLow() {
return systaddr[SYST_CLO];
}
//---------------------------------------------------------------------------
//
// Get system timer high byte
uint32_t SysTimer::GetTimerHigh()
{
return systimer_ptr->GetTimerHigh();
//
//---------------------------------------------------------------------------
uint32_t SysTimer::GetTimerHigh() {
return systaddr[SYST_CHI];
}
// Sleep for N nanoseconds
//---------------------------------------------------------------------------
//
// Sleep in nanoseconds
//
//---------------------------------------------------------------------------
void SysTimer::SleepNsec(uint32_t nsec)
{
systimer_ptr->SleepNsec(nsec);
// If time is 0, don't do anything
if (nsec == 0) {
return;
}
// Sleep for N microseconds
// Calculate the timer difference
uint32_t diff = corefreq * nsec / 1000;
// Return if the difference in time is too small
if (diff == 0) {
return;
}
// Start
uint32_t start = armtaddr[ARMT_FREERUN];
// Loop until timer has elapsed
while ((armtaddr[ARMT_FREERUN] - start) < diff);
}
//---------------------------------------------------------------------------
//
// Sleep in microseconds
//
//---------------------------------------------------------------------------
void SysTimer::SleepUsec(uint32_t usec)
{
systimer_ptr->SleepUsec(usec);
// If time is 0, don't do anything
if (usec == 0) {
return;
}
uint32_t now = GetTimerLow();
while ((GetTimerLow() - now) < usec);
}

View File

@ -13,31 +13,11 @@
#pragma once
#include <memory>
#include <stdint.h>
#include "config.h"
#include "scsi.h"
class PlatformSpecificTimer
{
public:
// Default constructor
PlatformSpecificTimer() = default;
// Default destructor
virtual ~PlatformSpecificTimer() = default;
// Initialization
virtual void Init() = 0;
// Get system timer low byte
virtual uint32_t GetTimerLow() = 0;
// Get system timer high byte
virtual uint32_t GetTimerHigh() = 0;
// Sleep for N nanoseconds
virtual void SleepNsec(uint32_t nsec) = 0;
// Sleep for N microseconds
virtual void SleepUsec(uint32_t usec) = 0;
};
//===========================================================================
//
// System timer
@ -46,20 +26,22 @@ class PlatformSpecificTimer
class SysTimer
{
public:
static void Init();
// Get system timer low byte
static void Init(uint32_t *syst, uint32_t *armt);
// Initialization
static uint32_t GetTimerLow();
// Get system timer high byte
// Get system timer low byte
static uint32_t GetTimerHigh();
// Sleep for N nanoseconds
// Get system timer high byte
static void SleepNsec(uint32_t nsec);
// Sleep for N microseconds
// Sleep for N nanoseconds
static void SleepUsec(uint32_t usec);
// Sleep for N microseconds
private:
static bool initialized;
static bool is_allwinnner;
static bool is_raspberry;
static std::unique_ptr<PlatformSpecificTimer> systimer_ptr;
static volatile uint32_t *systaddr;
// System timer address
static volatile uint32_t *armtaddr;
// ARM timer address
static volatile uint32_t corefreq;
// Core frequency
};

View File

@ -1,155 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
//
// [ High resolution timer for the Allwinner series of SoC's]
//
//---------------------------------------------------------------------------
#include "hal/systimer_allwinner.h"
#include <sys/mman.h>
#include "hal/gpiobus.h"
#include "config.h"
#include "log.h"
const std::string SysTimer_AllWinner::dev_mem_filename = "/dev/mem";
//---------------------------------------------------------------------------
//
// Initialize the system timer
//
//---------------------------------------------------------------------------
void SysTimer_AllWinner::Init()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
int fd;
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
LOGERROR("I can't open /dev/mem. Are you running as root?")
exit(-1);
}
hsitimer_regs = (struct sun8i_hsitimer_registers *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
hs_timer_base_address);
if (hsitimer_regs == MAP_FAILED) {
LOGERROR("Unable to map high speed timer registers. Are you running as root?")
}
sysbus_regs = (struct sun8i_sysbus_registers *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
system_bus_base_address);
if (sysbus_regs == MAP_FAILED) {
LOGERROR("Unable to map system bus registers. Are you running as root?")
}
enable_hs_timer();
}
void SysTimer_AllWinner::enable_hs_timer()
{
// By default, the HSTimer clock gating is masked. When it is necessary to use
// the HSTimer, its clock gating should be opened in BUS Clock Gating Register 0
// and then de-assert the software reset in BUS Software Reset Register 0 on the
// CCU module. If it is not needed to use the HSTimer, both the gating bit and
// the software reset bit should be set 0.
LOGTRACE("%s [Before Enable] CLK GATE: %08X SOFT RST: %08X", __PRETTY_FUNCTION__, sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
sysbus_regs->bus_clk_gating_reg0 = sysbus_regs->bus_clk_gating_reg0 | (1 << BUS_CLK_GATING_REG0_HSTMR);
sysbus_regs->bus_soft_rst_reg0 = sysbus_regs->bus_soft_rst_reg0 | (1 << BUS_SOFT_RST_REG0_HSTMR);
LOGTRACE("%s [After Enable] CLK GATE: %08X SOFT RST: %08X", __PRETTY_FUNCTION__, sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
// Set interval value to the maximum value. (its a 52 bit register)
hsitimer_regs->hs_tmr_intv_hi_reg = (1 << 20) - 1; //(0xFFFFF)
hsitimer_regs->hs_tmr_intv_lo_reg = UINT32_MAX;
// Select prescale value of 1, continuouse mode
hsitimer_regs->hs_tmr_ctrl_reg = HS_TMR_CLK_PRE_SCALE_1;
// Set reload bit
hsitimer_regs->hs_tmr_ctrl_reg |= HS_TMR_RELOAD;
// Enable HSTimer
hsitimer_regs->hs_tmr_ctrl_reg |= HS_TMR_EN;
}
// TODO: According to the data sheet, we should turn off the HS timer when we're done with it. But, its just going to
// eat up a little extra power if we leave it running.
void SysTimer_AllWinner::disable_hs_timer()
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
LOGINFO("[Before Disable] CLK GATE: %08X SOFT RST: %08X", sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
sysbus_regs->bus_clk_gating_reg0 = sysbus_regs->bus_clk_gating_reg0 & ~(1 << BUS_CLK_GATING_REG0_HSTMR);
sysbus_regs->bus_soft_rst_reg0 = sysbus_regs->bus_soft_rst_reg0 & ~(1 << BUS_SOFT_RST_REG0_HSTMR);
LOGINFO("[After Disable] CLK GATE: %08X SOFT RST: %08X", sysbus_regs->bus_clk_gating_reg0,
sysbus_regs->bus_soft_rst_reg0)
}
uint32_t SysTimer_AllWinner::GetTimerLow()
{
// RaSCSI expects the timer to count UP, but the Allwinner HS timer counts
// down. So, we subtract the current timer value from UINT32_MAX
return UINT32_MAX - (hsitimer_regs->hs_tmr_curnt_lo_reg / 200);
}
uint32_t SysTimer_AllWinner::GetTimerHigh()
{
return (uint32_t)0;
}
//---------------------------------------------------------------------------
//
// Sleep in nanoseconds
//
//---------------------------------------------------------------------------
void SysTimer_AllWinner::SleepNsec(uint32_t nsec)
{
// If time is less than one HS timer clock tick, don't do anything
if (nsec < 20) {
return;
}
// The HS timer receives a 200MHz clock input, which equates to
// one clock tick every 5 ns.
auto clockticks = (uint32_t)std::ceil(nsec / 5);
uint32_t enter_time = hsitimer_regs->hs_tmr_curnt_lo_reg;
LOGTRACE("%s entertime: %08X ns: %d clockticks: %d", __PRETTY_FUNCTION__, enter_time, nsec, clockticks)
while ((enter_time - hsitimer_regs->hs_tmr_curnt_lo_reg) < clockticks)
;
return;
}
//---------------------------------------------------------------------------
//
// Sleep in microseconds
//
//---------------------------------------------------------------------------
void SysTimer_AllWinner::SleepUsec(uint32_t usec)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
// If time is 0, don't do anything
if (usec == 0) {
return;
}
uint32_t enter_time = GetTimerLow();
while ((GetTimerLow() - enter_time) < usec)
;
}

View File

@ -1,106 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
//
// [ High resolution timer ]
//
//---------------------------------------------------------------------------
#pragma once
#include "systimer.h"
#include <stdint.h>
#include <string>
//===========================================================================
//
// System timer
//
//===========================================================================
class SysTimer_AllWinner : public PlatformSpecificTimer
{
public:
// Default constructor
SysTimer_AllWinner() = default;
// Default destructor
~SysTimer_AllWinner() override = default;
// Initialization
void Init() override;
// Get system timer low byte
uint32_t GetTimerLow() override;
// Get system timer high byte
uint32_t GetTimerHigh() override;
// Sleep for N nanoseconds
void SleepNsec(uint32_t nsec) override;
// Sleep for N microseconds
void SleepUsec(uint32_t usec) override;
private:
void enable_hs_timer();
void disable_hs_timer();
static const std::string dev_mem_filename;
/* Reference: Allwinner H3 Datasheet, section 4.9.3 */
static const uint32_t hs_timer_base_address = 0x01C60000;
/* Note: Currently the high speed timer is NOT in the armbian
* device tree. If it is ever added, this should be pulled
* from there */
struct sun8i_hsitimer_registers {
/* 0x00 HS Timer IRQ Enabled Register */
uint32_t hs_tmr_irq_en_reg;
/* 0x04 HS Timer Status Register */
uint32_t hs_tmr_irq_stat_reg;
/* 0x08 Unused */
uint32_t unused_08;
/* 0x0C Unused */
uint32_t unused_0C;
/* 0x10 HS Timer Control Register */
uint32_t hs_tmr_ctrl_reg;
/* 0x14 HS Timer Interval Value Low Reg */
uint32_t hs_tmr_intv_lo_reg;
/* 0x18 HS Timer Interval Value High Register */
uint32_t hs_tmr_intv_hi_reg;
/* 0x1C HS Timer Current Value Low Register */
uint32_t hs_tmr_curnt_lo_reg;
/* 0x20 HS Timer Current Value High Register */
uint32_t hs_tmr_curnt_hi_reg;
};
/* Constants for the HS Timer IRQ enable Register (section 4.9.4.1) */
static const uint32_t HS_TMR_INTERUPT_ENB = (1 << 0);
/* Constants for the HS Timer Control Register (section 4.9.4.3) */
static const uint32_t HS_TMR_EN = (1 << 0);
static const uint32_t HS_TMR_RELOAD = (1 << 1);
static const uint32_t HS_TMR_CLK_PRE_SCALE_1 = (0 << 4);
static const uint32_t HS_TMR_CLK_PRE_SCALE_2 = (1 << 4);
static const uint32_t HS_TMR_CLK_PRE_SCALE_4 = (2 << 4);
static const uint32_t HS_TMR_CLK_PRE_SCALE_8 = (3 << 4);
static const uint32_t HS_TMR_CLK_PRE_SCALE_16 = (4 << 4); // NOSONAR This matches the datasheet
static const uint32_t HS_TMR_MODE_SINGLE = (1 << 7);
static const uint32_t HS_TMR_TEST_MODE = (1 << 31);
struct sun8i_hsitimer_registers *hsitimer_regs;
/* Reference: Allwinner H3 Datasheet, section 4.3.4 */
static const uint32_t system_bus_base_address = 0x01C20000;
struct sun8i_sysbus_registers {
uint32_t pad_00_5C[(0x60 / sizeof(uint32_t))]; //NOSONAR c-style array used for padding
/* 0x0060 Bus Clock Gating Register 0 */
uint32_t bus_clk_gating_reg0;
uint32_t pad_64_2C0[((0x2C0 - 0x64) / sizeof(uint32_t))]; //NOSONAR c-style array used for padding
/* 0x2C0 Bus Software Reset Register 0 */
uint32_t bus_soft_rst_reg0;
};
/* Bit associated with the HS Timer */
static const uint32_t BUS_CLK_GATING_REG0_HSTMR = 19;
static const uint32_t BUS_SOFT_RST_REG0_HSTMR = 19;
struct sun8i_sysbus_registers *sysbus_regs;
};

View File

@ -1,148 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2022 akuker
//
// [ High resolution timer ]
//
//---------------------------------------------------------------------------
#include "hal/systimer_raspberry.h"
#include <memory>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "hal/gpiobus.h"
#include "hal/sbc_version.h"
#include "config.h"
#include "log.h"
// System timer address
volatile uint32_t *SysTimer_Raspberry::systaddr = nullptr;
// ARM timer address
volatile uint32_t *SysTimer_Raspberry::armtaddr = nullptr;
volatile uint32_t SysTimer_Raspberry::corefreq = 0;
//---------------------------------------------------------------------------
//
// Initialize the system timer
//
//---------------------------------------------------------------------------
void SysTimer_Raspberry::Init()
{
// Get the base address
auto baseaddr = SBC_Version::GetPeripheralAddress();
// Open /dev/mem
int mem_fd = open("/dev/mem", O_RDWR | O_SYNC);
if (mem_fd == -1) {
LOGERROR("Error: Unable to open /dev/mem. Are you running as root?")
return;
}
// Map peripheral region memory
void *map = mmap(nullptr, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, baseaddr);
if (map == MAP_FAILED) {
LOGERROR("Error: Unable to map memory")
close(mem_fd);
return;
}
close(mem_fd);
// RPI Mailbox property interface
// Get max clock rate
// Tag: 0x00030004
//
// Request: Length: 4
// Value: u32: clock id
// Response: Length: 8
// Value: u32: clock id, u32: rate (in Hz)
//
// Clock id
// 0x000000004: CORE
std::array<uint32_t, 32> maxclock = {32, 0, 0x00030004, 8, 0, 4, 0, 0};
// Save the base address
systaddr = (uint32_t *)map + SYST_OFFSET / sizeof(uint32_t);
armtaddr = (uint32_t *)map + ARMT_OFFSET / sizeof(uint32_t);
// Change the ARM timer to free run mode
armtaddr[ARMT_CTRL] = 0x00000282;
// Get the core frequency
if (int vcio_fd = open("/dev/vcio", O_RDONLY); vcio_fd >= 0) {
ioctl(vcio_fd, _IOWR(100, 0, char *), maxclock.data());
corefreq = maxclock[6] / 1000000;
close(vcio_fd);
}
}
//---------------------------------------------------------------------------
//
// Get system timer low byte
//
//---------------------------------------------------------------------------
uint32_t SysTimer_Raspberry::GetTimerLow()
{
return systaddr[SYST_CLO];
}
//---------------------------------------------------------------------------
//
// Get system timer high byte
//
//---------------------------------------------------------------------------
uint32_t SysTimer_Raspberry::GetTimerHigh()
{
return systaddr[SYST_CHI];
}
//---------------------------------------------------------------------------
//
// Sleep in nanoseconds
//
//---------------------------------------------------------------------------
void SysTimer_Raspberry::SleepNsec(uint32_t nsec)
{
// If time is 0, don't do anything
if (nsec == 0) {
return;
}
// Calculate the timer difference
uint32_t diff = corefreq * nsec / 1000;
// Return if the difference in time is too small
if (diff == 0) {
return;
}
// Start
uint32_t start = armtaddr[ARMT_FREERUN];
// Loop until timer has elapsed
while ((armtaddr[ARMT_FREERUN] - start) < diff)
;
}
//---------------------------------------------------------------------------
//
// Sleep in microseconds
//
//---------------------------------------------------------------------------
void SysTimer_Raspberry::SleepUsec(uint32_t usec)
{
// If time is 0, don't do anything
if (usec == 0) {
return;
}
uint32_t now = GetTimerLow();
while ((GetTimerLow() - now) < usec)
;
}

View File

@ -1,49 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2022 akuker
//
// [ High resolution timer ]
//
//---------------------------------------------------------------------------
#pragma once
#include "systimer.h"
#include <stdint.h>
//===========================================================================
//
// System timer
//
//===========================================================================
class SysTimer_Raspberry : public PlatformSpecificTimer
{
public:
// Default constructor
SysTimer_Raspberry() = default;
// Default destructor
~SysTimer_Raspberry() override = default;
// Initialization
void Init() override;
// Get system timer low byte
uint32_t GetTimerLow() override;
// Get system timer high byte
uint32_t GetTimerHigh() override;
// Sleep for N nanoseconds
void SleepNsec(uint32_t nsec) override;
// Sleep for N microseconds
void SleepUsec(uint32_t usec) override;
private:
// System timer address
static volatile uint32_t *systaddr;
// ARM timer address
static volatile uint32_t *armtaddr;
// Core frequency
static volatile uint32_t corefreq;
};

View File

@ -9,13 +9,12 @@
//---------------------------------------------------------------------------
#include "log.h"
#include "hal/gpiobus.h"
#include "hal/gpiobus_factory.h"
#include "hal/gpiobus.h"
#include "rascsi_version.h"
#include <sys/time.h>
#include <climits>
#include <csignal>
#include <sstream>
#include <iostream>
#include <getopt.h>
#include <sched.h>
@ -25,53 +24,20 @@
using namespace std;
static const int _MAX_FNAME = 256;
//---------------------------------------------------------------------------
//
// Variable declarations
//
//---------------------------------------------------------------------------
static volatile bool running; // Running flag
unique_ptr<GPIOBUS> bus; // GPIO Bus
uint32_t buff_size = 1000000;
data_capture *data_buffer;
uint32_t data_idx = 0;
// TODO Should not be global and not be used by sm_vcd_report
double ns_per_loop;
bool print_help = false;
bool import_data = false;
// We don't really need to support 256 character file names - this causes
// all kinds of compiler warnings when the log filename can be up to 256
// characters. _MAX_FNAME/2 is just an arbitrary value.
char file_base_name[_MAX_FNAME / 2] = "log";
char vcd_file_name[_MAX_FNAME];
char json_file_name[_MAX_FNAME];
char html_file_name[_MAX_FNAME];
char input_file_name[_MAX_FNAME];
//---------------------------------------------------------------------------
//
// Signal Processing
//
//---------------------------------------------------------------------------
void KillHandler(int)
void ScsiMon::KillHandler(int)
{
// Stop instruction
running = false;
}
void ScsiMon::parse_arguments(const vector<char *>& args)
void ScsiMon::ParseArguments(const vector<char *>& args)
{
int opt;
while ((opt = getopt(args.size(), args.data(), "-Hhb:i:")) != -1)
{
switch (opt)
{
while ((opt = getopt(static_cast<int>(args.size()), args.data(), "-Hhb:i:")) != -1) {
switch (opt) {
// The three options below are kind of a compound option with two letters
case 'h':
case 'H':
@ -81,11 +47,11 @@ void ScsiMon::parse_arguments(const vector<char *>& args)
buff_size = atoi(optarg);
break;
case 'i':
strncpy(input_file_name, optarg, sizeof(input_file_name)-1);
input_file_name = optarg;
import_data = true;
break;
case 1:
strncpy(file_base_name, optarg, sizeof(file_base_name) - 5);
file_base_name = optarg;
break;
default:
cout << "default: " << optarg << endl;
@ -96,22 +62,18 @@ void ScsiMon::parse_arguments(const vector<char *>& args)
/* Process any remaining command line arguments (not options). */
if (optind < static_cast<int>(args.size())) {
while (optind < static_cast<int>(args.size()))
strncpy(file_base_name, args[optind++], sizeof(file_base_name)-1);
file_base_name = args[optind++];
}
strcpy(vcd_file_name, file_base_name);
strcat(vcd_file_name, ".vcd");
strcpy(json_file_name, file_base_name);
strcat(json_file_name, ".json");
strcpy(html_file_name, file_base_name);
strcat(html_file_name, ".html");
vcd_file_name = file_base_name;
vcd_file_name += ".vcd";
json_file_name = file_base_name;
json_file_name += ".json";
html_file_name = file_base_name;
html_file_name += ".html";
}
//---------------------------------------------------------------------------
//
// Copyright text
//
//---------------------------------------------------------------------------
void ScsiMon::print_copyright_text()
void ScsiMon::PrintCopyrightText() const
{
LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) ")
LOGINFO("version %s (%s, %s)",
@ -124,12 +86,7 @@ void ScsiMon::print_copyright_text()
LOGINFO(" ")
}
//---------------------------------------------------------------------------
//
// Help text
//
//---------------------------------------------------------------------------
void ScsiMon::print_help_text(const vector<char *>& args)
void ScsiMon::PrintHelpText(const vector<char *>& args) const
{
LOGINFO("%s -i [input file json] -b [buffer size] [output file]", args[0])
LOGINFO(" -i [input file json] - scsimon will parse the json file instead of capturing new data")
@ -139,15 +96,10 @@ void ScsiMon::print_help_text(const vector<char *>& args)
LOGINFO(" will be appended to this file name")
}
//---------------------------------------------------------------------------
//
// Banner Output
//
//---------------------------------------------------------------------------
void ScsiMon::Banner()
void ScsiMon::Banner() const
{
if (import_data) {
LOGINFO("Reading input file: %s", input_file_name)
LOGINFO("Reading input file: %s", input_file_name.c_str())
}
else {
LOGINFO("Reading live data from the GPIO pins")
@ -156,16 +108,11 @@ void ScsiMon::Banner()
LOGINFO(" Data buffer size: %u", buff_size)
LOGINFO(" ")
LOGINFO("Generating output files:")
LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", vcd_file_name)
LOGINFO(" %s - JSON file with raw data", json_file_name)
LOGINFO(" %s - HTML file with summary of commands", html_file_name)
LOGINFO(" %s - Value Change Dump file that can be opened with GTKWave", vcd_file_name.c_str())
LOGINFO(" %s - JSON file with raw data", json_file_name.c_str())
LOGINFO(" %s - HTML file with summary of commands", html_file_name.c_str())
}
//---------------------------------------------------------------------------
//
// Initialization
//
//---------------------------------------------------------------------------
bool ScsiMon::Init()
{
// Interrupt handler settings
@ -179,43 +126,35 @@ bool ScsiMon::Init()
return false;
}
// GPIO Initialization
bus = GPIOBUS_Factory::Create();
if (!bus->Init())
{
bus = GPIOBUS_Factory::Create(BUS::mode_e::TARGET);
if (bus == nullptr) {
LOGERROR("Unable to intiailize the GPIO bus. Exiting....")
return false;
}
// Bus Reset
bus->Reset();
// Other
running = false;
return true;
}
void ScsiMon::Cleanup()
void ScsiMon::Cleanup() const
{
if (!import_data) {
LOGINFO("Stopping data collection....")
}
LOGINFO(" ")
LOGINFO("Generating %s...", vcd_file_name)
scsimon_generate_value_change_dump(vcd_file_name, data_buffer, data_idx);
LOGINFO("Generating %s...", json_file_name)
scsimon_generate_json(json_file_name, data_buffer, data_idx);
LOGINFO("Generating %s...", html_file_name)
scsimon_generate_html(html_file_name, data_buffer, data_idx);
LOGINFO("Generating %s...", vcd_file_name.c_str())
scsimon_generate_value_change_dump(vcd_file_name.c_str(), data_buffer, data_idx);
LOGINFO("Generating %s...", json_file_name.c_str())
scsimon_generate_json(json_file_name.c_str(), data_buffer, data_idx);
LOGINFO("Generating %s...", html_file_name.c_str())
scsimon_generate_html(html_file_name.c_str(), data_buffer, data_idx);
// Cleanup the Bus
bus->Cleanup();
}
void ScsiMon::Reset()
void ScsiMon::Reset() const
{
// Reset the bus
bus->Reset();
}
@ -225,7 +164,7 @@ void ScsiMon::Reset()
//
//---------------------------------------------------------------------------
#ifdef __linux__
void ScsiMon::FixCpu(int cpu)
void ScsiMon::FixCpu(int cpu) const
{
// Get the number of CPUs
cpu_set_t cpuset;
@ -248,14 +187,8 @@ static uint32_t high_bits = 0x0;
static uint32_t low_bits = 0xFFFFFFFF;
#endif
//---------------------------------------------------------------------------
//
// Main processing
//
//---------------------------------------------------------------------------
int ScsiMon::run(const vector<char *>& args)
{
#ifdef DEBUG
spdlog::set_level(spdlog::level::trace);
#else
@ -263,14 +196,13 @@ int ScsiMon::run(const vector<char *>& args)
#endif
spdlog::set_pattern("%^[%l]%$ %v");
print_copyright_text();
parse_arguments(args);
PrintCopyrightText();
ParseArguments(args);
#ifdef DEBUG
uint32_t prev_high = high_bits;
uint32_t prev_low = low_bits;
#endif
ostringstream s;
uint32_t prev_sample = 0xFFFFFFFF;
uint32_t this_sample = 0;
timeval start_time;
@ -279,23 +211,20 @@ int ScsiMon::run(const vector<char *>& args)
timeval time_diff;
uint64_t elapsed_us;
if (print_help)
{
print_help_text(args);
if (print_help) {
PrintHelpText(args);
exit(0);
}
// Output the Banner
Banner();
data_buffer = (data_capture *)calloc(buff_size, sizeof(data_capture_t));
if (import_data)
{
data_idx = scsimon_read_json(input_file_name, data_buffer, buff_size);
if (import_data) {
data_idx = scsimon_read_json(input_file_name.c_str(), data_buffer, buff_size);
if (data_idx > 0)
{
LOGDEBUG("Read %d samples from %s", data_idx, input_file_name)
LOGDEBUG("Read %d samples from %s", data_idx, input_file_name.c_str())
Cleanup();
}
exit(0);
@ -327,48 +256,40 @@ int ScsiMon::run(const vector<char *>& args)
// Start execution
running = true;
bus->SetACT(false);
bus->SetACK(false);
(void)gettimeofday(&start_time, nullptr);
LOGDEBUG("ALL_SCSI_PINS %08X\n", ALL_SCSI_PINS)
// Main Loop
while (running)
{
while (running) {
// Work initialization
this_sample = (bus->Acquire() & ALL_SCSI_PINS);
loop_count++;
if (loop_count > LLONG_MAX - 1)
{
if (loop_count > LLONG_MAX - 1) {
LOGINFO("Maximum amount of time has elapsed. SCSIMON is terminating.")
running = false;
}
if (data_idx >= (buff_size - 2))
{
if (data_idx >= (buff_size - 2)) {
LOGINFO("Internal data buffer is full. SCSIMON is terminating.")
running = false;
}
if (this_sample != prev_sample)
{
if (this_sample != prev_sample) {
#ifdef DEBUG
// This is intended to be a debug check to see if every pin is set
// high and low at some point.
high_bits |= this_sample;
low_bits &= this_sample;
if ((high_bits != prev_high) || (low_bits != prev_low))
{
if ((high_bits != prev_high) || (low_bits != prev_low)) {
LOGDEBUG(" %08X %08X\n", high_bits, low_bits)
}
prev_high = high_bits;
prev_low = low_bits;
if ((data_idx % 1000) == 0)
{
s.str("");
s << "Collected " << data_idx << " samples...";
LOGDEBUG("%s", s.str().c_str())
if ((data_idx % 1000) == 0) {
LOGDEBUG("%s", ("Collected " + to_string(data_idx) + " samples...").c_str())
}
#endif
data_buffer[data_idx].data = this_sample;
@ -381,8 +302,7 @@ int ScsiMon::run(const vector<char *>& args)
}
// Collect one last sample, otherwise it looks like the end of the data was cut off
if (data_idx < buff_size)
{
if (data_idx < buff_size) {
data_buffer[data_idx].data = this_sample;
data_buffer[data_idx].timestamp = loop_count;
data_idx++;
@ -393,18 +313,13 @@ int ScsiMon::run(const vector<char *>& args)
timersub(&stop_time, &start_time, &time_diff);
elapsed_us = ((time_diff.tv_sec * 1000000) + time_diff.tv_usec);
s.str("");
s << "Elapsed time: " << elapsed_us << " microseconds (" << elapsed_us / 1000000 << " seconds)";
LOGINFO("%s", s.str().c_str())
s.str("");
s << "Collected " << data_idx << " changes";
LOGINFO("%s", s.str().c_str())
LOGINFO("%s", ("Elapsed time: " + to_string(elapsed_us) + " microseconds (" + to_string(elapsed_us / 1000000)
+ " seconds").c_str())
LOGINFO("%s", ("Collected " + to_string(data_idx) + " changes").c_str())
// Note: ns_per_loop is a global variable that is used by Cleanup() to printout the timestamps.
ns_per_loop = (elapsed_us * 1000) / (double)loop_count;
s.str("");
s << "Read the SCSI bus " << loop_count << " times with an average of " << ns_per_loop << " ns for each read";
LOGINFO("%s", s.str().c_str())
ns_per_loop = (double)(elapsed_us * 1000) / (double)loop_count;
LOGINFO("%s", ("Read the SCSI bus " + to_string(loop_count) + " times with an average of "
+ to_string(ns_per_loop) + " ns for each read").c_str())
Cleanup();

View File

@ -9,10 +9,14 @@
#pragma once
#include "hal/bus.h"
#include "monitor/data_sample.h"
#include <vector>
#include <memory>
using namespace std;
// TODO Make static fields/methods non-static
class ScsiMon
{
public:
@ -24,12 +28,35 @@ public:
private:
void parse_arguments(const vector<char *>&);
void print_copyright_text();
void print_help_text(const vector<char *>&);
void Banner();
void ParseArguments(const vector<char *>&);
void PrintCopyrightText() const;
void PrintHelpText(const vector<char *>&) const;
void Banner() const;
bool Init();
void Cleanup();
void Reset();
void FixCpu(int);
void Cleanup() const;
void Reset() const;
void FixCpu(int) const;
static void KillHandler(int);
static inline volatile bool running;
unique_ptr<BUS> bus;
uint32_t buff_size = 1000000;
data_capture *data_buffer = nullptr;
uint32_t data_idx = 0;
bool print_help = false;
bool import_data = false;
string file_base_name = "log";
string vcd_file_name;
string json_file_name;
string html_file_name;
string input_file_name;
};

View File

@ -43,7 +43,7 @@ uint32_t scsimon_read_json(const char *json_filename, data_capture *data_capture
continue;
strncpy(data, &data_str[strlen(data_label)], 8);
data[8] = '\0';
data_uint = strtoul(data, &ptr, 16);
data_uint = static_cast<uint32_t>(strtoul(data, &ptr, 16));
data_capture_array[sample_count].timestamp = timestamp_uint;
data_capture_array[sample_count].data = data_uint;

View File

@ -75,8 +75,8 @@ static uint8_t get_data_field(uint32_t data)
static void vcd_output_if_changed_phase(ofstream& fp, uint32_t data, int pin, char symbol)
{
const BUS::phase_t new_value = GPIOBUS::GetPhaseRaw(data);
if (prev_value[pin] != static_cast<int>(new_value)) {
prev_value[pin] = static_cast<int>(new_value);
if (prev_value[pin] != static_cast<uint8_t>(new_value)) {
prev_value[pin] = static_cast<uint8_t>(new_value);
fp << "s" << GPIOBUS::GetPhaseStrRaw(new_value) << " " << symbol << endl;
}
}
@ -164,7 +164,7 @@ void scsimon_generate_value_change_dump(const char *filename, const data_capture
uint32_t i = 0;
while (i < capture_count) {
vcd_ofstream << "#" << (uint64_t)(data_capture_array[i].timestamp * ns_per_loop) << endl;
vcd_ofstream << "#" << (uint64_t)((double)data_capture_array[i].timestamp * ns_per_loop) << endl;
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_SEL, SYMBOL_PIN_SEL);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_CD, SYMBOL_PIN_CD);

View File

@ -16,9 +16,8 @@
#include "controllers/scsi_controller.h"
#include "devices/device_factory.h"
#include "devices/storage_device.h"
#include "hal/gpiobus.h"
#include "hal/gpiobus_factory.h"
#include "hal/sbc_version.h"
#include "hal/gpiobus.h"
#include "hal/systimer.h"
#include "rascsi_version.h"
#include "protobuf_serializer.h"
@ -68,20 +67,11 @@ void Rascsi::Banner(const vector<char *>& args) const
bool Rascsi::InitBus() const
{
#ifdef USE_SEL_EVENT_ENABLE
SBC_Version::Init();
#endif
// GPIOBUS creation
bus = GPIOBUS_Factory::Create();
// GPIO Initialization
if (!bus->Init()) {
bus = GPIOBUS_Factory::Create(BUS::mode_e::TARGET);
if (bus == nullptr) {
return false;
}
bus->Reset();
auto b = bus;
controller_manager = make_shared<ControllerManager>(*b);
auto c = controller_manager;

View File

@ -19,8 +19,7 @@
using namespace std;
// TODO Only BUS should be known
class GPIOBUS;
class BUS;
class ControllerManager;
class RascsiExecutor;
@ -61,7 +60,7 @@ private:
const static inline RascsiResponse rascsi_response;
static inline shared_ptr<GPIOBUS> bus;
static inline shared_ptr<BUS> bus;
static inline shared_ptr<ControllerManager> controller_manager;

File diff suppressed because it is too large Load Diff

View File

@ -9,13 +9,27 @@
#pragma once
#include "scsi.h"
#include "hal/bus.h"
#include <string>
#include <vector>
#include <memory>
#include <stdexcept>
using namespace std;
class rasdump_exception : public runtime_error
{
using runtime_error::runtime_error;
};
class RasDump
{
static const char COMPONENT_SEPARATOR = ':';
static const int MINIMUM_BUFFER_SIZE = 1024 * 64;
static const int DEFAULT_BUFFER_SIZE = 1024 * 1024;
public:
RasDump() = default;
@ -25,23 +39,47 @@ public:
private:
bool Banner(const vector<char *>&);
bool Init();
void Reset();
bool ParseArguments(const vector<char *>&);
bool WaitPhase(BUS::phase_t);
void BusFree();
bool Selection(int);
bool Command(uint8_t *, int);
int DataIn(uint8_t *, int);
int DataOut(uint8_t *, int);
int Status();
int MessageIn();
int TestUnitReady(int);
int RequestSense(int, uint8_t *);
int ModeSense(int, uint8_t *);
int Inquiry(int, uint8_t *);
int ReadCapacity(int, uint8_t *);
int Read10(int, uint32_t, uint32_t, uint32_t, uint8_t *);
int Write10(int, uint32_t, uint32_t, uint32_t, uint8_t *);
bool Banner(const vector<char *>&) const;
bool Init() const;
void ParseArguments(const vector<char *>&);
int DumpRestore();
void WaitPhase(BUS::phase_t) const;
void Selection() const;
void Command(scsi_defs::scsi_command, vector<uint8_t>&) const;
void DataIn(int);
void DataOut(int);
void Status() const;
void MessageIn() const;
void MessageOut();
void BusFree() const;
void TestUnitReady() const;
void RequestSense();
void Inquiry();
pair<uint64_t, uint32_t> ReadCapacity();
void Read10(uint32_t, uint32_t, uint32_t);
void Write10(uint32_t, uint32_t, uint32_t);
void WaitForBusy() const;
// TODO Use ras_util after removing its dependencies on protobuf interface.
// Not required in case rasdump is integrated into rascsi.
static void ProcessId(const string&, int&, int&);
static bool GetAsInt(const string&, int&);
static void CleanUp();
static void KillHandler(int);
// A static instance is needed because of the signal handler
static inline unique_ptr<BUS> bus;
vector<uint8_t> buffer;
int target_id = -1;
int target_lun = 0;
int initiator_id = 7;
string filename;
bool restore = false;
};

View File

@ -1,103 +0,0 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2010-2020 GIMONS
// [ File I/O (Subset for RaSCSI) ]
//
//---------------------------------------------------------------------------
#include "rasdump/rasdump_fileio.h"
#include <fcntl.h>
#include <unistd.h>
#include <cassert>
Fileio::~Fileio()
{
// Safety measure for Release
Close();
}
bool Fileio::Open(const char *fname, OpenMode mode, bool directIO)
{
assert(fname);
assert(handle < 0);
// Always fail a read from a null array
if (fname[0] == '\0') {
handle = -1;
return false;
}
// Default mode
const mode_t omode = directIO ? O_DIRECT : 0;
switch (mode) {
case OpenMode::ReadOnly:
handle = open(fname, O_RDONLY | omode);
break;
case OpenMode::WriteOnly:
handle = open(fname, O_CREAT | O_WRONLY | O_TRUNC | omode, 0666);
break;
case OpenMode::ReadWrite:
handle = open(fname, O_RDWR | omode);
break;
default:
assert(false);
break;
}
// Evaluate results
return handle != -1;
}
bool Fileio::Open(const char *fname, OpenMode mode)
{
return Open(fname, mode, false);
}
bool Fileio::Read(uint8_t *buffer, int size) const
{
assert(buffer);
assert(size > 0);
assert(handle >= 0);
return read(handle, buffer, size) == size;
}
bool Fileio::Write(const uint8_t *buffer, int size) const
{
assert(buffer);
assert(size > 0);
assert(handle >= 0);
return write(handle, buffer, size) == size;
}
off_t Fileio::GetFileSize() const
{
assert(handle >= 0);
// Get file position in 64bit
const off_t cur = lseek(handle, 0, SEEK_CUR);
// Get file size in64bitで
const off_t end = lseek(handle, 0, SEEK_END);
// Return to start position
lseek(handle, cur, SEEK_SET);
return end;
}
void Fileio::Close()
{
if (handle != -1) {
close(handle);
handle = -1;
}
}

View File

@ -1,42 +0,0 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2005 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2013-2020 GIMONS
// [ File I/O (Subset for RaSCSI) ]
//
//---------------------------------------------------------------------------
#pragma once
#include <cstdint>
#include <cstdlib>
class Fileio
{
public:
enum class OpenMode {
ReadOnly,
WriteOnly,
ReadWrite
};
Fileio() = default;
virtual ~Fileio();
Fileio(Fileio&) = default;
Fileio& operator=(const Fileio&) = default;
bool Open(const char *fname, OpenMode mode);
bool Read(uint8_t *buffer, int size) const;
bool Write(const uint8_t *buffer, int size) const;
off_t GetFileSize() const;
void Close();
private:
bool Open(const char *fname, OpenMode mode, bool directIO);
int handle = -1;
};

View File

@ -3,10 +3,12 @@
rasdump \- SCSI disk dumping tool for RaSCSI
.SH SYNOPSIS
.B rasdump
\fB\-i\fR \fIID\fR
[\fB\-b\fR \fIBID\fR]
\fB\-t\fR \fIID[:LUN]\fR
[\fB\-i\fR \fIBID\fR]
\fB\-f\fR \fIFILE\fR
[\fB\-s\fR \fIBUFFER_SIZE\fR]
[\fB\-r\fR]
[\fB\-v\fR]
.SH DESCRIPTION
.B rasdump
samples the data on physical SCSI storage media, including hard drives and MO drives, and stores it to an image file. It can also restore from a dumped file onto physical SCSI storage media. Can be connected directly, through a STANDARD RaSCSI board, or a FULLSPEC RaSCSI board.
@ -15,21 +17,25 @@ Set its own ID with the BID option. Defaults to 7 if ommitted.
.SH OPTIONS
.TP
.BR \-i\fI " "\fIID
SCSI ID of the target device
.BR \-t\fI " "\fIID[:LUN]
SCSI ID and optional LUN of the target device.
.TP
.BR \-b\fI " "\fIBID
SCSI ID of the RaSCSI device
.BR \-i\fI " "\fIBID
SCSI ID of the RaSCSI device.
.TP
.BR \-f\fI " "\fIFILE
Path to the dump file
Path to the dump file.
.TP
.BR \-s\fI " "\fIBUFFER_SIZE
The transfer buffer size, at least 64 KiB. Default is 64 KiB.
.TP
.BR \-r\fI
Restoration mode
.SH EXAMPLES
Run in restore mode.
.TP
.BR \-v\fI
Enable verbose logging.
.SH SEE ALSO
rasctl(1), rascsi(1), scsimon(1), sasidump(1)
rasctl(1), rascsi(1), scsimon(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>

42
doc/rasdump_man_page.txt Normal file
View File

@ -0,0 +1,42 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
!! ------ The native file is rasdump.1. Re-run 'make docs' after updating\n\n
rasdump(1) General Commands Manual rasdump(1)
NAME
rasdump - SCSI disk dumping tool for RaSCSI
SYNOPSIS
rasdump -t ID[:LUN] [-i BID] -f FILE [-s BUFFER_SIZE] [-r] [-v]
DESCRIPTION
rasdump samples the data on physical SCSI storage media, including hard
drives and MO drives, and stores it to an image file. It can also re
store from a dumped file onto physical SCSI storage media. Can be con
nected directly, through a STANDARD RaSCSI board, or a FULLSPEC RaSCSI
board.
Set its own ID with the BID option. Defaults to 7 if ommitted.
OPTIONS
-t ID[:LUN]
SCSI ID and optional LUN of the target device.
-i BID SCSI ID of the RaSCSI device.
-f FILE
Path to the dump file.
-s BUFFER_SIZE
The transfer buffer size, at least 64 KiB. Default is 64 KiB.
-r Run in restore mode.
-v Enable verbose logging.
SEE ALSO
rasctl(1), rascsi(1), scsimon(1)
Full documentation is available at:
<https://www.github.com/akuker/RASCSI/wiki/>
rasdump(1)