1
0
mirror of synced 2024-11-27 22:50:47 +01:00

Add roel's code

This commit is contained in:
Bobby Dilley 2023-11-07 21:03:06 +00:00
parent fdd590a2f8
commit 70b7824419
19 changed files with 826 additions and 412 deletions

View File

@ -1,5 +1,5 @@
CC=gcc -m32
CFLAGS = -g -O0 -fPIC -m32 -D_GNU_SOURCE -Wall -Werror -Wno-unused-variable -Wno-unused-function
CFLAGS = -g -O0 -fPIC -m32 -D_GNU_SOURCE -Wall -Werror -Wno-unused-variable -Wno-unused-function -DLOG_USE_COLOR
LD = g++ -m32
LDFLAGS = -Wl,-z,defs -rdynamic -static-libstdc++ -static-libgcc -lc -ldl -lGL -lglut -lX11 -lm -lpthread -shared -nostdlib
@ -7,7 +7,7 @@ BUILD = build
OBJS := $(patsubst %.c,%.o,$(wildcard src/lindbergh/*.c))
all: lindbergh.so libsegaapi.so libkswapapi.so
all: lindbergh.so libsegaapi.so
lindbergh.so: $(OBJS)
mkdir -p $(BUILD)
@ -21,8 +21,5 @@ libsegaapi.so: src/libsegaapi/segaapi.o
$(LIBSEGA_LD) $(LIBSEGA_LDFLAGS) src/libsegaapi/segaapi.o -L/usr/lib/i386-linux-gnu -lalut -fPIC -shared -o $(BUILD)/libsegaapi.so
rm -f src/libsegaapi/*.o
libkswapapi.so: src/libkswapapi/libkswapapi.o
$(LIBSEGA_LD) $(LIBSEGA_LDFLAGS) src/libkswapapi/libkswapapi.o -L/usr/lib/i386-linux-gnu -fPIC -shared -o $(BUILD)/libkswapapi.so
clean:
rm -rf $(BUILD)

View File

@ -23,13 +23,6 @@ sudo apt install libstdc++5:i386
## Building & Running
This emulator will need access to the input devices and serial devices on Linux. Before running this emulator you should add your user account to the following groups and then _restart your computer_.
```
sudo addgroup $USER dialout
sudo addgroup $USER input
```
To build, run the makefile, and then copy the contents of the build directory into your game directory and run.
```
@ -39,12 +32,10 @@ cd ~/the-house-of-the-dead-4/disk0/elf
LD_PRELOAD=lindbergh.so LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. ./hod4M.elf
```
Some games will require extra libraries like `libposixtime.so`. These can be found in any dumps of the Lindbergh CF image.
It is likely that the games will require various other libraries from the Lindbergh system such as `libkswapapi.so` and `libposixtime.so`. These can be found in any dumps of the Lindbergh CF image.
A default configuration file is provided in `docs/lindbergh.conf`. It should be placed in the same folder as the game is run from. If no config file is present a default setting will be used.
I recomend that you do not run this as root, and instead use the usergroups for input/dialout to give the emulator access to what it needs. The Lindbergh games expect full control of the Linux OS and with root privilages it is possible that they could cause damage to your computer.
## Controls
Currently the controls are set up for The House of the Dead 4.

View File

@ -1,10 +0,0 @@
#include "libkswapapi.h"
/**
* Hook for the only function provided by kswapapi.so
* @param p No idea this gets discarded
*/
void kswap_collect(void *p)
{
return;
}

View File

@ -1,10 +0,0 @@
#ifndef __LIBKSWAP_H
#define __LIBKSWAP_H
/**
* Hook for the only function provided by kswapapi.so
* @param p No idea this gets discarded
*/
void kswap_collect(void *p);
#endif /* __LIBKSWAP_H */

Binary file not shown.

View File

@ -1,27 +1,16 @@
#include <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <linux/serial.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <stdlib.h> /* Standard library functions like malloc, free, exit, and atoi */
#include "log.h"
#include "baseboard.h"
#include "config.h"
#include "jvs.h"
#include "serial.h"
#include "jvsserial.h"
#define SERIAL_STRING "FE11-X018012022X"
@ -38,29 +27,26 @@
#define BASEBOARD_PROCESS_JVS 0x220
#define BASEBOARD_READY 0x201
typedef struct
{
uint32_t srcAddress;
uint32_t srcSize;
uint32_t destAddress;
uint32_t destSize;
typedef struct {
uint32_t srcAddress;
uint32_t srcSize;
uint32_t destAddress;
uint32_t destSize;
} BaseboardCommand;
BaseboardCommand jvsCommand = {0};
BaseboardCommand serialCommand = {0};
typedef struct
{
uint32_t *data;
uint32_t offset;
uint32_t size;
typedef struct {
uint32_t *data;
uint32_t offset;
uint32_t size;
} readData_t;
typedef struct
{
uint32_t offset;
uint32_t *data;
uint32_t size;
typedef struct {
uint32_t offset;
uint32_t *data;
uint32_t size;
} writeData_t;
FILE *sram = NULL;
@ -71,231 +57,251 @@ uint8_t sharedMemory[1024 * 32] = {0};
int selectReply = -1;
int jvsFileDescriptor = -1;
int initBaseboard()
{
char *sramPath = getConfig()->sramPath;
sram = fopen(sramPath, "a");
// Create file if it doesn't exist
if (sram == NULL)
{
printf("Error: Cannot open %s\n", sramPath);
return 1;
}
fclose(sram);
sram = fopen(sramPath, "rb+");
fseek(sram, 0, SEEK_SET);
if (getConfig()->emulateJVS == 0 && strcmp(getConfig()->jvsPath, "none") != 0)
{
jvsFileDescriptor = open(getConfig()->jvsPath, O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY);
if (jvsFileDescriptor < 0)
{
printf("Error: Failed to open %s for JVS\n", getConfig()->jvsPath);
/**
* Set up the fake Lindy baseboard
* @return
*/
int initBaseboard() {
// TODO: move SRAM part to a dedicated function
char *sramPath = getConfig()->sramPath;
sram = fopen(sramPath, "a");
// Create file if it doesn't exist
if (sram == NULL) {
log_error("Cannot open %s", sramPath);
return 1;
}
setSerialAttributes(jvsFileDescriptor, B115200);
}
fclose(sram);
sram = fopen(sramPath, "rb+");
fseek(sram, 0, SEEK_SET);
return 0;
}
ssize_t baseboardRead(int fd, void *buf, size_t count)
{
memcpy(buf, &sharedMemory[sharedMemoryIndex], count);
return count;
}
ssize_t baseboardWrite(int fd, const void *buf, size_t count)
{
memcpy(&sharedMemory[sharedMemoryIndex], buf, count);
return count;
}
int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict timeout)
{
return selectReply;
}
int baseboardIoctl(int fd, unsigned int request, void *data)
{
switch (request)
{
case BASEBOARD_GET_VERSION:
{
uint8_t versionData[4] = {0x00, 0x19, 0x20, 0x07};
memcpy(data, versionData, 4);
return 0;
}
break;
case BASEBOARD_INIT:
{
// selectReply = -1; Considering adding this in
return 0;
}
break;
case BASEBOARD_READY: // Not sure this is what it should be called
{
selectReply = 0;
return 0;
}
break;
case BASEBOARD_SEEK_SHM:
{
sharedMemoryIndex = (unsigned int)data;
return 0;
}
break;
case BASEBOARD_READ_SRAM:
{
readData_t *_data = data;
fseek(sram, _data->offset, SEEK_SET);
fread(_data->data, 1, _data->size, sram);
return 0;
}
break;
case BASEBOARD_WRITE_SRAM:
{
writeData_t *_data = data;
fseek(sram, _data->offset, SEEK_SET);
fwrite(_data->data, 1, _data->size, sram);
return 0;
}
break;
case BASEBOARD_REQUEST:
{
uint32_t *_data = data;
switch (_data[0])
{
case BASEBOARD_GET_SERIAL: // bcCmdSysInfoGetReq
{
serialCommand.destAddress = _data[1];
serialCommand.destSize = _data[2];
}
break;
case BASEBOARD_WRITE_FLASH: // bcCmdSysFlashWrite
{
printf("Warning: The game attempted to write to the baseboard flash\n");
}
break;
case BASEBOARD_PROCESS_JVS:
{
jvsCommand.srcAddress = _data[1];
jvsCommand.srcSize = _data[2];
jvsCommand.destAddress = _data[3];
jvsCommand.destSize = _data[4];
memcpy(inputBuffer, &sharedMemory[jvsCommand.srcAddress], jvsCommand.srcSize);
if (getConfig()->emulateJVS)
{
processPacket();
}
else if (jvsFileDescriptor >= 0)
{
write(jvsFileDescriptor, inputBuffer, jvsCommand.srcSize);
for (int i = 0; i < jvsCommand.srcSize; i++)
{
if (inputBuffer[i] == 0xF0)
{
setSenseLine(3);
}
else if (inputBuffer[i] == 0xF1)
{
setSenseLine(1);
}
// Config disable JVS emulation and jvsPath is set, we open serial port for JVS interface
if (getConfig()->emulateJVS == 0 && strcmp(getConfig()->jvsPath, "none") != 0) {
jvsFileDescriptor = openJVSSerial(getConfig()->jvsPath);
if (jvsFileDescriptor < 0) {
log_error("Failed to open '%s' for JVS", getConfig()->jvsPath);
exit(1);
}
}
// Set up the serial port settings, so it acts as a JVS interface
initJVSSerial(jvsFileDescriptor);
// Start a thread to listen for serial port
startJVSFrameThread(&jvsFileDescriptor);
}
return 0;
}
ssize_t baseboardRead(int fd, void *buf, size_t count) {
memcpy(buf, &sharedMemory[sharedMemoryIndex], count);
return count;
}
ssize_t baseboardWrite(int fd, const void *buf, size_t count) {
memcpy(&sharedMemory[sharedMemoryIndex], buf, count);
return count;
}
int baseboardSelect(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds,
struct timeval *restrict timeout) {
return selectReply;
}
void baseboardIoctlRequest(uint32_t *_data) {
switch (_data[0]) {
case BASEBOARD_GET_SERIAL: // bcCmdSysInfoGetReq
{
serialCommand.destAddress = _data[1];
serialCommand.destSize = _data[2];
}
break;
case BASEBOARD_WRITE_FLASH: // bcCmdSysFlashWrite
{
log_warn("The game attempted to write to the baseboard flash.");
}
break;
case BASEBOARD_PROCESS_JVS: {
jvsCommand.srcAddress = _data[1];
jvsCommand.srcSize = _data[2];
jvsCommand.destAddress = _data[3];
jvsCommand.destSize = _data[4];
memcpy(inputBuffer, &sharedMemory[jvsCommand.srcAddress], jvsCommand.srcSize);
char hex_string[(jvsCommand.srcSize * 3) + 1];
int hex_string_len = 0;
for (int i = 0; i < jvsCommand.srcSize; i++) {
hex_string_len += sprintf(hex_string+hex_string_len, "%02X ", (&sharedMemory[jvsCommand.destAddress])[i]);
}
log_trace("JVS: Writing %s", hex_string);
if (getConfig()->emulateJVS) {
processPacket();
} else if (jvsFileDescriptor >= 0) {
// F0 D9 Command: Reset (RESET)
for (int i = 0; i < jvsCommand.srcSize; i++) {
write(jvsFileDescriptor, &inputBuffer[i], 1);
if (inputBuffer[i] == 0xF0) {
log_trace("JVS: got 0xF0 (Reset), soft senseLine is set to 3.");
setSenseLine(3);
}
if (inputBuffer[i] == 0xF1) {
log_trace("JVS: got 0xF1 (Set Address), soft senseLine is set to 1.");
setSenseLine(1);
}
}
log_trace("JVS: Hardware Control lines: CTS %02X - DSR %02X - DCD %02X", getCTS(jvsFileDescriptor), getDSR(jvsFileDescriptor), getDCD(jvsFileDescriptor));
}
}
break;
case BASEBOARD_GET_SENSE_LINE:
log_trace("JVS DEBUG: GetSenseLine");
break;
default:
log_error("Unknown baseboard command %X", _data[0]);
}
// Acknowledge the command
_data[0] |= 0xF0000000;
}
void baseboardIoctlReceive(uint32_t *_data) {
switch (_data[0] & 0xFFF) {
case BASEBOARD_GET_SERIAL: {
memcpy(&sharedMemory[serialCommand.destAddress + 96], SERIAL_STRING, strlen(SERIAL_STRING));
_data[1] = 1; // Set the status to success
}
break;
case BASEBOARD_GET_SENSE_LINE: {
/*
* Values are:
* 3 = no device, after a RESET
* 1 = address assigned
*/
if (getConfig()->emulateJVS) {
_data[2] = getSenseLine();
_data[1] = 1; // Set the status to success
} else {
_data[2] = getSenseLine();
_data[1] = 1;
}
log_trace("JVS: GetSenseLine: %02X - %02X", _data[2], _data[1]);
}
break;
case BASEBOARD_PROCESS_JVS: {
if (getConfig()->emulateJVS) {
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, outputPacket.length + 3);
_data[2] = jvsCommand.destAddress;
_data[3] = outputPacket.length + 3;
_data[1] = 1; // Set the status to success
} else if (jvsFileDescriptor >= 0) {
jvsFrame frame = readJVSFrameFromThread();
memcpy(&sharedMemory[jvsCommand.destAddress], frame.buffer, frame.size);
_data[2] = jvsCommand.destAddress;
_data[3] = frame.size;
_data[1] = frame.ready;
}
if (_data[3] > 0) {
log_trace("JVS: Data extraction: Ready: %d - Address: %d - Length: %d", _data[1], _data[2], _data[3]);
log_trace("JVS: Hardware Control lines: CTS %02X - DSR %02X - DCD %02X", getCTS(jvsFileDescriptor), getDSR(jvsFileDescriptor), getDCD(jvsFileDescriptor));
char hex_string[(_data[3] * 3) + 1];
int hex_string_len = 0;
for (int i = 0; i < _data[3]; i++) {
hex_string_len += sprintf(hex_string+hex_string_len, "%02X ", (&sharedMemory[jvsCommand.destAddress])[i]);
}
log_trace("JVS: Reading %s", hex_string);
}
}
break;
default:
log_error("Unknown baseboard receive command %X", _data[0] & 0xFFF);
}
// Acknowledge the command
_data[0] |= 0xF0000000;
}
int baseboardIoctl(int fd, unsigned int request, void *data) {
switch (request) {
case BASEBOARD_GET_VERSION: {
uint8_t versionData[4] = {0x00, 0x19, 0x20, 0x07};
memcpy(data, versionData, 4);
return 0;
}
break;
case BASEBOARD_INIT: {
// selectReply = -1; Considering adding this in
return 0;
}
break;
case BASEBOARD_READY: // Not sure this is what it should be called
{
selectReply = 0;
return 0;
}
break;
case BASEBOARD_SEEK_SHM: {
sharedMemoryIndex = (unsigned int) data;
return 0;
}
break;
case BASEBOARD_READ_SRAM: {
readData_t *_data = data;
fseek(sram, _data->offset, SEEK_SET);
fread(_data->data, 1, _data->size, sram);
return 0;
}
break;
case BASEBOARD_WRITE_SRAM: {
writeData_t *_data = data;
fseek(sram, _data->offset, SEEK_SET);
fwrite(_data->data, 1, _data->size, sram);
return 0;
}
break;
case BASEBOARD_REQUEST: {
uint32_t *_data = data;
baseboardIoctlRequest(_data);
return 0;
}
break;
case BASEBOARD_RECEIVE: {
uint32_t *_data = data;
baseboardIoctlReceive(_data);
return 0;
}
break;
default:
log_error("Unknown baseboard ioctl %X", request);
}
break;
case BASEBOARD_GET_SENSE_LINE:
break;
default:
printf("Error: Unknown baseboard command %X\n", _data[0]);
}
// Acknowledge the command
_data[0] |= 0xF0000000;
return 0;
}
break;
case BASEBOARD_RECEIVE:
{
uint32_t *_data = data;
switch (_data[0] & 0xFFF)
{
case BASEBOARD_GET_SERIAL:
{
memcpy(&sharedMemory[serialCommand.destAddress + 96], SERIAL_STRING, strlen(SERIAL_STRING));
_data[1] = 1; // Set the status to success
}
break;
case BASEBOARD_GET_SENSE_LINE:
{
_data[2] = getSenseLine();
_data[1] = 1; // Set the status to success
}
break;
case BASEBOARD_PROCESS_JVS:
{
if (getConfig()->emulateJVS)
{
memcpy(&sharedMemory[jvsCommand.destAddress], outputBuffer, outputPacket.length + 3);
_data[2] = jvsCommand.destAddress;
_data[3] = outputPacket.length + 3;
_data[1] = 1; // Set the status to success
}
else if (jvsFileDescriptor >= 0)
{
int count = readBytes(jvsFileDescriptor, &sharedMemory[jvsCommand.destAddress], 255);
if (count == -1)
count = 0;
_data[2] = jvsCommand.destAddress;
_data[3] = count;
_data[1] = (count > 0); // Success if we receive any sort of data back
}
}
break;
default:
printf("Error: Unknown baseboard receive command %X\n", _data[0] & 0xFFF);
}
// Acknowledge the command
_data[0] |= 0xF0000000;
return 0;
}
break;
default:
printf("Error: Unknown baseboard ioctl %X\n", request);
}
return 0;
}

View File

@ -4,6 +4,7 @@
#include <string.h>
#include "config.h"
#include "log.h"
EmulatorConfig config = {0};
@ -149,7 +150,7 @@ int readConfig(FILE *configFile, EmulatorConfig *config)
}
else
printf("Error: Unknown settings command %s\n", command);
log_error("Unknown settings command %s", command);
}
return 0;
@ -173,14 +174,14 @@ int initConfig()
config.height = 768;
if (detectGame() != 0)
{
printf("Warning: Unsure what game this is, using default configuration values");
log_warn("Unsure what game this is, using default configuration values");
}
configFile = fopen(CONFIG_PATH, "r");
if (configFile == NULL)
{
printf("Warning: Cannot open %s, using default values\n", CONFIG_PATH);
log_warn("Cannot open %s, using default values", CONFIG_PATH);
return 1;
}

View File

@ -4,6 +4,7 @@
#include "eeprom.h"
#include "config.h"
#include "log.h"
#define I2C_SMBUS_BLOCK_MAX 32
#define I2C_GET_FUNCTIONS 0x705
@ -38,7 +39,7 @@ int initEeprom()
// Create file if it doesn't exist
if (eeprom == NULL)
{
printf("Error: Cannot open %s\n", eepromPath);
log_error("Cannot open %s", eepromPath);
return 1;
}
@ -97,7 +98,7 @@ int eepromIoctl(int fd, unsigned int request, void *data)
break;
default:
printf("Error: Incorrect I2C transfer size\n");
log_error("Incorrect I2C transfer size");
}
}
break;
@ -107,7 +108,7 @@ int eepromIoctl(int fd, unsigned int request, void *data)
break;
default:
printf("Error: Unkown I2C ioctl %X\n", request);
log_error("Unkown I2C ioctl %X", request);
}
return 0;

View File

@ -11,6 +11,7 @@
#include "config.h"
#include "jvs.h"
#include "securityboard.h"
#include "log.h"
int gameModeWidth = -1;
int gameModeHeight = -1;
@ -51,7 +52,7 @@ FGAPI void FGAPIENTRY glutSetCursor(int cursor)
FGAPI void FGAPIENTRY glutGameModeString(const char *string)
{
printf("glutGameModeString: %s\n", string);
log_info("glutGameModeString: %s", string);
char gameModeString[1024];
strcpy(gameModeString, string);
@ -79,7 +80,7 @@ FGAPI void FGAPIENTRY glutGameModeString(const char *string)
if (getConfig()->width != width || getConfig()->height != height)
{
printf("Warning: Game is overriding resolution settings to %dX%d\n", width, height);
log_warn("Game is overriding resolution settings to %dX%d", width, height);
getConfig()->width = width;
getConfig()->height = height;
}
@ -98,7 +99,7 @@ Window XCreateWindow(Display *display, Window parent, int x, int y, unsigned int
// attributes->override_redirect = False;
Window window = _XCreateWindow(display, parent, x, y, width, height, border_width, depth, class, visual, valueMask, attributes);
printf("XCreateWindow Resolution %d %d %d %d\n", x, y, width, height);
log_info("XCreateWindow Resolution %d %d %d %d", x, y, width, height);
if (getConfig()->fullscreen)
{
@ -200,8 +201,8 @@ int XNextEvent(Display *display, XEvent *event_return)
case MotionNotify:
{
setAnalogue(ANALOGUE_1, ((double)event_return->xmotion.x / (double)getConfig()->width) * 255);
setAnalogue(ANALOGUE_2, ((double)event_return->xmotion.y / (double)getConfig()->height) * 255);
setAnalogue(ANALOGUE_1, ((double)event_return->xmotion.x / (double)getConfig()->width) * 1024.0);
setAnalogue(ANALOGUE_2, ((double)event_return->xmotion.y / (double)getConfig()->height) * 1024.0);
}
break;

View File

@ -24,6 +24,7 @@
#include "rideboard.h"
#include "securityboard.h"
#include "patch.h"
#include "log.h"
#define HOOK_FILE_NAME "/dev/zero"
@ -41,16 +42,24 @@ int fileRead[2] = {0, 0};
uint16_t basePortAddress = 0xFFFF;
/**
* Signal handler for the SIGSEGV signal, which is triggered when a process tries to access an illegal memory location.
* @param signal
* @param info
* @param ptr
*/
static void handleSegfault(int signal, siginfo_t *info, void *ptr)
{
ucontext_t *ctx = ptr;
// Get the address of the instruction causing the segfault
uint8_t *code = (uint8_t *)ctx->uc_mcontext.gregs[REG_EIP];
switch (*code)
{
case 0xED:
{
// Get the port number from the EDX register
uint16_t port = ctx->uc_mcontext.gregs[REG_EDX] & 0xFFFF;
// The first port called is usually random, but everything after that
@ -60,9 +69,11 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
if (basePortAddress == 0xFFFF)
basePortAddress = port;
// Adjust the port number if necessary
if (port > 0x38)
port = port - basePortAddress;
// Call the security board input function with the port number and data
securityBoardIn(port, (uint32_t *)&(ctx->uc_mcontext.gregs[REG_EAX]));
ctx->uc_mcontext.gregs[REG_EIP]++;
@ -70,15 +81,17 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
}
break;
case 0xE7: // OUT IMMIDIATE
case 0xE7: // OUT IMMEDIATE
{
// Increment the instruction pointer by two to skip over this instruction
ctx->uc_mcontext.gregs[REG_EIP] += 2;
return;
}
break;
case 0xE6: // OUT IMMIDIATE
case 0xE6: // OUT IMMEDIATE
{
// Increment the instruction pointer by two to skip over this instruction
ctx->uc_mcontext.gregs[REG_EIP] += 2;
return;
}
@ -102,7 +115,7 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
break;
default:
printf("Warning: Skipping SEGFAULT %X\n", *code);
log_warn("Skipping SEGFAULT %X", *code);
ctx->uc_mcontext.gregs[REG_EIP]++;
// abort();
}
@ -111,6 +124,8 @@ static void handleSegfault(int signal, siginfo_t *info, void *ptr)
void __attribute__((constructor)) hook_init()
{
// "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
log_set_level(1);
printf("SEGA Lindbergh Loader\nRobert Dilley 2022\nNot for public consumption\n\n");
// Implement SIGSEGV handler
@ -150,7 +165,7 @@ void __attribute__((constructor)) hook_init()
securityBoardSetDipResolution(getConfig()->width, getConfig()->height);
printf("Loader init success\n");
log_info("Loader init success");
}
int open(const char *pathname, int flags)
@ -162,14 +177,14 @@ int open(const char *pathname, int flags)
if (strcmp(pathname, "/dev/lbb") == 0)
{
hooks[BASEBOARD] = _open(HOOK_FILE_NAME, flags);
printf("Baseboard opened %d\n", hooks[BASEBOARD]);
log_info("Baseboard opened %d", hooks[BASEBOARD]);
return hooks[BASEBOARD];
}
if (strcmp(pathname, "/dev/i2c/0") == 0)
{
hooks[EEPROM] = _open(HOOK_FILE_NAME, flags);
printf("EEPROM opened %d\n", hooks[EEPROM]);
log_info("EEPROM opened %d", hooks[EEPROM]);
return hooks[EEPROM];
}
@ -179,7 +194,7 @@ int open(const char *pathname, int flags)
return -1;
hooks[SERIAL0] = _open(HOOK_FILE_NAME, flags);
printf("SERIAL0 Opened %d\n", hooks[SERIAL0]);
log_info("SERIAL0 Opened %d", hooks[SERIAL0]);
return hooks[SERIAL0];
}
@ -189,7 +204,7 @@ int open(const char *pathname, int flags)
return -1;
hooks[SERIAL1] = _open(HOOK_FILE_NAME, flags);
printf("SERIAL1 opened %d\n", hooks[SERIAL1]);
log_info("SERIAL1 opened %d", hooks[SERIAL1]);
return hooks[SERIAL1];
}
@ -215,7 +230,6 @@ int sem_wait(sem_t *sem)
FILE *fopen(const char *restrict pathname, const char *restrict mode)
{
FILE *(*_fopen)(const char *restrict pathname, const char *restrict mode) = dlsym(RTLD_NEXT, "fopen");
// printf("fopen %s\n", pathname);
if (strcmp(pathname, "/root/lindbergrc") == 0)
{
@ -480,6 +494,10 @@ int sem_wait(sem_t *sem)
/**
* Hook function used by Harley Davidson to change IPs to localhost
* Currently does nothing.
* @param sockfd
* @param addr
* @param addrlen
* @return
*/
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
@ -490,7 +508,7 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
// Change the IP to connect to to 127.0.0.1
// in_pointer->sin_addr.s_addr = inet_addr("127.0.0.1");
char *some_addr = inet_ntoa(in_pointer->sin_addr);
printf("Connecting to %s\n", some_addr);
log_info("Connecting to %s", some_addr);
return _connect(sockfd, addr, addrlen);
}

View File

@ -30,7 +30,7 @@ int initJVS()
io.capabilities.switches = 14;
io.capabilities.coins = 2;
io.capabilities.players = 2;
io.capabilities.analogueInBits = 8;
io.capabilities.analogueInBits = 10;
io.capabilities.rightAlignBits = 0;
io.capabilities.analogueInChannels = 20;
io.capabilities.generalPurposeOutputs = 20;

274
src/lindbergh/jvsserial.c Normal file
View File

@ -0,0 +1,274 @@
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/ioctl.h> /* Ioctl function to control device drivers in the kernel */
#include <stdlib.h> /* Standard library functions like malloc, free, exit, and atoi */
#include <pthread.h> /* POSIX threads API to create and manage threads in the program */
#include "jvsserial.h"
#include "log.h"
#define TIMEOUT_SELECT 200
#define CTS_ON_RETRY 20
// Used to read JVS frame in a non-blocking way
jvsFrame jvsFrameBuffer;
pthread_mutex_t jvsBuffer_lock = PTHREAD_MUTEX_INITIALIZER;
/**
* Open the serial interface and return a file descriptor
* @param jvsPath The serial port path. Ex: "/dev/ttyS3"
* @return A file descriptor
*/
int openJVSSerial(char *jvsPath) {
int jvsFileDescriptor = -1;
// TODO: check O_NOCTTY declaration
jvsFileDescriptor = open(jvsPath, O_RDWR | O_NOCTTY);
if (jvsFileDescriptor < 0) {
log_error("Failed to open '%s' for JVS.", jvsPath);
}
return jvsFileDescriptor;
}
/**
* Init a serial port (using file descriptor) so it behaves correctly for JVS usage
* @param fd
* @return 0|1
*/
int initJVSSerial(int fd) {
struct termios options;
int status;
// Get the current options
if (tcgetattr(fd, &options) != 0) {
log_error("Error %i from tcgetattr: %s\n", errno, strerror(errno));
return 1;
}
// Set rates
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
/* From doc:
* If the CLOCAL flag for a line is off, the hardware carrier detect (DCD) signal is significant,
* an open(2) of the corresponding terminal will block until DCD is asserted, unless the O_NONBLOCK
* flag is given. If CLOCAL is set, the line behaves as if DCD is always asserted. The software
* carrier flag is usually turned on for local devices, and is off for lines with modems.
*/
// options.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines
options.c_cflag |= CREAD; // Turn on READ, let ctrl lines work
options.c_cflag &= ~PARENB; // Clear parity bit & disable parity
options.c_cflag &= ~CSTOPB; // Clear stop field, 1 stop bit
options.c_cflag &= ~CSIZE; // Clear all bits that set the data size
options.c_cflag |= CS8; // 8 bits
options.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control
options.c_lflag &= ~ICANON; // Disable canonical mode, so no input processing is performed
options.c_lflag &= ~ECHO; // Disable echo
options.c_lflag &= ~ECHOE; // Disable erasure
options.c_lflag &= ~ECHONL; // Disable new-line echo
options.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
options.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
ICRNL); // Disable any special handling of received bytes
options.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
options.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
// Set VMIN and VTIME to 0, so it returns immediately when no data are present
// options.c_cc[VMIN] = 0;
// options.c_cc[VTIME] = 0;
// With threaded serial read we should rely on a blocking read() function so the loop doesn't run crazy => read() could block indefinitely.
// options.c_cc[VMIN] = 1;
// options.c_cc[VTIME] = 0;
// Block until either VMIN characters have been received or VTIME **after first character** has been received
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 1;
tcsetattr(fd, TCSANOW, &options);
/* No use ? Save it for later
// Set the serial port to non-blocking mode
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
*/
return 0;
}
/**
* The DCD (Data Carrier Detect) status of a serial port indicates whether a carrier is present on the line, meaning that a connection has been established with another device.
* @param fd File descriptor of JVS (serial) port
* @return 0|1 According to control line status
*/
int getDCD(int fd) {
int status;
ioctl(fd, TIOCMGET, &status);
return (status & TIOCM_CAR) != 0;
}
/**
* The DSR (Data Set Ready) status of a serial port indicates whether the device at the other end of the connection is ready to receive data.
* @param fd File descriptor of JVS (serial) port
* @return 0|1 According to control line status
*/
int getDSR(int fd) {
int status;
ioctl(fd, TIOCMGET, &status);
return (status & TIOCM_DSR) != 0;
}
/**
* The CTS (Clear To Send) status of a serial port indicates whether the device at the other end of the connection is ready to accept data.
* @param fd File descriptor of JVS (serial) port
* @return 0|1 According to control line status
*/
int getCTS(int fd) {
int status;
ioctl(fd, TIOCMGET, &status);
return (status & TIOCM_CTS) != 0;
}
/**
* In charge of reading serial port and bufferize JVS frame
* @param arg
* @return
*/
void *readJVSFrameThread(void * arg)
{
int fd = *((int *) arg);
int byteCount, bytesRead, ackSize, waitForEnd;
int ctsRetry = CTS_ON_RETRY;
char localBuffer[JVSBUFFER_SIZE];
while (1)
{
// Reset local variable
byteCount = 0;
ackSize = 0;
waitForEnd = 0;
do {
// printf("SERIAL thread debug: trying to read byte.\n");
// Try to read a byte from serial, this call will be blocking if VMIN > 0 and VTIME = 0
bytesRead = read(fd, &localBuffer[byteCount], 1);
// If nothing on serial and CTS is ON, we try to read "CTS_ON_RETRY" times
ctsRetry = CTS_ON_RETRY;
while (bytesRead < 1 && --ctsRetry > 0 && jvsFrameBuffer.ready == 0) {
bytesRead = read(fd, &localBuffer[byteCount], 1);
log_trace("SERIAL: Retry number %d.\n", ctsRetry);
}
if (bytesRead > 0) {
// Sync byte, we will stick in the loop
if (byteCount == 0 && localBuffer[byteCount] == (char) 0xE0) {
waitForEnd = 1;
}
// Size byte
if (byteCount == 2) {
ackSize = localBuffer[byteCount] + 3;
}
// Start counting bytes only if SYNC has been found
if (waitForEnd) {
byteCount++;
}
// Reached the end of the message
if (byteCount == ackSize) {
waitForEnd = 0;
}
}
} while (waitForEnd);
// Lock the buffer while we write to it
pthread_mutex_lock(&jvsBuffer_lock);
// Fake a response if CTS is ON but no message from JVS board, not sure about this...
if (ctsRetry == 0) {
char hexData[] = {0xE0, 0x00, 0x02, 0x01, 0x03};
byteCount = sizeof(hexData);
memcpy(localBuffer, hexData, byteCount);
}
// We copy local buffer and length to mutexed buffer
if (/* ctsRetry == 0 || */ byteCount > 0) {
memcpy(jvsFrameBuffer.buffer, localBuffer, byteCount);
jvsFrameBuffer.size = byteCount;
jvsFrameBuffer.ready = 1;
// Reset local variable
byteCount = 0;
ctsRetry = CTS_ON_RETRY;
}
pthread_mutex_unlock(&jvsBuffer_lock);
}
return NULL;
}
/**
* Init the thread in charge of reading serial port
* @param fd
* @return 0|1
*/
int startJVSFrameThread(int * fd) {
int fdlocal = *((int *) fd);
log_trace("SERIAL: starting thread.\n");
// Clean shared JVS frame buffer
jvsFrameBuffer.ready = 0;
jvsFrameBuffer.size = 0;
memset(jvsFrameBuffer.buffer, 0, JVSBUFFER_SIZE);
pthread_t jvsFrameThread;
int ret = pthread_create(&jvsFrameThread, NULL, readJVSFrameThread, fd);
if (ret != 0)
{
log_error("SERIAL: Failed to create reader thread");
return 1;
}
return 0;
}
/**
* Return a jvsFrame structure with empty or full data, no in between
* @return
*/
jvsFrame readJVSFrameFromThread() {
jvsFrame frame;
// Lock while reading/writing to shared frame
pthread_mutex_lock(&jvsBuffer_lock);
// Check if we have a valid frame
if (jvsFrameBuffer.ready == 1) {
frame = jvsFrameBuffer;
// It has been red, we disable this frame
jvsFrameBuffer.ready = 0;
} else {
frame.ready = 0;
frame.size = 0;
memset(frame.buffer, 0, JVSBUFFER_SIZE);
}
pthread_mutex_unlock(&jvsBuffer_lock);
return frame;
}

21
src/lindbergh/jvsserial.h Normal file
View File

@ -0,0 +1,21 @@
#define JVSBUFFER_SIZE 1024
typedef struct {
int ctsCounter;
int ready;
int size;
char buffer[JVSBUFFER_SIZE];
} jvsFrame;
int getDCD(int fd);
int getDSR(int fd);
int getCTS(int fd);
jvsFrame readJVSFrameFromThread();
int startJVSFrameThread(int * fd);
void * readJVSFrameThread(void * arg);
int openJVSSerial(char *jvsPath);
int initJVSSerial(int fd);
int readJVSFrame(int fd, unsigned char *buffer);

168
src/lindbergh/log.c Normal file
View File

@ -0,0 +1,168 @@
/*
* Copyright (c) 2020 rxi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "log.h"
#define MAX_CALLBACKS 32
typedef struct {
log_LogFn fn;
void *udata;
int level;
} Callback;
static struct {
void *udata;
log_LockFn lock;
int level;
bool quiet;
Callback callbacks[MAX_CALLBACKS];
} L;
static const char *level_strings[] = {
"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
};
#ifdef LOG_USE_COLOR
static const char *level_colors[] = {
"\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"
};
#endif
static void stdout_callback(log_Event *ev) {
char buf[16];
buf[strftime(buf, sizeof(buf), "%H:%M:%S", ev->time)] = '\0';
#ifdef LOG_USE_COLOR
fprintf(
ev->udata, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ",
buf, level_colors[ev->level], level_strings[ev->level],
ev->file, ev->line);
#else
fprintf(
ev->udata, "%s %-5s %s:%d: ",
buf, level_strings[ev->level], ev->file, ev->line);
#endif
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
}
static void file_callback(log_Event *ev) {
char buf[64];
buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0';
fprintf(
ev->udata, "%s %-5s %s:%d: ",
buf, level_strings[ev->level], ev->file, ev->line);
vfprintf(ev->udata, ev->fmt, ev->ap);
fprintf(ev->udata, "\n");
fflush(ev->udata);
}
static void lock(void) {
if (L.lock) { L.lock(true, L.udata); }
}
static void unlock(void) {
if (L.lock) { L.lock(false, L.udata); }
}
const char* log_level_string(int level) {
return level_strings[level];
}
void log_set_lock(log_LockFn fn, void *udata) {
L.lock = fn;
L.udata = udata;
}
void log_set_level(int level) {
L.level = level;
}
void log_set_quiet(bool enable) {
L.quiet = enable;
}
int log_add_callback(log_LogFn fn, void *udata, int level) {
for (int i = 0; i < MAX_CALLBACKS; i++) {
if (!L.callbacks[i].fn) {
L.callbacks[i] = (Callback) { fn, udata, level };
return 0;
}
}
return -1;
}
int log_add_fp(FILE *fp, int level) {
return log_add_callback(file_callback, fp, level);
}
static void init_event(log_Event *ev, void *udata) {
if (!ev->time) {
time_t t = time(NULL);
ev->time = localtime(&t);
}
ev->udata = udata;
}
void log_log(int level, const char *file, int line, const char *fmt, ...) {
log_Event ev = {
.fmt = fmt,
.file = file,
.line = line,
.level = level,
};
lock();
if (!L.quiet && level >= L.level) {
init_event(&ev, stderr);
va_start(ev.ap, fmt);
stdout_callback(&ev);
va_end(ev.ap);
}
for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].fn; i++) {
Callback *cb = &L.callbacks[i];
if (level >= cb->level) {
init_event(&ev, cb->udata);
va_start(ev.ap, fmt);
cb->fn(&ev);
va_end(ev.ap);
}
}
unlock();
}

49
src/lindbergh/log.h Normal file
View File

@ -0,0 +1,49 @@
/**
* Copyright (c) 2020 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See `log.c` for details.
*/
#ifndef LOG_H
#define LOG_H
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <time.h>
#define LOG_VERSION "0.1.0"
typedef struct {
va_list ap;
const char *fmt;
const char *file;
struct tm *time;
void *udata;
int line;
int level;
} log_Event;
typedef void (*log_LogFn)(log_Event *ev);
typedef void (*log_LockFn)(bool lock, void *udata);
enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL };
#define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
const char* log_level_string(int level);
void log_set_lock(log_LockFn fn, void *udata);
void log_set_level(int level);
void log_set_quiet(bool enable);
int log_add_callback(log_LogFn fn, void *udata, int level);
int log_add_fp(FILE *fp, int level);
void log_log(int level, const char *file, int line, const char *fmt, ...);
#endif

View File

@ -5,6 +5,7 @@
#include "patch.h"
#include "config.h"
#include "log.h"
static void setVariable(uint32_t address, uint32_t value)
{
@ -17,7 +18,7 @@ static void setVariable(uint32_t address, uint32_t value)
int prot = mprotect(toModify, pagesize, PROT_WRITE);
if (prot != 0)
{
printf("Error: Cannot unprotect memory region to change variable (%d)\n", prot);
log_error("Cannot unprotect memory region to change variable (%d)", prot);
return;
}
@ -33,7 +34,7 @@ static void detourFunction(uint32_t address, void *function)
int prot = mprotect(toModify, pagesize, PROT_EXEC | PROT_WRITE);
if (prot != 0)
{
printf("Error: Cannot detour memory region to change variable (%d)\n", prot);
log_error("Cannot detour memory region to change variable (%d)", prot);
return;
}
@ -107,9 +108,6 @@ int initPatch()
setVariable(0x0a737f1c, 2); // amSysDataDebugLevel
setVariable(0x0a737f20, 2); // bcLibDebugLevel
setVariable(0x0a737f24, 0x0FFFFFFF); // s_logMask
detourFunction(0x08320178, amDongleInit);
detourFunction(0x08320459, amDongleIsAvailable);
detourFunction(0x083203c0, amDongleUpdate);
}
break;

View File

@ -3,6 +3,7 @@
#include "securityboard.h"
#include "config.h"
#include "log.h"
#define SECURITY_BOARD_FRONT_PANEL 0x38
#define SECURITY_BOARD_FRONT_PANEL_NON_ROOT 0x1038
@ -53,7 +54,7 @@ int securityBoardSetDipResolution(int width, int height)
else if (width == 1360 && height == 768)
setResolutionDips(1, 1, 1);
else
printf("Warning: Resolution not compatible, using 640 x 480\n");
log_warn("Warning: Resolution not compatible, using 640 x 480");
return 0;
}
@ -68,7 +69,7 @@ int securityBoardSetDipSwitch(int switchNumber, int value)
{
if (switchNumber == 0)
{
printf("Error: Dip Switch index starts at 1\n");
log_error("Dip Switch index starts at 1");
return 1;
}
securityBoard.dipSwitch[switchNumber] = value;
@ -86,7 +87,7 @@ int securityBoardSetSwitch(JVSInput switchNumber, int value)
securityBoard.serviceSwitch = value;
break;
default:
printf("Error: Attempted to set a security board switch incorrectly");
log_error("Attempted to set a security board switch incorrectly");
return -1;
}

View File

@ -1,89 +0,0 @@
#include <stdio.h>
#include <sys/types.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <linux/serial.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#include "serial.h"
#define TIMEOUT_SELECT 200
int setSerialAttributes(int fd, int myBaud)
{
struct termios options;
int status;
tcgetattr(fd, &options);
cfmakeraw(&options);
cfsetispeed(&options, myBaud);
cfsetospeed(&options, myBaud);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_oflag &= ~OPOST;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 0; // One seconds (10 deciseconds)
tcsetattr(fd, TCSANOW, &options);
ioctl(fd, TIOCMGET, &status);
status |= TIOCM_DTR;
status |= TIOCM_RTS;
ioctl(fd, TIOCMSET, &status);
usleep(100 * 1000); // 10mS
struct serial_struct serial_settings;
ioctl(fd, TIOCGSERIAL, &serial_settings);
serial_settings.flags |= ASYNC_LOW_LATENCY;
ioctl(fd, TIOCSSERIAL, &serial_settings);
tcflush(fd, TCIOFLUSH);
usleep(100 * 1000); // Required to make flush work, for some reason
return 0;
}
int readBytes(int fd, unsigned char *buffer, int amount)
{
fd_set fd_serial;
struct timeval tv;
FD_ZERO(&fd_serial);
FD_SET(fd, &fd_serial);
tv.tv_sec = 0;
tv.tv_usec = TIMEOUT_SELECT * 1000;
int filesReadyToRead = select(fd + 1, &fd_serial, NULL, NULL, &tv);
if (filesReadyToRead < 1)
return 0;
if (!FD_ISSET(fd, &fd_serial))
return 0;
return read(fd, buffer, amount);
}

View File

@ -1,3 +0,0 @@
int setSerialAttributes(int fd, int myBaud);
int readBytes(int fd, unsigned char *buffer, int amount);