//--------------------------------------------------------------------------- // // SCSI Target Emulator RaSCSI (*^..^*) // for Raspberry Pi // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS // // [ GPIO-SCSI bus ] // //--------------------------------------------------------------------------- #include #include "os.h" #include "gpiobus.h" #include "config.h" #include "log.h" #ifdef __linux__ //--------------------------------------------------------------------------- // // imported from bcm_host.c // //--------------------------------------------------------------------------- static DWORD get_dt_ranges(const char *filename, DWORD offset) { DWORD address = ~0; FILE *fp = fopen(filename, "rb"); if (fp) { fseek(fp, offset, SEEK_SET); BYTE buf[4]; if (fread(buf, 1, sizeof buf, fp) == sizeof buf) { address = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0; } fclose(fp); } return address; } DWORD bcm_host_get_peripheral_address(void) { DWORD 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 == (DWORD)~0) ? 0x20000000 : address; #if 0 printf("Peripheral address : 0x%lx\n", address); #endif return address; } #endif // __linux__ #ifdef __NetBSD__ // Assume the Raspberry Pi series and estimate the address from CPU DWORD bcm_host_get_peripheral_address(void) { char buf[1024]; size_t len = sizeof(buf); DWORD 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; } printf("Peripheral address : 0x%lx\n", address); return address; } #endif // __NetBSD__ GPIOBUS::GPIOBUS() { actmode = TARGET; baseaddr = 0; gicc = 0; gicd = 0; gpio = 0; level = 0; pads = 0; irpctl = 0; qa7regs = 0; signals = 0; rpitype = 0; } GPIOBUS::~GPIOBUS() { } BOOL GPIOBUS::Init(mode_e 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 void *map; int i; int j; int pullmode; int fd; #ifdef USE_SEL_EVENT_ENABLE struct epoll_event ev; #endif // USE_SEL_EVENT_ENABLE // Save operation mode actmode = mode; // Get the base address baseaddr = (DWORD)bcm_host_get_peripheral_address(); // Open /dev/mem 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 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 = (DWORD *)map; gpio += GPIO_OFFSET / sizeof(DWORD); level = &gpio[GPIO_LEV_0]; // PADS pads = (DWORD *)map; pads += PADS_OFFSET / sizeof(DWORD); // System timer SysTimer::Init( (DWORD *)map + SYST_OFFSET / sizeof(DWORD), (DWORD *)map + ARMT_OFFSET / sizeof(DWORD)); // Interrupt controller irpctl = (DWORD *)map; irpctl += IRPT_OFFSET / sizeof(DWORD); // Quad-A7 control qa7regs = (DWORD *)map; qa7regs += QA7_OFFSET / sizeof(DWORD); // 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 = (DWORD *)map; gicc = (DWORD *)map; gicc += (ARM_GICC_BASE - ARM_GICD_BASE) / sizeof(DWORD); } else { gicd = NULL; gicc = NULL; } close(fd); // Set Drive Strength to 16mA DrvConfig(7); // Set pull up/pull down #if SIGNAL_CONTROL_MODE == 0 pullmode = GPIO_PULLNONE; #elif SIGNAL_CONTROL_MODE == 1 pullmode = GPIO_PULLUP; #else pullmode = GPIO_PULLDOWN; #endif // Initialize all signals for (i = 0; SignalTable[i] >= 0; i++) { j = SignalTable[i]; PinSetSignal(j, FALSE); PinConfig(j, GPIO_INPUT); PullConfig(j, pullmode); } // Set control signals PinSetSignal(PIN_ACT, FALSE); PinSetSignal(PIN_TAD, FALSE); PinSetSignal(PIN_IND, FALSE); PinSetSignal(PIN_DTD, FALSE); 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); 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 // Show the user that this app is running SetControl(PIN_ENB, ENB_ON); return TRUE; #endif // ifdef __x86_64__ || __X86__ } void GPIOBUS::Cleanup() { #if defined(__x86_64__) || defined(__X86__) return; #else int i; int pin; // Release SEL signal interrupt #ifdef USE_SEL_EVENT_ENABLE close(selevreq.fd); #endif // USE_SEL_EVENT_ENABLE // Set control signals PinSetSignal(PIN_ENB, FALSE); PinSetSignal(PIN_ACT, FALSE); PinSetSignal(PIN_TAD, FALSE); PinSetSignal(PIN_IND, FALSE); PinSetSignal(PIN_DTD, FALSE); PinConfig(PIN_ACT, GPIO_INPUT); PinConfig(PIN_TAD, GPIO_INPUT); PinConfig(PIN_IND, GPIO_INPUT); PinConfig(PIN_DTD, GPIO_INPUT); // Initialize all signals for (i = 0; SignalTable[i] >= 0; i++) { pin = SignalTable[i]; PinSetSignal(pin, FALSE); PinConfig(pin, GPIO_INPUT); PullConfig(pin, GPIO_PULLNONE); } // Set drive strength back to 8mA DrvConfig(3); #endif // ifdef __x86_64__ || __X86__ } void GPIOBUS::Reset() { #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 == 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__ } //--------------------------------------------------------------------------- // // ENB signal setting // //--------------------------------------------------------------------------- void GPIOBUS::SetENB(BOOL ast) { PinSetSignal(PIN_ENB, ast ? ENB_ON : ENB_OFF); } //--------------------------------------------------------------------------- // // Get BSY signal // //--------------------------------------------------------------------------- bool GPIOBUS::GetBSY() { return GetSignal(PIN_BSY); } //--------------------------------------------------------------------------- // // Set BSY signal // //--------------------------------------------------------------------------- void GPIOBUS::SetBSY(bool ast) { // Set BSY signal SetSignal(PIN_BSY, ast); if (actmode == TARGET) { if (ast) { // Turn on ACTIVE signal SetControl(PIN_ACT, ACT_ON); // Set Target signal to output SetControl(PIN_TAD, TAD_OUT); SetMode(PIN_BSY, OUT); SetMode(PIN_MSG, OUT); SetMode(PIN_CD, OUT); SetMode(PIN_REQ, OUT); SetMode(PIN_IO, OUT); } else { // Turn off the ACTIVE signal SetControl(PIN_ACT, ACT_OFF); // Set the 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); } } } //--------------------------------------------------------------------------- // // Get SEL signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetSEL() { return GetSignal(PIN_SEL); } //--------------------------------------------------------------------------- // // Set SEL signal // //--------------------------------------------------------------------------- void GPIOBUS::SetSEL(BOOL ast) { if (actmode == INITIATOR && ast) { // Turn on ACTIVE signal SetControl(PIN_ACT, ACT_ON); } // Set SEL signal SetSignal(PIN_SEL, ast); } //--------------------------------------------------------------------------- // // Get ATN signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetATN() { return GetSignal(PIN_ATN); } //--------------------------------------------------------------------------- // // Get ATN signal // //--------------------------------------------------------------------------- void GPIOBUS::SetATN(BOOL ast) { SetSignal(PIN_ATN, ast); } //--------------------------------------------------------------------------- // // Get ACK signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetACK() { return GetSignal(PIN_ACK); } //--------------------------------------------------------------------------- // // Set ACK signal // //--------------------------------------------------------------------------- void GPIOBUS::SetACK(BOOL ast) { SetSignal(PIN_ACK, ast); } //--------------------------------------------------------------------------- // // Get ACK signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetACT() { return GetSignal(PIN_ACT); } //--------------------------------------------------------------------------- // // Set ACK signal // //--------------------------------------------------------------------------- void GPIOBUS::SetACT(BOOL ast) { SetSignal(PIN_ACT, ast); } //--------------------------------------------------------------------------- // // Get RST signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetRST() { return GetSignal(PIN_RST); } //--------------------------------------------------------------------------- // // Set RST signal // //--------------------------------------------------------------------------- void GPIOBUS::SetRST(BOOL ast) { SetSignal(PIN_RST, ast); } //--------------------------------------------------------------------------- // // Get MSG signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetMSG() { return GetSignal(PIN_MSG); } //--------------------------------------------------------------------------- // // Set MSG signal // //--------------------------------------------------------------------------- void GPIOBUS::SetMSG(BOOL ast) { SetSignal(PIN_MSG, ast); } //--------------------------------------------------------------------------- // // Get CD signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetCD() { return GetSignal(PIN_CD); } //--------------------------------------------------------------------------- // // Set CD Signal // //--------------------------------------------------------------------------- void GPIOBUS::SetCD(BOOL ast) { SetSignal(PIN_CD, ast); } //--------------------------------------------------------------------------- // // Get IO Signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetIO() { BOOL ast = GetSignal(PIN_IO); if (actmode == INITIATOR) { // Change the data input/output direction by IO signal if (ast) { 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 { 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); } } return ast; } //--------------------------------------------------------------------------- // // Set IO signal // //--------------------------------------------------------------------------- void GPIOBUS::SetIO(BOOL ast) { SetSignal(PIN_IO, ast); if (actmode == TARGET) { // Change the data input/output direction by IO signal if (ast) { SetControl(PIN_DTD, DTD_OUT); SetDAT(0); 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); } else { 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); } } } //--------------------------------------------------------------------------- // // Get REQ signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetREQ() { return GetSignal(PIN_REQ); } //--------------------------------------------------------------------------- // // Set REQ signal // //--------------------------------------------------------------------------- void GPIOBUS::SetREQ(BOOL ast) { SetSignal(PIN_REQ, ast); } //--------------------------------------------------------------------------- // // Get data signals // //--------------------------------------------------------------------------- BYTE GPIOBUS::GetDAT() { DWORD data = Aquire(); 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 (BYTE)data; } //--------------------------------------------------------------------------- // // Set data signals // //--------------------------------------------------------------------------- void GPIOBUS::SetDAT(BYTE dat) { // Write to port #if SIGNAL_CONTROL_MODE == 0 DWORD 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 } //--------------------------------------------------------------------------- // // Get data parity signal // //--------------------------------------------------------------------------- BOOL GPIOBUS::GetDP() { return GetSignal(PIN_DP); } //--------------------------------------------------------------------------- // // Receive command handshake // //--------------------------------------------------------------------------- int GPIOBUS::CommandHandShake(BYTE *buf) { int count; // Only works in TARGET mode if (actmode != TARGET) { return 0; } // IRQs disabled DisableIRQ(); // Get the first command byte int i = 0; // Assert REQ signal SetSignal(PIN_REQ, ON); // Wait for ACK signal BOOL ret = WaitSignal(PIN_ACK, TRUE); // Wait until the signal line stabilizes SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); // Get data *buf = GetDAT(); // Disable REQ signal SetSignal(PIN_REQ, OFF); // Timeout waiting for ACK assertion if (!ret) { goto irq_enable_exit; } // Wait for ACK to clear ret = WaitSignal(PIN_ACK, FALSE); // Timeout waiting for ACK to clear if (!ret) { goto irq_enable_exit; } // The ICD AdSCSI ST, AdSCSI Plus ST and AdSCSI Micro ST host adapters allow SCSI devices to be connected // to the ACSI bus of Atari ST/TT computers and some clones. ICD-aware drivers prepend a $1F byte in front // of the CDB (effectively resulting in a custom SCSI command) in order to get access to the full SCSI // command set. Native ACSI is limited to the low SCSI command classes with command bytes < $20. // Most other host adapters (e.g. LINK96/97 and the one by Inventronik) and also several devices (e.g. // UltraSatan or GigaFile) that can directly be connected to the Atari's ACSI port also support ICD // semantics. I fact, these semantics have become a standard in the Atari world. // RaSCSI becomes ICD compatible by ignoring the prepended $1F byte before processing the CDB. if (*buf == 0x1F) { SetSignal(PIN_REQ, ON); ret = WaitSignal(PIN_ACK, TRUE); SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); // Get the actual SCSI command *buf = GetDAT(); SetSignal(PIN_REQ, OFF); if (!ret) { goto irq_enable_exit; } WaitSignal(PIN_ACK, FALSE); if (!ret) { goto irq_enable_exit; } } count = GetCommandByteCount(*buf); // Increment buffer pointer buf++; for (i = 1; i < count; i++) { // Assert REQ signal SetSignal(PIN_REQ, ON); // Wait for ACK signal ret = WaitSignal(PIN_ACK, TRUE); // Wait until the signal line stabilizes SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); // Get data *buf = GetDAT(); // Clear the REQ signal SetSignal(PIN_REQ, OFF); // Check for timeout waiting for ACK assertion if (!ret) { break; } // Wait for ACK to clear ret = WaitSignal(PIN_ACK, FALSE); // Check for timeout waiting for ACK to clear if (!ret) { break; } // Advance the buffer pointer to receive the next byte buf++; } irq_enable_exit: // IRQs enabled EnableIRQ(); // returned the number of bytes received return i; } //--------------------------------------------------------------------------- // // Data reception handshake // //--------------------------------------------------------------------------- int GPIOBUS::ReceiveHandShake(BYTE *buf, int count) { int i; BOOL ret; DWORD phase; // Disable IRQs DisableIRQ(); if (actmode == TARGET) { for (i = 0; i < count; i++) { // Assert the REQ signal SetSignal(PIN_REQ, ON); // Wait for ACK ret = WaitSignal(PIN_ACK, TRUE); // Wait until the signal line stabilizes SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); // Get data *buf = GetDAT(); // Clear the REQ signal SetSignal(PIN_REQ, OFF); // Check for timeout waiting for ACK signal if (!ret) { break; } // Wait for ACK to clear ret = WaitSignal(PIN_ACK, FALSE); // Check for timeout waiting for ACK to clear if (!ret) { break; } // Advance the buffer pointer to receive the next byte buf++; } } else { // Get phase phase = Aquire() & GPIO_MCI; for (i = 0; i < count; i++) { // Wait for the REQ signal to be asserted ret = WaitSignal(PIN_REQ, TRUE); // Check for timeout waiting for REQ signal if (!ret) { break; } // Phase error if ((signals & GPIO_MCI) != phase) { break; } // Wait until the signal line stabilizes SysTimer::SleepNsec(SCSI_DELAY_BUS_SETTLE_DELAY_NS); // Get data *buf = GetDAT(); // Assert the ACK signal SetSignal(PIN_ACK, ON); // Wait for REQ to clear ret = WaitSignal(PIN_REQ, FALSE); // Clear the ACK signal SetSignal(PIN_ACK, OFF); // Check for timeout waiting for REQ to clear if (!ret) { break; } // Phase error if ((signals & GPIO_MCI) != phase) { break; } // Advance the buffer pointer to receive the next byte buf++; } } // Re-enable IRQ EnableIRQ(); // Return the number of bytes received return i; } //--------------------------------------------------------------------------- // // Data transmission handshake // //--------------------------------------------------------------------------- int GPIOBUS::SendHandShake(BYTE *buf, int count, int delay_after_bytes) { int i; // Disable IRQs DisableIRQ(); if (actmode == 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); SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US); } // Set the DATA signals SetDAT(*buf); // Wait for ACK to clear BOOL ret = WaitSignal(PIN_ACK, FALSE); // Check for timeout waiting for ACK to clear if (!ret) { break; } // Already waiting for ACK to clear // Assert the REQ signal SetSignal(PIN_REQ, ON); // Wait for ACK ret = WaitSignal(PIN_ACK, TRUE); // Clear REQ signal SetSignal(PIN_REQ, OFF); // Check for timeout waiting for ACK to clear if (!ret) { break; } // Advance the data buffer pointer to receive the next byte buf++; } // Wait for ACK to clear WaitSignal(PIN_ACK, FALSE); } else { // Get Phase DWORD phase = Aquire() & GPIO_MCI; 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); SysTimer::SleepUsec(SCSI_DELAY_SEND_DATA_DAYNAPORT_US); } // Set the DATA signals SetDAT(*buf); // Wait for REQ to be asserted BOOL ret = WaitSignal(PIN_REQ, TRUE); // Check for timeout waiting for REQ to be asserted if (!ret) { break; } // Phase error if ((signals & GPIO_MCI) != phase) { break; } // Already waiting for REQ assertion // Assert the ACK signal SetSignal(PIN_ACK, ON); // Wait for REQ to clear ret = WaitSignal(PIN_REQ, FALSE); // Clear the ACK signal SetSignal(PIN_ACK, OFF); // Check for timeout waiting for REQ to clear if (!ret) { break; } // Phase error if ((signals & GPIO_MCI) != phase) { break; } // Advance the data buffer pointer to receive the next byte buf++; } } // Re-enable IRQ EnableIRQ(); // Return number of transmissions return i; } #ifdef USE_SEL_EVENT_ENABLE //--------------------------------------------------------------------------- // // SEL signal event polling // //--------------------------------------------------------------------------- int GPIOBUS::PollSelectEvent() { // clear errno errno = 0; struct epoll_event epev; struct gpioevent_data gpev; if (epoll_wait(epfd, &epev, 1, -1) <= 0) { LOGWARN("%s epoll_wait failed", __PRETTY_FUNCTION__); return -1; } if (read(selevreq.fd, &gpev, sizeof(gpev)) < 0) { LOGWARN("%s read failed", __PRETTY_FUNCTION__); return -1; } return 0; } //--------------------------------------------------------------------------- // // Cancel SEL signal event // //--------------------------------------------------------------------------- void GPIOBUS::ClearSelectEvent() { } #endif // USE_SEL_EVENT_ENABLE //--------------------------------------------------------------------------- // // Signal table // //--------------------------------------------------------------------------- const int GPIOBUS::SignalTable[19] = { 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 }; //--------------------------------------------------------------------------- // // Create work table // //--------------------------------------------------------------------------- void GPIOBUS::MakeTable(void) { const int pintbl[] = { PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP }; int i; int j; BOOL tblParity[256]; #if SIGNAL_CONTROL_MODE == 0 int index; int shift; #else DWORD gpclr; DWORD gpset; #endif // Create parity table for (i = 0; i < 0x100; i++) { DWORD bits = (DWORD)i; DWORD parity = 0; for (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 memset(tblDatMsk, 0xff, sizeof(tblDatMsk)); memset(tblDatSet, 0x00, sizeof(tblDatSet)); for (i = 0; i < 0x100; i++) { // Bit string for inspection DWORD bits = (DWORD)i; // Get parity if (tblParity[i]) { bits |= (1 << 8); } // Bit check for (j = 0; j < 9; j++) { // Index and shift amount calculation index = pintbl[j] / 10; 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 // Mask and setting data generation memset(tblDatMsk, 0x00, sizeof(tblDatMsk)); memset(tblDatSet, 0x00, sizeof(tblDatSet)); for (i = 0; i < 0x100; i++) { // bit string for inspection DWORD bits = (DWORD)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 gpclr = 0; gpset = 0; for (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::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; DWORD 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) { 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; DWORD 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 // //--------------------------------------------------------------------------- BOOL GPIOBUS::WaitSignal(int pin, BOOL ast) { // Get current time DWORD now = SysTimer::GetTimerLow(); // Calculate timeout (3000ms) DWORD timeout = 3000 * 1000; // end immediately if the signal has changed do { // Immediately upon receiving a reset Aquire(); 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; } //--------------------------------------------------------------------------- // // Disable IRQ // //--------------------------------------------------------------------------- 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 } //--------------------------------------------------------------------------- // // Enable IRQ // //--------------------------------------------------------------------------- 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; DWORD 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) { DWORD 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; DWORD 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(DWORD drive) { DWORD data = pads[PAD_0_27]; pads[PAD_0_27] = (0xFFFFFFF8 & data) | drive | 0x5a000000; } //--------------------------------------------------------------------------- // // Generic Phase Acquisition (Doesn't read GPIO) // //--------------------------------------------------------------------------- BUS::phase_t GPIOBUS::GetPhaseRaw(DWORD raw_data) { // Selection Phase if (GetPinRaw(raw_data, PIN_SEL)) { if(GetPinRaw(raw_data, PIN_IO)){ return BUS::reselection; }else{ return BUS::selection; } } // Bus busy phase if (!GetPinRaw(raw_data, PIN_BSY)) { return BUS::busfree; } // Get target phase from bus signal line DWORD mci = GetPinRaw(raw_data, PIN_MSG) ? 0x04 : 0x00; mci |= GetPinRaw(raw_data, PIN_CD) ? 0x02 : 0x00; mci |= GetPinRaw(raw_data, PIN_IO) ? 0x01 : 0x00; return GetPhase(mci); } //--------------------------------------------------------------------------- // // Get the number of bytes for a command // // TODO The command length should be determined based on the bytes transferred in the COMMAND phase // //--------------------------------------------------------------------------- int GPIOBUS::GetCommandByteCount(BYTE opcode) { if (opcode == 0x88 || opcode == 0x8A || opcode == 0x8F || opcode == 0x91 || opcode == 0x9E || opcode == 0x9F) { return 16; } else if (opcode == 0xA0) { return 12; } else if (opcode >= 0x20 && opcode <= 0x7D) { return 10; } else { return 6; } } //--------------------------------------------------------------------------- // // System timer address // //--------------------------------------------------------------------------- volatile DWORD* SysTimer::systaddr; //--------------------------------------------------------------------------- // // ARM timer address // //--------------------------------------------------------------------------- volatile DWORD* SysTimer::armtaddr; //--------------------------------------------------------------------------- // // Core frequency // //--------------------------------------------------------------------------- volatile DWORD SysTimer::corefreq; //--------------------------------------------------------------------------- // // Initialize the system timer // //--------------------------------------------------------------------------- void SysTimer::Init(DWORD *syst, DWORD *armt) { // 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 DWORD maxclock[32] = { 32, 0, 0x00030004, 8, 0, 4, 0, 0 }; // 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 // //--------------------------------------------------------------------------- DWORD SysTimer::GetTimerLow() { return systaddr[SYST_CLO]; } //--------------------------------------------------------------------------- // // Get system timer high byte // //--------------------------------------------------------------------------- DWORD SysTimer::GetTimerHigh() { return systaddr[SYST_CHI]; } //--------------------------------------------------------------------------- // // Sleep in nanoseconds // //--------------------------------------------------------------------------- void SysTimer::SleepNsec(DWORD nsec) { // If time is 0, don't do anything if (nsec == 0) { return; } // Calculate the timer difference DWORD diff = corefreq * nsec / 1000; // Return if the difference in time is too small if (diff == 0) { return; } // Start DWORD start = armtaddr[ARMT_FREERUN]; // Loop until timer has elapsed while ((armtaddr[ARMT_FREERUN] - start) < diff); } //--------------------------------------------------------------------------- // // Sleep in microseconds // //--------------------------------------------------------------------------- void SysTimer::SleepUsec(DWORD usec) { // If time is 0, don't do anything if (usec == 0) { return; } DWORD now = GetTimerLow(); while ((GetTimerLow() - now) < usec); }