1
0
mirror of https://github.com/pumpitupdev/pumptools.git synced 2024-11-27 16:10:55 +01:00

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.
This commit is contained in:
Cube 2022-06-21 13:58:21 -07:00 committed by voidderef
parent bae69dc090
commit d129b53561
9 changed files with 445 additions and 0 deletions

View File

@ -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)/

View File

@ -1,3 +1,4 @@
add_subdirectory(piubtn)
add_subdirectory(piuio)
add_subdirectory(lxio)
add_subdirectory(util)

View File

@ -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)

View File

@ -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)

View File

@ -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)

15
src/main/io/lxio/defs.h Normal file
View File

@ -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

89
src/main/io/lxio/device.c Normal file
View File

@ -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);
}

41
src/main/io/lxio/device.h Normal file
View File

@ -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 <stdbool.h>
#include <stdint.h>
/**
* 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

View File

@ -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 <stdlib.h>
#include <string.h>
#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));
}