mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2025-02-17 19:19:16 +01:00
feat(extiodrv): Add (DDR) EXTIO driver
Initial version that supports the most important features to operate a DDR SD cabinet with all inputs and outputs working. Note that the individual sensor cycling modes are currently broken. The driver will always set the sensor read mode to all for now.
This commit is contained in:
parent
2bbbfccd13
commit
db2d71e13c
5
src/main/extiodrv/Module.mk
Normal file
5
src/main/extiodrv/Module.mk
Normal file
@ -0,0 +1,5 @@
|
||||
libs += extiodrv
|
||||
|
||||
src_extiodrv := \
|
||||
device.c \
|
||||
extio.c \
|
182
src/main/extiodrv/device.c
Normal file
182
src/main/extiodrv/device.c
Normal file
@ -0,0 +1,182 @@
|
||||
#define LOG_MODULE "extiodrv-device"
|
||||
|
||||
#include "device.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
HRESULT extiodrv_device_open(const char *port, HANDLE *handle)
|
||||
{
|
||||
HRESULT hr;
|
||||
COMMTIMEOUTS ct;
|
||||
DCB dcb;
|
||||
|
||||
log_assert(port);
|
||||
log_assert(handle);
|
||||
|
||||
log_info("Opening extio on %s", port);
|
||||
|
||||
*handle = CreateFile(
|
||||
port,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
if (*handle == INVALID_HANDLE_VALUE) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("Failed to open %s: %lX", port, hr);
|
||||
|
||||
goto early_fail;
|
||||
}
|
||||
|
||||
if (!SetCommMask(*handle, EV_RXCHAR)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("SetCommMask failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetupComm(*handle, 0x4000u, 0x4000u)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("SetupComm failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!PurgeComm(
|
||||
*handle,
|
||||
PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("PurgeComm failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ct.ReadTotalTimeoutConstant = 0;
|
||||
ct.WriteTotalTimeoutConstant = 0;
|
||||
ct.ReadIntervalTimeout = -1;
|
||||
ct.ReadTotalTimeoutMultiplier = 10;
|
||||
ct.WriteTotalTimeoutMultiplier = 100;
|
||||
|
||||
if (!SetCommTimeouts(*handle, &ct)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("SetCommTimeouts failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcb.DCBlength = sizeof(dcb);
|
||||
|
||||
if (!GetCommState(*handle, &dcb)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("GetCommState failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcb.BaudRate = 38400;
|
||||
dcb.fBinary = TRUE;
|
||||
dcb.fParity = FALSE;
|
||||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
dcb.fDsrSensitivity = FALSE;
|
||||
dcb.fOutX = FALSE;
|
||||
dcb.fInX = FALSE;
|
||||
dcb.fErrorChar = FALSE;
|
||||
dcb.fNull = FALSE;
|
||||
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
dcb.fAbortOnError = FALSE;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
dcb.XonChar = 17;
|
||||
dcb.XoffChar = 19;
|
||||
dcb.XonLim = 100;
|
||||
dcb.XoffLim = 100;
|
||||
|
||||
if (!SetCommState(*handle, &dcb)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("SetCommState failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_info("[%p] Opened extio device on %s", *handle, port);
|
||||
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
CloseHandle(*handle);
|
||||
*handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
early_fail:
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT extiodrv_device_close(HANDLE *handle)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(handle);
|
||||
|
||||
if (CloseHandle(*handle) == FALSE) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hr;
|
||||
}
|
||||
|
||||
*handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT extiodrv_device_read(
|
||||
HANDLE handle, void *bytes, size_t nbytes, size_t *read_bytes)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD dw_read_bytes;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(bytes);
|
||||
log_assert(read_bytes);
|
||||
|
||||
if (!ClearCommError(handle, NULL, NULL)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("[%p] ClearCommError failed: %lX", handle, hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (!ReadFile(handle, bytes, nbytes, &dw_read_bytes, NULL)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("[%p] ReadFile failed: %lX", handle, hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
*read_bytes = dw_read_bytes;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT extiodrv_device_write(
|
||||
HANDLE handle, const void *bytes, size_t nbytes, size_t *written_bytes)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD dw_written_bytes;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(bytes);
|
||||
log_assert(written_bytes);
|
||||
|
||||
if (!WriteFile(handle, bytes, nbytes, &dw_written_bytes, NULL)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("[%p] WriteFile failed: %lX", handle, hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
*written_bytes = dw_written_bytes;
|
||||
|
||||
return S_OK;
|
||||
}
|
57
src/main/extiodrv/device.h
Normal file
57
src/main/extiodrv/device.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef EXTIODRV_DEVICE_H
|
||||
#define EXTIODRV_DEVICE_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Open an EXTIO device connected to a com port.
|
||||
*
|
||||
* @param port Com port the device is connected to, e.g. "COM1"
|
||||
* @param handle Pointer to a HANDLE variable to return the handle to when
|
||||
* succesfully opened
|
||||
* @return S_OK on success with the handle returned in handle, other HRESULT
|
||||
* value on error.
|
||||
*/
|
||||
HRESULT extiodrv_device_open(const char *port, HANDLE *handle);
|
||||
|
||||
/**
|
||||
* Close an opened EXTIO device
|
||||
*
|
||||
* @param handle Pointer to a handle variable that stores a valid and opened
|
||||
* EXTIO handle.
|
||||
* @return S_OK on success and the handle value is set to INVALID_HANDLE_VALUE,
|
||||
* other HRESULT value on error.
|
||||
*/
|
||||
HRESULT extiodrv_device_close(HANDLE *handle);
|
||||
|
||||
/**
|
||||
* Read data from the EXTIO device. This call blocks until data is available.
|
||||
*
|
||||
* @param handle A valid handle to an opened EXTIO device.
|
||||
* @param bytes Pointer to a buffer to read the data into
|
||||
* @param nbytes (Maximum) number of bytes to read
|
||||
* @param read_bytes Pointer to a variable to store the resulting read size to
|
||||
* @return S_OK on success with read_bytes populated with the number of bytes
|
||||
* read, other HRESULT value on error.
|
||||
*/
|
||||
HRESULT extiodrv_device_read(
|
||||
HANDLE handle, void *bytes, size_t nbytes, size_t *read_bytes);
|
||||
|
||||
/**
|
||||
* WRite data to the EXTIO device. This call blocks until the data is written.
|
||||
*
|
||||
* @param handle A valid handle to an opened EXTIO device.
|
||||
* @param bytes Pointer to a buffer with data to write to the device
|
||||
* @param nbytes (Maximum) number of bytes to write
|
||||
* @param written_bytes Pointer to a variable to store the resulting write size
|
||||
* to
|
||||
* @return S_OK on success with written_bytes populated with the number of bytes
|
||||
* written, other HRESULT value on error.
|
||||
*/
|
||||
HRESULT extiodrv_device_write(
|
||||
HANDLE handle, const void *bytes, size_t nbytes, size_t *written_bytes);
|
||||
|
||||
#endif
|
110
src/main/extiodrv/extio.c
Normal file
110
src/main/extiodrv/extio.c
Normal file
@ -0,0 +1,110 @@
|
||||
#define LOG_MODULE "extiodrv-extio"
|
||||
|
||||
#include "extio.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
// static uint8_t _extiodrv_extio_sensor_read_mode_map[5] = {
|
||||
// 1, // all
|
||||
// 2, // up
|
||||
// 3, // down
|
||||
// 4, // left
|
||||
// 5, // right
|
||||
// };
|
||||
|
||||
HRESULT extiodrv_extio_transfer(
|
||||
HANDLE handle,
|
||||
enum extiodrv_extio_sensor_read_mode sensor_read_mode,
|
||||
const struct extiodrv_extio_pad_lights
|
||||
pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS],
|
||||
bool neons)
|
||||
{
|
||||
HRESULT hr;
|
||||
struct extio_cmd_write write;
|
||||
struct extio_cmd_read read;
|
||||
size_t bytes_written;
|
||||
size_t bytes_read;
|
||||
const uint8_t *raw;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
memset(&write, 0, sizeof(write));
|
||||
|
||||
// TODO this doesn't seem to work, yet
|
||||
// write.sensor_read_mode =
|
||||
// 0;//_extiodrv_extio_sensor_read_mode_map[sensor_read_mode]; 0 = all 1 =
|
||||
// mess? 2 = mess? 3 = mess? 4 = mess? 5 = mess? 6 = right sensor only 7 =
|
||||
// right sensor only 8 = right sensor only
|
||||
write.sensor_read_mode = 0;
|
||||
|
||||
for (uint8_t i = 0; i < EXTIO_PAD_LIGHT_MAX_PLAYERS; i++) {
|
||||
write.pad_lights[i].up = pad_lights[i].up ? 1 : 0;
|
||||
write.pad_lights[i].down = pad_lights[i].down ? 1 : 0;
|
||||
write.pad_lights[i].left = pad_lights[i].left ? 1 : 0;
|
||||
write.pad_lights[i].right = pad_lights[i].right ? 1 : 0;
|
||||
}
|
||||
|
||||
write.neons = neons ? 1 : 0;
|
||||
|
||||
// This MUST be set but only on the p1 packet
|
||||
// Not setting it or also setting it on the p2 packet results in the EXTIO
|
||||
// not responding at all
|
||||
write.pad_lights[0].unknown_80 = 1;
|
||||
|
||||
write.checksum = extio_cmd_checksum(&write);
|
||||
|
||||
raw = (uint8_t *) &write;
|
||||
|
||||
log_misc("Raw write paket: %X %X %X %X", raw[0], raw[1], raw[2], raw[3]);
|
||||
|
||||
hr = extiodrv_device_write(handle, &write, sizeof(write), &bytes_written);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning("Writing extio device failed");
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (bytes_written != sizeof(write)) {
|
||||
log_warning(
|
||||
"Writing extio device failed, expected bytes %d != actual bytes %d",
|
||||
(uint32_t) sizeof(write),
|
||||
(uint32_t) bytes_written);
|
||||
return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH);
|
||||
}
|
||||
|
||||
// Read loop to keep reading until a full message is read
|
||||
// There are various occasions, especially if polling from the host side is
|
||||
// faster than the device can put the response to the wire. this can result
|
||||
// in empty reads
|
||||
while (true) {
|
||||
hr = extiodrv_device_read(handle, &read, sizeof(read), &bytes_read);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning("Reading extio device failed");
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (bytes_read < sizeof(read)) {
|
||||
log_misc("Empty read, retry read");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bytes_read != sizeof(read)) {
|
||||
log_warning(
|
||||
"Reading extio device failed, expected bytes %d != actual "
|
||||
"bytes %d",
|
||||
(uint32_t) sizeof(read),
|
||||
(uint32_t) bytes_read);
|
||||
return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (read.status != EXTIO_STATUS_OK) {
|
||||
log_warning("Status not ok: 0x%X", read.status);
|
||||
return HRESULT_FROM_WIN32(ERROR_GEN_FAILURE);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
45
src/main/extiodrv/extio.h
Normal file
45
src/main/extiodrv/extio.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef EXTIODRV_EXTIO_H
|
||||
#define EXTIODRV_EXTIO_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "extio/cmd.h"
|
||||
|
||||
#include "device.h"
|
||||
|
||||
enum extiodrv_extio_sensor_read_mode {
|
||||
EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL = 0,
|
||||
EXTIODRV_EXTIO_SENSOR_READ_MODE_UP = 1,
|
||||
EXTIODRV_EXTIO_SENSOR_READ_MODE_DOWN = 2,
|
||||
EXTIODRV_EXTIO_SENSOR_READ_MODE_LEFT = 3,
|
||||
EXTIODRV_EXTIO_SENSOR_READ_MODE_RIGHT = 4
|
||||
};
|
||||
|
||||
struct extiodrv_extio_pad_lights {
|
||||
bool up;
|
||||
bool down;
|
||||
bool left;
|
||||
bool right;
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a data transfer (write + read) to the EXTIO device
|
||||
*
|
||||
* @param handle A valid and opened handle to an EXTIO device
|
||||
* @param sensor_read_mode The sensor read mode to set on the EXTIO device.
|
||||
* This only needs to be set once that input reads return the configured
|
||||
* sensor read setting.
|
||||
* @param pad_lights Pad light ouptut state to set
|
||||
* @param neons Neon output state to set
|
||||
* @return S_OK on success, other HRESULT value on error
|
||||
*/
|
||||
HRESULT extiodrv_extio_transfer(
|
||||
HANDLE handle,
|
||||
enum extiodrv_extio_sensor_read_mode sensor_read_mode,
|
||||
const struct extiodrv_extio_pad_lights
|
||||
pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS],
|
||||
bool neons);
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user