From f494984cdc708b437a4dbec236037d701eaa396b Mon Sep 17 00:00:00 2001 From: Bobby Dilley Date: Mon, 13 Nov 2023 22:45:51 +0000 Subject: [PATCH] Add roel's JVS pass through code --- .vscode/settings.json | 3 +- src/lindbergh/baseboard.c | 50 +++---- src/lindbergh/graphics.c | 43 +++--- src/lindbergh/jvs.c | 10 +- src/lindbergh/passthrough.c | 258 ++++++++++++++++++++++++++++++++++++ src/lindbergh/passthrough.h | 20 +++ 6 files changed, 334 insertions(+), 50 deletions(-) create mode 100644 src/lindbergh/passthrough.c create mode 100644 src/lindbergh/passthrough.h diff --git a/.vscode/settings.json b/.vscode/settings.json index 66d166a..71a6611 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "time.h": "c", "stdarg.h": "c", "ioctl.h": "c", - "segaeax.h": "c" + "segaeax.h": "c", + "passthrough.h": "c" } } diff --git a/src/lindbergh/baseboard.c b/src/lindbergh/baseboard.c index 43e1478..970611a 100644 --- a/src/lindbergh/baseboard.c +++ b/src/lindbergh/baseboard.c @@ -1,27 +1,16 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include /* Standard input/output definitions */ +#include /* String function definitions */ +#include /* UNIX standard function definitions */ +#include /* File control definitions */ +#include /* Error number definitions */ +#include /* Standard library functions like malloc, free, exit, and atoi */ #include "baseboard.h" #include "config.h" #include "jvs.h" #include "serial.h" +#include "passthrough.h" #define SERIAL_STRING "FE11-X018012022X" @@ -83,21 +72,21 @@ int initBaseboard() 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); + jvsFileDescriptor = openJVSSerial(getConfig()->jvsPath); if (jvsFileDescriptor < 0) { printf("Error: Failed to open %s for JVS\n", getConfig()->jvsPath); + return -1; } - setSerialAttributes(jvsFileDescriptor, B115200); + + initJVSSerial(jvsFileDescriptor); + startJVSFrameThread(&jvsFileDescriptor); } return 0; @@ -206,11 +195,9 @@ int baseboardIoctl(int fd, unsigned int request, void *data) } else if (jvsFileDescriptor >= 0) { - - write(jvsFileDescriptor, inputBuffer, jvsCommand.srcSize); - for (int i = 0; i < jvsCommand.srcSize; i++) { + write(jvsFileDescriptor, &inputBuffer[i], 1); if (inputBuffer[i] == 0xF0) { setSenseLine(3); @@ -270,14 +257,11 @@ int baseboardIoctl(int fd, unsigned int request, void *data) } else if (jvsFileDescriptor >= 0) { - int count = readBytes(jvsFileDescriptor, &sharedMemory[jvsCommand.destAddress], 255); - - if (count == -1) - count = 0; - + JVSFrame frame = readJVSFrameFromThread(); + memcpy(&sharedMemory[jvsCommand.destAddress], frame.buffer, frame.size); _data[2] = jvsCommand.destAddress; - _data[3] = count; - _data[1] = (count > 0); // Success if we receive any sort of data back + _data[3] = frame.size; + _data[1] = frame.ready; } } break; diff --git a/src/lindbergh/graphics.c b/src/lindbergh/graphics.c index a1c5b2b..104da34 100644 --- a/src/lindbergh/graphics.c +++ b/src/lindbergh/graphics.c @@ -88,7 +88,7 @@ FGAPI void FGAPIENTRY glutGameModeString(const char *string) Window XCreateWindow(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valueMask, XSetWindowAttributes *attributes) { - Window (*_XCreateWindow)(Display * display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valueMask, XSetWindowAttributes *attributes) = dlsym(RTLD_NEXT, "XCreateWindow"); + Window (*_XCreateWindow)(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int class, Visual *visual, unsigned long valueMask, XSetWindowAttributes *attributes) = dlsym(RTLD_NEXT, "XCreateWindow"); width = getConfig()->width; height = getConfig()->height; @@ -112,7 +112,7 @@ Window XCreateWindow(Display *display, Window parent, int x, int y, unsigned int int XGrabPointer(Display *display, Window grab_window, Bool owner_events, unsigned int event_mask, int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor, Time time) { - int (*_XGrabPointer)(Display * display, Window grab_window, Bool owner_events, unsigned int event_mask, int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor, Time time) = dlsym(RTLD_NEXT, "XGrabPointer"); + int (*_XGrabPointer)(Display *display, Window grab_window, Bool owner_events, unsigned int event_mask, int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor, Time time) = dlsym(RTLD_NEXT, "XGrabPointer"); int returnValue = _XGrabPointer(display, grab_window, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor, time); XUngrabPointer(display, time); return returnValue; @@ -120,7 +120,7 @@ int XGrabPointer(Display *display, Window grab_window, Bool owner_events, unsign int XGrabKeyboard(Display *display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time) { - int (*_XGrabKeyboard)(Display * display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time) = dlsym(RTLD_NEXT, "XGrabKeyboard"); + int (*_XGrabKeyboard)(Display *display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time) = dlsym(RTLD_NEXT, "XGrabKeyboard"); int returnValue = _XGrabKeyboard(display, grab_window, owner_events, pointer_mode, keyboard_mode, time); XUngrabKeyboard(display, time); return returnValue; @@ -133,7 +133,7 @@ int XDefineCursor(Display *display, Window w, Cursor cursor) int XStoreName(Display *display, Window w, const char *window_name) { - int (*_XStoreName)(Display * display, Window w, const char *window_name) = dlsym(RTLD_NEXT, "XStoreName"); + int (*_XStoreName)(Display *display, Window w, const char *window_name) = dlsym(RTLD_NEXT, "XStoreName"); char gameTitle[256] = {0}; strcat(gameTitle, getGameName()); strcat(gameTitle, " (X11)"); @@ -143,8 +143,15 @@ int XStoreName(Display *display, Window w, const char *window_name) int XNextEvent(Display *display, XEvent *event_return) { - int (*_XNextEvent)(Display * display, XEvent * event_return) = dlsym(RTLD_NEXT, "XNextEvent"); + int (*_XNextEvent)(Display *display, XEvent *event_return) = dlsym(RTLD_NEXT, "XNextEvent"); int returnValue = _XNextEvent(display, event_return); + + // Return now if we're not emulating JVS + if (!getConfig()->emulateJVS) + { + return returnValue; + } + switch (event_return->type) { @@ -231,7 +238,7 @@ int XNextEvent(Display *display, XEvent *event_return) int XSetStandardProperties(Display *display, Window window, const char *window_name, const char *icon_name, Pixmap icon_pixmap, char **argv, int argc, XSizeHints *hints) { - int (*_XSetStandardProperties)(Display * display, Window window, const char *window_name, const char *icon_name, Pixmap icon_pixmap, char **argv, int argc, XSizeHints *hints) = dlsym(RTLD_NEXT, "XSetStandardProperties"); + int (*_XSetStandardProperties)(Display *display, Window window, const char *window_name, const char *icon_name, Pixmap icon_pixmap, char **argv, int argc, XSizeHints *hints) = dlsym(RTLD_NEXT, "XSetStandardProperties"); char gameTitle[256] = {0}; strcat(gameTitle, getGameName()); strcat(gameTitle, " (X11)"); @@ -245,30 +252,36 @@ Bool XF86VidModeSwitchToMode(Display *display, int screen, XF86VidModeModeInfo * typedef unsigned int uint; -int glXSwapIntervalSGI(int interval) { +int glXSwapIntervalSGI(int interval) +{ return 0; } -int glXGetVideoSyncSGI(uint *count) { +int glXGetVideoSyncSGI(uint *count) +{ static unsigned int frameCount = 0; - //TODO: Framecount should depend on current system time - *count = (frameCount++)/2; // NOTE: Keeps the same frame for 2 calls + // TODO: Framecount should depend on current system time + *count = (frameCount++) / 2; // NOTE: Keeps the same frame for 2 calls return 0; } -int glXGetRefreshRateSGI(unsigned int * rate) { //TODO: need an actual prototype - *rate = 60; //TODO: what does this function return? +int glXGetRefreshRateSGI(unsigned int *rate) +{ // TODO: need an actual prototype + *rate = 60; // TODO: what does this function return? return 0; } -void glGenFencesNV(int n, uint *fences) { +void glGenFencesNV(int n, uint *fences) +{ static unsigned int curf = 1; - while(n--) { + while (n--) + { *fences++ = curf++; } return; } -void glDeleteFencesNV(int a, const uint *b) { +void glDeleteFencesNV(int a, const uint *b) +{ return; } \ No newline at end of file diff --git a/src/lindbergh/jvs.c b/src/lindbergh/jvs.c index 314cbe8..bdd8208 100644 --- a/src/lindbergh/jvs.c +++ b/src/lindbergh/jvs.c @@ -598,6 +598,13 @@ JVSStatus writePacket(JVSPacket *packet) return JVS_STATUS_SUCCESS; } +/** + * Gets the sense line value + * + * Values are: + * 3 = no device, after a RESET + * 1 = address assigned + */ int getSenseLine() { return senseLine; @@ -635,6 +642,7 @@ int setAnalogue(JVSInput channel, int value) return 1; } -void setSenseLine(int _senseLine) { +void setSenseLine(int _senseLine) +{ senseLine = _senseLine; } diff --git a/src/lindbergh/passthrough.c b/src/lindbergh/passthrough.c new file mode 100644 index 0000000..472b9bc --- /dev/null +++ b/src/lindbergh/passthrough.c @@ -0,0 +1,258 @@ +#include /* Standard input/output definitions */ +#include /* String function definitions */ +#include /* UNIX standard function definitions */ +#include /* File control definitions */ +#include /* Error number definitions */ +#include /* POSIX terminal control definitions */ +#include /* Ioctl function to control device drivers in the kernel */ +#include /* Standard library functions like malloc, free, exit, and atoi */ +#include /* POSIX threads API to create and manage threads in the program */ + +#include "passthrough.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) { + printf("Failed to open '%s' for JVS.\n", 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) { + printf("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); + // printf("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); + memcpy(jvsFrameBuffer.buffer, localBuffer, byteCount); + jvsFrameBuffer.size = byteCount; + jvsFrameBuffer.ready = 1; + 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); + printf("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) + { + printf("SERIAL: Failed to create reader thread\n"); + 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; +} \ No newline at end of file diff --git a/src/lindbergh/passthrough.h b/src/lindbergh/passthrough.h new file mode 100644 index 0000000..3a8a394 --- /dev/null +++ b/src/lindbergh/passthrough.h @@ -0,0 +1,20 @@ +#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);