From d129b535619cba295049b44d250a442e78a78062 Mon Sep 17 00:00:00 2001 From: Cube Date: Tue, 21 Jun 2022 13:58:21 -0700 Subject: [PATCH] LXIO Support: This is mostly Din's work. piuio and piubtn are both included in the piuio/lxio lib To use in games which contain a button board specify the same module for both piuio and piubtn configs. --- Package.mk | 1 + cmake/src/main/io/CMakeLists.txt | 1 + cmake/src/main/io/lxio/CMakeLists.txt | 14 + cmake/src/main/ptapi/piuio/CMakeLists.txt | 1 + .../src/main/ptapi/piuio/lxio/CMakeLists.txt | 15 + src/main/io/lxio/defs.h | 15 + src/main/io/lxio/device.c | 89 ++++++ src/main/io/lxio/device.h | 41 +++ src/main/ptapi/io/piuio/lxio/lxio.c | 268 ++++++++++++++++++ 9 files changed, 445 insertions(+) create mode 100644 cmake/src/main/io/lxio/CMakeLists.txt create mode 100644 cmake/src/main/ptapi/piuio/lxio/CMakeLists.txt create mode 100644 src/main/io/lxio/defs.h create mode 100644 src/main/io/lxio/device.c create mode 100644 src/main/io/lxio/device.h create mode 100644 src/main/ptapi/io/piuio/lxio/lxio.c diff --git a/Package.mk b/Package.mk index 971628b..1e8af76 100644 --- a/Package.mk +++ b/Package.mk @@ -147,6 +147,7 @@ $(zipdir)/piuio.zip: \ $(builddir)/bin/ptapi-io-piuio-keyboard-conf \ $(builddir)/bin/ptapi-io-piuio-null.so \ $(builddir)/bin/ptapi-io-piuio-real.so \ + $(builddir)/bin/ptapi-io-piuio-lxio.so \ $(builddir)/bin/ptapi-io-piuio-test \ dist/api/ptapi-io-piuio-stub.c \ | $(zipdir)/ diff --git a/cmake/src/main/io/CMakeLists.txt b/cmake/src/main/io/CMakeLists.txt index ee6ad67..c62dc06 100644 --- a/cmake/src/main/io/CMakeLists.txt +++ b/cmake/src/main/io/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(piubtn) add_subdirectory(piuio) +add_subdirectory(lxio) add_subdirectory(util) \ No newline at end of file diff --git a/cmake/src/main/io/lxio/CMakeLists.txt b/cmake/src/main/io/lxio/CMakeLists.txt new file mode 100644 index 0000000..8dc35fc --- /dev/null +++ b/cmake/src/main/io/lxio/CMakeLists.txt @@ -0,0 +1,14 @@ +project(io-lxio) +message(STATUS "Project " ${PROJECT_NAME}) + +set(SRC ${PT_ROOT_MAIN}/io/lxio) + +set(SOURCE_FILES + ${SRC}/device.c) + +add_library(${PROJECT_NAME} STATIC ${SOURCE_FILES}) +add_library(lxio SHARED ${SOURCE_FILES}) + +set_target_properties(lxio PROPERTIES PREFIX "") + +target_link_libraries(${PROJECT_NAME} io-util util usb-1.0) \ No newline at end of file diff --git a/cmake/src/main/ptapi/piuio/CMakeLists.txt b/cmake/src/main/ptapi/piuio/CMakeLists.txt index b62b082..86b0086 100644 --- a/cmake/src/main/ptapi/piuio/CMakeLists.txt +++ b/cmake/src/main/ptapi/piuio/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(joystick-util) add_subdirectory(keyboard-conf) add_subdirectory(keyboard) add_subdirectory(keyboard-util) +add_subdirectory(lxio) add_subdirectory(null) add_subdirectory(real) add_subdirectory(test) diff --git a/cmake/src/main/ptapi/piuio/lxio/CMakeLists.txt b/cmake/src/main/ptapi/piuio/lxio/CMakeLists.txt new file mode 100644 index 0000000..acea142 --- /dev/null +++ b/cmake/src/main/ptapi/piuio/lxio/CMakeLists.txt @@ -0,0 +1,15 @@ +project(ptapi-io-piuio-lxio) +message(STATUS "Project " ${PROJECT_NAME}) + +set(SRC ${PT_ROOT_MAIN}/ptapi/io/piuio/lxio) + +set(SOURCE_FILES + ${SRC}/lxio.c) + +add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES}) + +set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-fPIC") +# Remove library name "lib" prefix +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") + +target_link_libraries(${PROJECT_NAME} io-lxio io-util util usb-1.0) \ No newline at end of file diff --git a/src/main/io/lxio/defs.h b/src/main/io/lxio/defs.h new file mode 100644 index 0000000..c3b8ec1 --- /dev/null +++ b/src/main/io/lxio/defs.h @@ -0,0 +1,15 @@ +#ifndef LXIO_DRV_DEFS_H +#define LXIO_DRV_DEFS_H + +#define LXIO_VID 0x0D2F +#define LXIO_PID 0x1020 + +#define LXIO_DRV_USB_REQ_TIMEOUT 10000 + +// NOTE: usb.c takes care of setting the MSB for the endpoint address. +#define LXIO_ENDPOINT_INPUT 0x81 +#define LXIO_ENDPOINT_OUT 0x02 + +#define LXIO_MSG_SIZE 16 + +#endif \ No newline at end of file diff --git a/src/main/io/lxio/device.c b/src/main/io/lxio/device.c new file mode 100644 index 0000000..2ed1998 --- /dev/null +++ b/src/main/io/lxio/device.c @@ -0,0 +1,89 @@ +#define LOG_MODULE "lxio-drv-device" + +#include "io/util/usb_.h" + +#include "util/log.h" + +#include "defs.h" +#include "device.h" + +static void *lxio_drv_device_handle; + +bool lxio_drv_device_open(void) +{ + if (lxio_drv_device_handle) { + log_warn("LXIO Device already opened"); + return true; + } + + lxio_drv_device_handle = io_usb_open(LXIO_VID, LXIO_PID, 1, 0); + + return lxio_drv_device_handle; +} + +bool lxio_drv_device_read(uint8_t *buffer, uint8_t len) +{ + int32_t res; + + if (!lxio_drv_device_handle) { + log_error("Device not opened"); + return false; + } + + if (len < LXIO_MSG_SIZE) { + log_error("Read failed, buffer (%d) too small", len); + return false; + } + + res = io_usb_interrupt_transfer( + lxio_drv_device_handle, + LXIO_ENDPOINT_INPUT, + buffer, + LXIO_MSG_SIZE, + LXIO_DRV_USB_REQ_TIMEOUT); + + if (res != LXIO_MSG_SIZE) { + log_error("Read failed: %d", res); + return false; + } + + return true; +} + +bool lxio_drv_device_write(uint8_t *buffer, uint8_t len) +{ + int32_t res; + + if (!lxio_drv_device_handle) { + log_error("Device not opened"); + return false; + } + + if (len < LXIO_MSG_SIZE) { + log_error("Write failed, buffer (%d) too small", len); + return false; + } + + res = io_usb_interrupt_transfer( + lxio_drv_device_handle, + LXIO_ENDPOINT_OUT, + buffer, + LXIO_MSG_SIZE, + LXIO_DRV_USB_REQ_TIMEOUT); + + if (res != LXIO_MSG_SIZE) { + log_error("Write failed: %d", res); + return false; + } + + return true; +} + +void lxio_drv_device_close(void) +{ + if (!lxio_drv_device_handle) { + log_error("Device not opened"); + } + + io_usb_close(lxio_drv_device_handle); +} \ No newline at end of file diff --git a/src/main/io/lxio/device.h b/src/main/io/lxio/device.h new file mode 100644 index 0000000..1c4be6e --- /dev/null +++ b/src/main/io/lxio/device.h @@ -0,0 +1,41 @@ +/** + * Module for sending and recv raw data to the "lxio"/atmegapump on the + * interrupt endpoint. + */ +#ifndef LXIO_DRV_DEVICE_H +#define LXIO_DRV_DEVICE_H + +#include +#include + +/** + * Open a connected lxio device + * + * @return True if opening a lxio device was successful, false on failure + */ +bool lxio_drv_device_open(void); + +/** + * Read raw data from the lxio device + * + * @param buffer Allocated buffer to read data from the device into + * @param len Number of bytes to read + * @return True if reading data to device was successful, false otherwise + */ +bool lxio_drv_device_read(uint8_t *buffer, uint8_t len); + +/** + * Write raw data to the lxio device + * + * @param buffer Buffer with data to write + * @param len Number of bytes to write + * @return True if writing data to device was successful, false otherwise + */ +bool lxio_drv_device_write(uint8_t *buffer, uint8_t len); + +/** + * Close the opened lxio device + */ +void lxio_drv_device_close(void); + +#endif \ No newline at end of file diff --git a/src/main/ptapi/io/piuio/lxio/lxio.c b/src/main/ptapi/io/piuio/lxio/lxio.c new file mode 100644 index 0000000..9f3ba2c --- /dev/null +++ b/src/main/ptapi/io/piuio/lxio/lxio.c @@ -0,0 +1,268 @@ +/** + * Communicate with the Andamiro "lxio" or ATMEGAPUMP or PIU HID. + * This device hosts all data in two address, and interrupt in and out. + * Device needs to be written then read in that order. Every read needs a preceding write. + */ +#define LOG_MODULE "ptapi-io-piuio-lxio" + +#include +#include + +#include "io/lxio/defs.h" +#include "io/lxio/device.h" + +#include "util/log.h" + +#include "ptapi/io/piuio.h" +#include "ptapi/io/piubtn.h" + +// Data going to/from lxio +uint8_t lxio_btn_state_buffer[LXIO_MSG_SIZE]; +uint8_t lxio_light_state_buffer[LXIO_MSG_SIZE]; + +// data going to/from the ptapi caller +static struct ptapi_io_piuio_pad_inputs + ptapi_piuio_pad_in[2][PTAPI_IO_PIUIO_SENSOR_GROUP_NUM]; +static struct ptapi_io_piuio_sys_inputs ptapi_piuio_sys; +static struct ptapi_io_piuio_pad_outputs ptapi_piuio_pad_out[2]; +static struct ptapi_io_piuio_cab_outputs ptapi_piuio_cab_out; +static struct ptapi_io_piubtn_outputs ptapi_piubtn_out[2]; + +const char *ptapi_io_piuio_ident(void) +{ + return "lxio"; +} + +bool ptapi_io_piuio_open(void) +{ + return lxio_drv_device_open(); +} + +void ptapi_io_piuio_close(void) +{ + lxio_drv_device_close(); +} + +void convert_output_to_lxio() +{ + memset(lxio_light_state_buffer, 0, sizeof(lxio_light_state_buffer)); + + // light mapping follows the same format as the piuio + // no sense in changing something if it's already set -andamiro probably. + + /* Pad lighting state. */ + for (uint8_t i = 0; i < 2; i++) { + if (ptapi_piuio_pad_out[i].lu) { + lxio_light_state_buffer[i * 2] |= (1 << 2); + } + + if (ptapi_piuio_pad_out[i].ru) { + lxio_light_state_buffer[i * 2] |= (1 << 3); + } + + if (ptapi_piuio_pad_out[i].cn) { + lxio_light_state_buffer[i * 2] |= (1 << 4); + } + + if (ptapi_piuio_pad_out[i].ld) { + lxio_light_state_buffer[i * 2] |= (1 << 5); + } + + if (ptapi_piuio_pad_out[i].rd) { + lxio_light_state_buffer[i * 2] |= (1 << 6); + } + } + + /* Neons/Bass */ + if (ptapi_piuio_cab_out.bass) { + lxio_light_state_buffer[1] |= (1 << 2); + } + + /* Halogens */ + + if (ptapi_piuio_cab_out.halo_r1) { + lxio_light_state_buffer[3] |= (1 << 0); + } + + if (ptapi_piuio_cab_out.halo_l2) { + lxio_light_state_buffer[3] |= (1 << 1); + } + + if (ptapi_piuio_cab_out.halo_l1) { + lxio_light_state_buffer[3] |= (1 << 2); + } + + /* Menu Buttons */ + + for (uint8_t i = 0; i < 2; i++) { + //both UR and UL are mapped to "back" + if (ptapi_piubtn_out[i].back) { + lxio_light_state_buffer[i+4] |= (1 << 0); + lxio_light_state_buffer[i+4] |= (1 << 1); + } + if (ptapi_piubtn_out[i].start) { + lxio_light_state_buffer[i+4] |= (1 << 2); + } + if (ptapi_piubtn_out[i].left) { + lxio_light_state_buffer[i+4] |= (1 << 3); + } + if (ptapi_piubtn_out[i].right) { + lxio_light_state_buffer[i+4] |= (1 << 4); + } + } +} + +static void convert_input_to_piuio() +{ + // LXIO sends all four sensor positions (the entire pad state 10*4) at once. + // Each byte is a sensor position, so we need to take a look at the bit flags. + // P1 is bytes 0->3 and P2 is 4->7 inclusive + // and LXIO follows the same order as PTAPI/PIUIO in sensor order + // that being lu, ru, cn, ld, rd (index: 0,1,2,3,4) + for (uint8_t i = 0; i < 2; i++) { + for (uint8_t j = 0; j < PTAPI_IO_PIUIO_SENSOR_GROUP_NUM; j++) { + ptapi_piuio_pad_in[i][j].lu = lxio_btn_state_buffer[i * 4 + j] & (1 << 0); + ptapi_piuio_pad_in[i][j].ru = lxio_btn_state_buffer[i * 4 + j] & (1 << 1); + ptapi_piuio_pad_in[i][j].cn = lxio_btn_state_buffer[i * 4 + j] & (1 << 2); + ptapi_piuio_pad_in[i][j].ld = lxio_btn_state_buffer[i * 4 + j] & (1 << 3); + ptapi_piuio_pad_in[i][j].rd = lxio_btn_state_buffer[i * 4 + j] & (1 << 4); + } + } + + // cabinet buttons + ptapi_piuio_sys.test = lxio_btn_state_buffer[8] & (1 << 1); + ptapi_piuio_sys.service = lxio_btn_state_buffer[8] & (1 << 6); + ptapi_piuio_sys.clear = lxio_btn_state_buffer[8] & (1 << 7); + ptapi_piuio_sys.coin = lxio_btn_state_buffer[8] & (1 << 2); + ptapi_piuio_sys.coin2 = lxio_btn_state_buffer[9] & (1 << 2); + +} + +bool ptapi_io_piuio_recv(void) +{ + // NOTE: The lxio *MUST* be written to in order to be read. + // It cannot be read individually without a written payload. It will soft + // lock. In order to ensure the full cycle, everything is taken care of here. + + // take the ptapi info and convert it to an lxio message. + convert_output_to_lxio(); + + if (!lxio_drv_device_write(lxio_light_state_buffer, LXIO_MSG_SIZE)) { + return false; + } + + if (!lxio_drv_device_read(lxio_btn_state_buffer, LXIO_MSG_SIZE)) { + return false; + } + + // Note: lxio contains an unsigned 16bit packet counter in the last two LSB + // bytes, we can check that for errors....or not. libusb will tell us. + + // input from lxio is active low, so invert the recv data for active high + // logic in ptapi. + for (uint8_t j = 0; j < LXIO_MSG_SIZE; j++) { + lxio_btn_state_buffer[j] ^= 0xFF; + } + + // take the lxio message and convert it to ptapi. + convert_input_to_piuio(); + + return true; +} + +bool ptapi_io_piuio_send(void) +{ + // taken care of in recv + return true; +} + +void ptapi_io_piuio_get_input_pad( + uint8_t player, + enum ptapi_io_piuio_sensor_group sensor_group, + struct ptapi_io_piuio_pad_inputs *inputs) +{ + memcpy( + inputs, + &ptapi_piuio_pad_in[player][sensor_group], + sizeof(struct ptapi_io_piuio_pad_inputs)); +} + +void ptapi_io_piuio_get_input_sys(struct ptapi_io_piuio_sys_inputs *inputs) +{ + memcpy(inputs, &ptapi_piuio_sys, sizeof(struct ptapi_io_piuio_sys_inputs)); +} + +void ptapi_io_piuio_set_output_pad( + uint8_t player, const struct ptapi_io_piuio_pad_outputs *outputs) +{ + memcpy( + &ptapi_piuio_pad_out[player], + outputs, + sizeof(struct ptapi_io_piuio_pad_outputs)); +} + +void ptapi_io_piuio_set_output_cab( + const struct ptapi_io_piuio_cab_outputs *outputs) +{ + memcpy( + &ptapi_piuio_cab_out, outputs, sizeof(struct ptapi_io_piuio_cab_outputs)); +} + +const char* ptapi_io_piubtn_ident(void) +{ + return "lxio"; +} + +bool ptapi_io_piubtn_open(void) +{ + //taken care of by piuio_open + return true; +} + +void ptapi_io_piubtn_close(void) +{ + //handled by piuio_close +} + +bool ptapi_io_piubtn_recv(void) +{ + ptapi_io_piuio_recv(); + return true; +} + +bool ptapi_io_piubtn_send(void) +{ + // taken care of in piuio_send + return true; +} + +void ptapi_io_piubtn_get_input(uint8_t player, struct ptapi_io_piubtn_inputs* inputs) +{ + // Note: There could be consistancy issues if the two IOs are threaded by seperate threads + // Decided to take the risk of potentially lost button inputs. Testing has yielded + // good results. + + // Set all inputs to off + if (player == 0) { + inputs->left = lxio_btn_state_buffer[10] & (1 << 3); + inputs->right = lxio_btn_state_buffer[10] & (1 << 4); + inputs->start = lxio_btn_state_buffer[10] & (1 << 2); + + //map both UR and UL to "back" as this is how kpump maps it. + inputs->back = (lxio_btn_state_buffer[10] & (1 << 0)) | + (lxio_btn_state_buffer[10] & (1 << 1)); + } else { + inputs->left = lxio_btn_state_buffer[11] & (1 << 3); + inputs->right = lxio_btn_state_buffer[11] & (1 << 4); + inputs->start = lxio_btn_state_buffer[11] & (1 << 2); + + //map both UR and UL to "back" as this is how kpump maps it. + inputs->back = (lxio_btn_state_buffer[11] & (1 << 0)) | + (lxio_btn_state_buffer[11] & (1 << 1)); + } +} + +void ptapi_io_piubtn_set_output(uint8_t player, const struct ptapi_io_piubtn_outputs* outputs) +{ + memcpy(&ptapi_piubtn_out[player], outputs, sizeof(struct ptapi_io_piubtn_outputs)); +}