From 7e5e0f132ef8143c645a450cb7527d80113361d5 Mon Sep 17 00:00:00 2001 From: Dniel97 Date: Sun, 23 Jun 2024 21:21:57 +0200 Subject: [PATCH] idac: 837-15070 board implementation --- board/led15070-cmd.h | 81 +++ board/led15070-frame.c | 194 ++++++ board/led15070-frame.h | 26 + board/led15070.c | 1250 +++++++++++++++++++++++++++++++++++++++ board/led15070.h | 29 + board/meson.build | 5 + dist/idac/segatools.ini | 9 + idachook/config.c | 38 ++ idachook/config.h | 2 + idachook/dllmain.c | 19 +- idachook/idac-dll.c | 12 + idachook/idac-dll.h | 4 + idachook/idachook.def | 4 + idachook/io4.c | 33 ++ idacio/dllmain.c | 43 +- idacio/idacio.def | 4 + idacio/idacio.h | 69 ++- idzhook/dllmain.c | 11 + 18 files changed, 1825 insertions(+), 8 deletions(-) create mode 100644 board/led15070-cmd.h create mode 100644 board/led15070-frame.c create mode 100644 board/led15070-frame.h create mode 100644 board/led15070.c create mode 100644 board/led15070.h diff --git a/board/led15070-cmd.h b/board/led15070-cmd.h new file mode 100644 index 0000000..d5d739a --- /dev/null +++ b/board/led15070-cmd.h @@ -0,0 +1,81 @@ +#pragma once + +#include "board/led15070-frame.h" + +/* Command IDs */ + +enum { + LED_15070_CMD_RESET = 0x10, + LED_15070_CMD_SET_INPUT = 0x28, // No known use case + LED_15070_CMD_SET_NORMAL_12BIT = 0x30, // TODO + LED_15070_CMD_SET_NORMAL_8BIT = 0x31, + LED_15070_CMD_SET_MULTI_FLASH_8BIT = 0x32, + LED_15070_CMD_SET_MULTI_FADE_8BIT = 0x33, + LED_15070_CMD_SET_PALETTE_7_NORMAL_LED = 0x34, // No known use case + LED_15070_CMD_SET_PALETTE_6_FLASH_LED = 0x35, // No known use case + LED_15070_CMD_SET_15DC_OUT = 0x36, // No known use case + LED_15070_CMD_SET_15GS_OUT = 0x37, // No known use case + LED_15070_CMD_SET_PSC_MAX = 0x38, // No known use case + LED_15070_CMD_SET_FET_OUTPUT = 0x39, + LED_15070_CMD_SET_GS_PALETTE = 0x3A, + LED_15070_CMD_DC_UPDATE = 0x3B, + LED_15070_CMD_GS_UPDATE = 0x3C, + LED_15070_CMD_ROTATE = 0x3E, // No known use case, wtf is this? + LED_15070_CMD_SET_DC_DATA = 0x3F, + LED_15070_CMD_EEPROM_WRITE = 0x7B, + LED_15070_CMD_EEPROM_READ = 0x7C, + LED_15070_CMD_ACK_ON = 0x7D, + LED_15070_CMD_ACK_OFF = 0x7E, + LED_15070_CMD_BOARD_INFO = 0xF0, + LED_15070_CMD_BOARD_STATUS = 0xF1, + LED_15070_CMD_FW_SUM = 0xF2, + LED_15070_CMD_PROTOCOL_VER = 0xF3, + LED_15070_CMD_TO_BOOT_MODE = 0xFD, + LED_15070_CMD_FW_UPDATE = 0xFE, +}; + +/* Response codes */ + +enum { + LED_15070_STATUS_OK = 0x01, + LED_15070_STATUS_SUM_ERR = 0x02, + LED_15070_STATUS_PARITY_ERR = 0x03, + LED_15070_STATUS_FRAMING_ERR = 0x04, + LED_15070_STATUS_OVERRUN_ERR = 0x05, + LED_15070_STATUS_BUFFER_OVERFLOW = 0x06, +}; + +enum { + LED_15070_REPORT_OK = 0x01, + LED_15070_REPORT_WAIT = 0x02, + LED_15070_REPORT_ERR1 = 0x03, + LED_15070_REPORT_ERR2 = 0x04, +}; + +/* Request data structures */ + +struct led15070_req_any { + struct led15070_hdr hdr; + uint8_t cmd; + uint8_t payload[256]; +}; + +/* Response data structures */ + +struct led15070_resp_any { + struct led15070_hdr hdr; + uint8_t status; + uint8_t cmd; + uint8_t report; + uint8_t data[32]; +}; + +struct led15070_resp_board_info { + struct led15070_hdr hdr; + uint8_t status; + uint8_t cmd; + uint8_t report; + char board_num[8]; + uint8_t endcode; // Always 0xFF + uint8_t fw_ver; +}; diff --git a/board/led15070-frame.c b/board/led15070-frame.c new file mode 100644 index 0000000..f550fbd --- /dev/null +++ b/board/led15070-frame.c @@ -0,0 +1,194 @@ +#include + +#include +#include +#include +#include + +#include "board/led15070-frame.h" + +#include "hook/iobuf.h" + +static void led15070_frame_sync(struct iobuf *src); +static HRESULT led15070_frame_accept(const struct iobuf *dest); +static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte); + +/* Frame structure: + + [0] Sync byte (0xE0) + [1] Destination address + [2] Source Address + [3] Length of data/payload + [4] Data/payload + For requests (host to board): + [0] Command + ... Payload + For responses (board to host): + [0] Status + [1] Command + [2] Report + ... Payload + [n] Checksum: Sum of all prior bytes (excluding sync byte) + + Byte stuffing: + + 0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */ + +static void led15070_frame_sync(struct iobuf *src) +{ + size_t i; + + for (i = 0 ; i < src->pos && src->bytes[i] != 0xE0 ; i++); + + src->pos -= i; + memmove(&src->bytes[0], &src->bytes[i], i); +} + +static HRESULT led15070_frame_accept(const struct iobuf *dest) +{ + uint8_t checksum; + size_t i; + + if (dest->pos < 3 || dest->pos != dest->bytes[3] + 5) { + return S_FALSE; + } + + checksum = 0; + + for (i = 1 ; i < dest->pos - 1 ; i++) { + checksum += dest->bytes[i]; + } + + //dprintf("LED checksum %02x, expected %02x\n", checksum, dest->bytes[dest->pos - 1]); + + if (checksum != dest->bytes[dest->pos - 1]) { + return HRESULT_FROM_WIN32(ERROR_CRC); + } + + return S_OK; +} + +HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src) +{ + uint8_t byte; + bool escape; + size_t i; + HRESULT hr; + + assert(dest != NULL); + assert(dest->bytes != NULL || dest->nbytes == 0); + assert(dest->pos <= dest->nbytes); + assert(src != NULL); + assert(src->bytes != NULL || src->nbytes == 0); + assert(src->pos <= src->nbytes); + + led15070_frame_sync(src); + + dest->pos = 0; + escape = false; + + for (i = 0, hr = S_FALSE ; i < src->pos && hr == S_FALSE ; i++) { + /* Step the FSM to unstuff another byte */ + + byte = src->bytes[i]; + + if (dest->pos >= dest->nbytes) { + hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } else if (i == 0) { + dest->bytes[dest->pos++] = byte; + } else if (byte == 0xE0) { + hr = E_FAIL; + } else if (byte == 0xD0) { + if (escape) { + hr = E_FAIL; + } + + escape = true; + } else if (escape) { + dest->bytes[dest->pos++] = byte + 1; + escape = false; + } else { + dest->bytes[dest->pos++] = byte; + } + + /* Try to accept the packet we've built up so far */ + + if (SUCCEEDED(hr)) { + hr = led15070_frame_accept(dest); + } + } + + /* Handle FSM terminal state */ + + if (hr != S_FALSE) { + /* Frame was either accepted or rejected, remove it from src */ + memmove(&src->bytes[0], &src->bytes[i], src->pos - i); + src->pos -= i; + } + + return hr; +} + +HRESULT led15070_frame_encode( + struct iobuf *dest, + const void *ptr, + size_t nbytes) +{ + const uint8_t *src; + uint8_t checksum; + uint8_t byte; + size_t i; + HRESULT hr; + + assert(dest != NULL); + assert(dest->bytes != NULL || dest->nbytes == 0); + assert(dest->pos <= dest->nbytes); + assert(ptr != NULL); + + src = ptr; + + assert(nbytes >= 3 && src[0] == 0xE0 && src[3] + 4 == nbytes); + + if (dest->pos >= dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = 0xE0; + checksum = 0; + // dprintf("%02x ", 0xe0); + + for (i = 1 ; i < nbytes ; i++) { + byte = src[i]; + checksum += byte; + // dprintf("%02x ", byte); + + hr = led15070_frame_encode_byte(dest, byte); + + if (FAILED(hr)) { + return hr; + } + } + // dprintf("%02x \n", checksum); + + return led15070_frame_encode_byte(dest, checksum); +} + +static HRESULT led15070_frame_encode_byte(struct iobuf *dest, uint8_t byte) +{ + if (byte == 0xE0 || byte == 0xD0) { + if (dest->pos + 2 > dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = 0xD0; + dest->bytes[dest->pos++] = byte - 1; + } else { + if (dest->pos + 1 > dest->nbytes) { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + dest->bytes[dest->pos++] = byte; + } + + return S_OK; +} diff --git a/board/led15070-frame.h b/board/led15070-frame.h new file mode 100644 index 0000000..078dd32 --- /dev/null +++ b/board/led15070-frame.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include +#include + +#include "hook/iobuf.h" + +enum { + LED_15070_FRAME_SYNC = 0xE0, +}; + +struct led15070_hdr { + uint8_t sync; + uint8_t dest_adr; + uint8_t src_adr; + uint8_t nbytes; +}; + +HRESULT led15070_frame_decode(struct iobuf *dest, struct iobuf *src); + +HRESULT led15070_frame_encode( + struct iobuf *dest, + const void *ptr, + size_t nbytes); diff --git a/board/led15070.c b/board/led15070.c new file mode 100644 index 0000000..6a634e8 --- /dev/null +++ b/board/led15070.c @@ -0,0 +1,1250 @@ +/* + SEGA 837-15070-0X LED Controller Board Emulator + + Credits: + 837-15070-04 LED Controller Board Emulator (emihiok) + 837-15093-06 LED Controller Board Emulator (somewhatlurker, skogaby) + (a/o June 2023) +*/ + +#include + +#include +#include +#include +#include +#include +#include + +#include "board/led15070-cmd.h" +#include "board/led15070-frame.h" + +#include "board/led15070.h" + +#include "hook/iobuf.h" +#include "hook/iohook.h" + +#include "hooklib/uart.h" + +#include "util/dprintf.h" +#include "util/dump.h" + +static HRESULT led15070_handle_irp(struct irp *irp); +static HRESULT led15070_handle_irp_locked(int board, struct irp *irp); + +static HRESULT led15070_req_dispatch(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_reset(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_input(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_normal_12bit(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_normal_8bit(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_multi_flash_8bit(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_multi_fade_8bit(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_palette_7_normal_led(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_palette_6_flash_led(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_15dc_out(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_15gs_out(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_psc_max(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_fet_output(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_gs_palette(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_dc_update(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_gs_update(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_rotate(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_set_dc_data(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_eeprom_write(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_eeprom_read(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_ack_on(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_ack_off(int board, const struct led15070_req_any *req); +static HRESULT led15070_req_board_info(int board); +static HRESULT led15070_req_board_status(int board); +static HRESULT led15070_req_fw_sum(int board); +static HRESULT led15070_req_protocol_ver(int board); +static HRESULT led15070_req_to_boot_mode(int board); +static HRESULT led15070_req_fw_update(int board, const struct led15070_req_any *req); + +static HRESULT led15070_eeprom_open(int board, wchar_t *path, HANDLE *handle); +static HRESULT led15070_eeprom_close(int board, wchar_t *path, HANDLE *handle); + +static char led15070_board_num[8]; +static uint8_t led15070_fw_ver; +static uint16_t led15070_fw_sum; +static uint8_t led15070_host_adr = 0x01; + +#define led15070_nboards 2 + +typedef struct { + CRITICAL_SECTION lock; + bool started; + HRESULT start_hr; + struct uart boarduart; + uint8_t written_bytes[520]; + uint8_t readable_bytes[520]; + uint8_t gs[32][4]; + uint8_t dc[32][3]; + uint8_t fet[3]; + uint8_t gs_palette[8][3]; + wchar_t eeprom_path[MAX_PATH]; + HANDLE eeprom_handle; + uint8_t boardadr; + bool enable_bootloader; + bool enable_response; +} _led15070_per_board_vars; + +_led15070_per_board_vars led15070_per_board_vars[led15070_nboards]; + +static io_led_init_t led_init; +static io_led_set_fet_output_t led_set_fet_output; +static io_led_dc_update_t led_dc_update; +static io_led_gs_update_t led_gs_update; + +HRESULT led15070_hook_init( + const struct led15070_config *cfg, + io_led_init_t _led_init, + io_led_set_fet_output_t _led_set_fet_output, + io_led_dc_update_t _led_dc_update, + io_led_gs_update_t _led_gs_update, + unsigned int first_port, + unsigned int num_boards) +{ + assert(cfg != NULL); + + if (!cfg->enable) { + return S_FALSE; + } + + if (cfg->port_no != 0) { + first_port = cfg->port_no; + } + + led_init = _led_init; + led_set_fet_output = _led_set_fet_output; + led_dc_update = _led_dc_update; + led_gs_update = _led_gs_update; + + memcpy(led15070_board_num, cfg->board_number, sizeof(led15070_board_num)); + led15070_fw_ver = cfg->fw_ver; + led15070_fw_sum = cfg->fw_sum; + + for (int i = 0; i < num_boards; i++) + { + _led15070_per_board_vars *v = &led15070_per_board_vars[i]; + + InitializeCriticalSection(&v->lock); + + // TODO: IMPROVE! + first_port = i == 1 ? first_port + 2 : first_port; + + uart_init(&v->boarduart, first_port); + v->boarduart.baud.BaudRate = 115200; + v->boarduart.written.bytes = v->written_bytes; + v->boarduart.written.nbytes = sizeof(v->written_bytes); + v->boarduart.readable.bytes = v->readable_bytes; + v->boarduart.readable.nbytes = sizeof(v->readable_bytes); + + memset(v->gs, 0, sizeof(v->gs)); + memset(v->dc, 0, sizeof(v->dc)); + memset(v->fet, 0, sizeof(v->fet)); + memset(v->gs_palette, 0, sizeof(v->gs_palette)); + memset(v->eeprom_path, 0, sizeof(v->eeprom_path)); + v->eeprom_handle = NULL; + + swprintf_s + ( + v->eeprom_path, MAX_PATH, + L"%s\\led15070_eeprom_%d.bin", + cfg->eeprom_path, i + ); + + /* Generate board EEPROM file if it doesn't already exist */ + led15070_eeprom_open(i, v->eeprom_path, &(v->eeprom_handle)); + led15070_eeprom_close(i, v->eeprom_path, &(v->eeprom_handle)); + + v->boardadr = 0x11; + v->enable_bootloader = false; + v->enable_response = false; + } + + dprintf("LED 15070: hook enabled.\n"); + + return iohook_push_handler(led15070_handle_irp); +} + +static HRESULT led15070_handle_irp(struct irp *irp) +{ + HRESULT hr; + + assert(irp != NULL); + + for (int i = 0; i < led15070_nboards; i++) + { + _led15070_per_board_vars *v = &led15070_per_board_vars[i]; + struct uart *boarduart = &v->boarduart; + + if (uart_match_irp(boarduart, irp)) + { + CRITICAL_SECTION lock = v->lock; + + EnterCriticalSection(&lock); + hr = led15070_handle_irp_locked(i, irp); + LeaveCriticalSection(&lock); + + return hr; + } + } + + return iohook_invoke_next(irp); +} + +static HRESULT led15070_handle_irp_locked(int board, struct irp *irp) +{ + struct led15070_req_any req; + struct iobuf req_iobuf; + HRESULT hr; + + _led15070_per_board_vars *v = &led15070_per_board_vars[board]; + struct uart *boarduart = &led15070_per_board_vars[board].boarduart; + + if (irp->op == IRP_OP_OPEN) { + // Unfortunately the LED board UART gets opened and closed + // repeatedly + + if (!v->started) { + dprintf("LED 15070: Starting LED backend\n"); + hr = led_init(); + + v->started = true; + v->start_hr = hr; + + if (FAILED(hr)) { + dprintf("LED 15070: Backend error, LED controller " + "disconnected: %x\n", + (int) hr); + + return hr; + } + } else { + hr = v->start_hr; + + if (FAILED(hr)) { + return hr; + } + } + } + + hr = uart_handle_irp(boarduart, irp); + + if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + return hr; + } + + for (;;) { +#if 0 + dprintf("TX Buffer:\n"); + dump_iobuf(&boarduart->written); +#endif + + req_iobuf.bytes = (byte*)&req; + req_iobuf.nbytes = sizeof(req.hdr) + sizeof(req.cmd) + sizeof(req.payload); + req_iobuf.pos = 0; + + hr = led15070_frame_decode(&req_iobuf, &boarduart->written); + + if (hr != S_OK) { + if (FAILED(hr)) { + dprintf("LED 15070: Deframe error: %x\n", (int) hr); + } + + return hr; + } + +#if 0 + dprintf("Deframe Buffer:\n"); + dump_iobuf(&req_iobuf); +#endif + + hr = led15070_req_dispatch(board, &req); + + if (FAILED(hr)) { + dprintf("LED 15070: Processing error: %x\n", (int) hr); + } + } +} + +static HRESULT led15070_req_dispatch(int board, const struct led15070_req_any *req) +{ + switch (req->cmd) { + case LED_15070_CMD_RESET: + return led15070_req_reset(board, req); + + case LED_15070_CMD_SET_INPUT: + return led15070_req_set_input(board, req); + + case LED_15070_CMD_SET_NORMAL_12BIT: + return led15070_req_set_normal_12bit(board, req); + + case LED_15070_CMD_SET_NORMAL_8BIT: + return led15070_req_set_normal_8bit(board, req); + + case LED_15070_CMD_SET_MULTI_FLASH_8BIT: + return led15070_req_set_multi_flash_8bit(board, req); + + case LED_15070_CMD_SET_MULTI_FADE_8BIT: + return led15070_req_set_multi_fade_8bit(board, req); + + case LED_15070_CMD_SET_PALETTE_7_NORMAL_LED: + return led15070_req_set_palette_7_normal_led(board, req); + + case LED_15070_CMD_SET_PALETTE_6_FLASH_LED: + return led15070_req_set_palette_6_flash_led(board, req); + + case LED_15070_CMD_SET_15DC_OUT: + return led15070_req_set_15dc_out(board, req); + + case LED_15070_CMD_SET_15GS_OUT: + return led15070_req_set_15gs_out(board, req); + + case LED_15070_CMD_SET_PSC_MAX: + return led15070_req_set_psc_max(board, req); + + case LED_15070_CMD_SET_FET_OUTPUT: + return led15070_req_set_fet_output(board, req); + + case LED_15070_CMD_SET_GS_PALETTE: + return led15070_req_set_gs_palette(board, req); + + case LED_15070_CMD_DC_UPDATE: + return led15070_req_dc_update(board, req); + + case LED_15070_CMD_GS_UPDATE: + return led15070_req_gs_update(board, req); + + case LED_15070_CMD_ROTATE: + return led15070_req_rotate(board, req); + + case LED_15070_CMD_SET_DC_DATA: + return led15070_req_set_dc_data(board, req); + + case LED_15070_CMD_EEPROM_WRITE: + return led15070_req_eeprom_write(board, req); + + case LED_15070_CMD_EEPROM_READ: + return led15070_req_eeprom_read(board, req); + + case LED_15070_CMD_ACK_ON: + return led15070_req_ack_on(board, req); + + case LED_15070_CMD_ACK_OFF: + return led15070_req_ack_off(board, req); + + case LED_15070_CMD_BOARD_INFO: + return led15070_req_board_info(board); + + case LED_15070_CMD_BOARD_STATUS: + return led15070_req_board_status(board); + + case LED_15070_CMD_FW_SUM: + return led15070_req_fw_sum(board); + + case LED_15070_CMD_PROTOCOL_VER: + return led15070_req_protocol_ver(board); + + case LED_15070_CMD_TO_BOOT_MODE: + return led15070_req_to_boot_mode(board); + + case LED_15070_CMD_FW_UPDATE: + return led15070_req_fw_update(board, req); + + default: + dprintf("LED 15070: Unhandled command %02x\n", req->cmd); + + return S_OK; + } +} + +static HRESULT led15070_req_reset(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Reset (board %u)\n", board); + + led15070_per_board_vars[board].enable_bootloader = false; + led15070_per_board_vars[board].enable_response = true; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_RESET; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_input(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Set input (board %u)\n", board); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_INPUT; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_normal_12bit(int board, const struct led15070_req_any *req) +{ + uint8_t idx = req->payload[0]; + + dprintf("LED 15070: Set LED - Normal 12bit (board %u, index %u)\n", + board, idx); + + // TODO: Data for this command. Seen with Carol + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_NORMAL_12BIT; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_normal_8bit(int board, const struct led15070_req_any *req) +{ + uint8_t idx = req->payload[0]; + + // dprintf("LED 15070: Set LED - Normal 8bit (board %u, index %u)\n", + // board, idx); + + led15070_per_board_vars[board].gs[idx][0] = req->payload[1]; // R + led15070_per_board_vars[board].gs[idx][1] = req->payload[2]; // G + led15070_per_board_vars[board].gs[idx][2] = req->payload[3]; // B + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_NORMAL_8BIT; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_multi_flash_8bit(int board, const struct led15070_req_any *req) +{ + uint8_t idx_start = req->payload[0]; + uint8_t idx_end = req->payload[1]; + uint8_t idx_skip = req->payload[2]; + + // TODO: useful? + // dprintf("LED 15070: Set LED - Multi flash 8bit (board %u, start %u, end %u, skip %u)\n", + // board, idx_start, idx_end, idx_skip); + + if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) { + idx_start += idx_skip; + } + + int i = idx_start; + do { + led15070_per_board_vars[board].gs[i][0] = req->payload[3]; // R + led15070_per_board_vars[board].gs[i][1] = req->payload[4]; // G + led15070_per_board_vars[board].gs[i][2] = req->payload[5]; // B + /* Always 0, tells the controller to immediately change to this color */ + led15070_per_board_vars[board].gs[i][3] = req->payload[6]; // Speed + i++; + } while (i < idx_end); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_MULTI_FLASH_8BIT; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_multi_fade_8bit(int board, const struct led15070_req_any *req) +{ + uint8_t idx_start = req->payload[0]; + uint8_t idx_end = req->payload[1]; + uint8_t idx_skip = req->payload[2]; + + // dprintf("LED 15070: Set LED - Multi fade 8bit (board %u, start %u, end %u, skip %u)\n", + // board, idx_start, idx_end, idx_skip); + + if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) { + idx_start += idx_skip; + } + + int i = idx_start; + do { + led15070_per_board_vars[board].gs[i][0] = req->payload[3]; // R + led15070_per_board_vars[board].gs[i][1] = req->payload[4]; // G + led15070_per_board_vars[board].gs[i][2] = req->payload[5]; // B + led15070_per_board_vars[board].gs[i][3] = req->payload[6]; // Speed + i++; + } while (i < idx_end); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_MULTI_FADE_8BIT; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_palette_7_normal_led(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Set palette - 7 Normal LED (board %u)\n", board); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_PALETTE_7_NORMAL_LED; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_palette_6_flash_led(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Set palette - 6 Flash LED (board %u)\n", board); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_PALETTE_6_FLASH_LED; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_15dc_out(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Set 15DC out (board %u)\n", board); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_15DC_OUT; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_15gs_out(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Set 15GS out (board %u)\n", board); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_15GS_OUT; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_psc_max(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Set PSC max (board %u)\n", board); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_PSC_MAX; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_fet_output(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Set FET output (board %u)\n", board); + + led15070_per_board_vars[board].fet[0] = req->payload[0]; // R or FET0 intensity + led15070_per_board_vars[board].fet[1] = req->payload[1]; // G or FET1 intensity + led15070_per_board_vars[board].fet[2] = req->payload[2]; // B or FET2 intensity + + if (led_set_fet_output) + led_set_fet_output((const uint8_t*)led15070_per_board_vars[board].fet); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_FET_OUTPUT; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_gs_palette(int board, const struct led15070_req_any *req) +{ + uint8_t idx = req->payload[0]; + + dprintf("LED 15070: Set GS palette (board %u, index %u)\n", board, idx); + + led15070_per_board_vars[board].gs_palette[idx][0] = req->payload[1]; // R + led15070_per_board_vars[board].gs_palette[idx][1] = req->payload[2]; // G + led15070_per_board_vars[board].gs_palette[idx][2] = req->payload[3]; // B + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_GS_PALETTE; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_dc_update(int board, const struct led15070_req_any *req) +{ + // dprintf("LED 15070: DC update (board %u)\n", board); + + if (led_dc_update) + led_dc_update((const uint8_t*)led15070_per_board_vars[board].dc); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_DC_UPDATE; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_gs_update(int board, const struct led15070_req_any *req) +{ + // dprintf("LED 15070: GS update (board %u)\n", board); + + if (led_gs_update) + led_gs_update((const uint8_t*)led15070_per_board_vars[board].gs); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_GS_UPDATE; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_rotate(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Rotate (board %u)\n", board); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_ROTATE; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_set_dc_data(int board, const struct led15070_req_any *req) +{ + uint8_t idx_start = req->payload[0]; + uint8_t idx_end = req->payload[1]; + uint8_t idx_skip = req->payload[2]; + + // dprintf("LED 15070: Set DC data (board %u, start %u, end %u, skip %u)\n", + // board, idx_start, idx_end, idx_skip); + + if (idx_skip > 0 && idx_skip <= (idx_end - idx_start + 1)) { + idx_start += idx_skip; + } + + int i = idx_start; + do { + led15070_per_board_vars[board].dc[i][0] = req->payload[3]; // R + led15070_per_board_vars[board].dc[i][1] = req->payload[4]; // G + led15070_per_board_vars[board].dc[i][2] = req->payload[5]; // B + i++; + } while (i < idx_end); + + if (!led15070_per_board_vars[board].enable_response) + return S_OK; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_SET_DC_DATA; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_eeprom_write(int board, const struct led15070_req_any *req) +{ + DWORD eeprom_bytes_written = 0; + HRESULT hr; + BOOL ok; + + uint8_t addr = req->payload[0]; + uint8_t data = req->payload[1]; + + dprintf("LED 15070: EEPROM write (board %u, address %02x, data %02x)\n", + board, addr, data); + + if (addr > 0x07) { + dprintf("LED 15070: Error -- Invalid EEPROM write address %02x\n", + addr); + + return E_INVALIDARG; + } + + hr = led15070_eeprom_open( + board, + led15070_per_board_vars[board].eeprom_path, + &(led15070_per_board_vars[board].eeprom_handle)); + + if (FAILED(hr)) { + return hr; + } + + hr = SetFilePointer( + led15070_per_board_vars[board].eeprom_handle, + addr, + NULL, + FILE_BEGIN); + + if (hr == INVALID_SET_FILE_POINTER) { + dprintf("LED 10570: Error -- Failed to set pointer to EEPROM file " + "(board %u)\n", board); + led15070_eeprom_close( + board, + led15070_per_board_vars[board].eeprom_path, + &(led15070_per_board_vars[board].eeprom_handle)); + + return hr; + } + + ok = WriteFile( + led15070_per_board_vars[board].eeprom_handle, + &data, + sizeof(data), + &eeprom_bytes_written, NULL); + + if (!ok || eeprom_bytes_written == 0) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("LED 15070: Error -- Failed to write to EEPROM file: %x " + "(board %u)\n", (int) hr, board); + led15070_eeprom_close( + board, + led15070_per_board_vars[board].eeprom_path, + &(led15070_per_board_vars[board].eeprom_handle)); + + return hr; + } + + hr = led15070_eeprom_close( + board, + led15070_per_board_vars[board].eeprom_path, + &(led15070_per_board_vars[board].eeprom_handle)); + + if (FAILED(hr)) { + return hr; + } + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 2 + 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_EEPROM_WRITE; + resp.report = LED_15070_REPORT_OK; + + resp.data[0] = addr; + resp.data[1] = data; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_eeprom_read(int board, const struct led15070_req_any *req) +{ + DWORD eeprom_bytes_read = 0; + HRESULT hr; + BOOL ok; + + uint8_t addr = req->payload[0]; + uint8_t data = 0; + + dprintf("LED 15070: EEPROM read (board %u, address %02x)\n", board, addr); + + if (addr > 0x07) { + dprintf("LED 15070: Error -- Invalid EEPROM read address %02x\n", + addr); + + return E_INVALIDARG; + } + + hr = led15070_eeprom_open( + board, + led15070_per_board_vars[board].eeprom_path, + &(led15070_per_board_vars[board].eeprom_handle)); + + if (FAILED(hr)) { + return hr; + } + + hr = SetFilePointer( + led15070_per_board_vars[board].eeprom_handle, + addr, + NULL, + FILE_BEGIN); + + if (hr == INVALID_SET_FILE_POINTER) { + dprintf("LED 10570: Error -- Failed to set pointer to EEPROM file " + "(board %u)\n", board); + led15070_eeprom_close( + board, + led15070_per_board_vars[board].eeprom_path, + &(led15070_per_board_vars[board].eeprom_handle)); + + return hr; + } + + ok = ReadFile( + led15070_per_board_vars[board].eeprom_handle, + &data, + sizeof(data), + &eeprom_bytes_read, + NULL); + + if (!ok || eeprom_bytes_read == 0) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("LED 15070: Error -- Failed to read from EEPROM file: %x " + "(board %u)\n", (int) hr, board); + led15070_eeprom_close( + board, + led15070_per_board_vars[board].eeprom_path, + &(led15070_per_board_vars[board].eeprom_handle)); + + return hr; + } + + hr = led15070_eeprom_close( + board, + led15070_per_board_vars[board].eeprom_path, + &(led15070_per_board_vars[board].eeprom_handle)); + + if (FAILED(hr)) { + return hr; + } + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 1 + 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_EEPROM_READ; + resp.report = LED_15070_REPORT_OK; + + resp.data[0] = data; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_ack_on(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Acknowledge commands ON (board %u)\n", board); + + led15070_per_board_vars[board].enable_response = true; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_ACK_ON; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_ack_off(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Acknowledge commands OFF (board %u)\n", board); + + led15070_per_board_vars[board].enable_response = false; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_ACK_OFF; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_board_info(int board) +{ + dprintf("LED 15070: Get board info (board %u)\n", board); + + struct led15070_resp_board_info resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 10 + 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_BOARD_INFO; + resp.report = LED_15070_REPORT_OK; + + memcpy(resp.board_num, led15070_board_num, sizeof(resp.board_num)); + resp.endcode = 0xff; + resp.fw_ver = led15070_fw_ver; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_board_status(int board) +{ + dprintf("LED 15070: Get board status (board %u)\n", board); + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 4 + 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_BOARD_STATUS; + resp.report = LED_15070_REPORT_OK; + + resp.data[0] = 0; // Timeout status + resp.data[1] = 0; // Timeout sec + resp.data[2] = 0; // PWM IO + resp.data[3] = 0; // FET timeout + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_fw_sum(int board) +{ + dprintf("LED 15070: Get firmware checksum (board %u)\n", board); + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 2 + 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_FW_SUM; + resp.report = LED_15070_REPORT_OK; + + resp.data[0] = (led15070_fw_sum >> 8) & 0xff; + resp.data[1] = led15070_fw_sum & 0xff; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_protocol_ver(int board) +{ + dprintf("LED 15070: Get protocol version (board %u)\n", board); + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3 + 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_PROTOCOL_VER; + resp.report = LED_15070_REPORT_OK; + + if (led15070_per_board_vars[board].enable_bootloader) { + resp.data[0] = 0; // BOOT mode + resp.data[1] = 1; // Version major + resp.data[2] = 1; // Version minor + } else { + resp.data[0] = 1; // APPLI mode + resp.data[1] = 1; // Version major + resp.data[2] = 4; // Version minor + } + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_to_boot_mode(int board) +{ + dprintf("LED 15070: To boot mode (board %u)\n", board); + + led15070_per_board_vars[board].enable_bootloader = true; + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_TO_BOOT_MODE; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_req_fw_update(int board, const struct led15070_req_any *req) +{ + dprintf("LED 15070: Firmware update (UNIMPLEMENTED) (board %u)\n", board); + + struct led15070_resp_any resp; + + memset(&resp, 0, sizeof(resp)); + resp.hdr.sync = LED_15070_FRAME_SYNC; + resp.hdr.dest_adr = led15070_host_adr; + resp.hdr.src_adr = led15070_per_board_vars[board].boardadr; + resp.hdr.nbytes = 3; + + resp.status = LED_15070_STATUS_OK; + resp.cmd = LED_15070_CMD_FW_UPDATE; + resp.report = LED_15070_REPORT_OK; + + return led15070_frame_encode(&led15070_per_board_vars[board].boarduart.readable, &resp, sizeof(resp.hdr) + resp.hdr.nbytes); +} + +static HRESULT led15070_eeprom_open(int board, wchar_t *path, HANDLE *handle) +{ + DWORD eeprom_bytes_written; + BYTE null_bytes[8] = { 0x00 }; + HRESULT hr; + BOOL ok; + +#if 0 + dprintf("LED 15070: Opening EEPROM file '%S' handle (board %u)\n", path, board); +#endif + + *handle = CreateFileW( + path, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (*handle == INVALID_HANDLE_VALUE) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("LED 15070: Failed to open EEPROM file '%S' handle: %x " + "(board %u)\n", path, (int) hr, board); + + return hr; + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) return S_OK; + + ok = WriteFile( + *handle, + null_bytes, + sizeof(null_bytes), + &eeprom_bytes_written, + NULL); + + if (!ok || eeprom_bytes_written != sizeof(null_bytes)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("LED 15070: Failed to initialize empty EEPROM file '%S': %x " + "(board %u)\n", path, (int) hr, board); + + return hr; + } + + return S_OK; +} + +static HRESULT led15070_eeprom_close(int board, wchar_t *path, HANDLE *handle) +{ + HRESULT hr; + BOOL ok; + +#if 0 + dprintf("LED 15070: Closing EEPROM file '%S' handle (board %u)\n", path, board); +#endif + + ok = CloseHandle(*handle); + + if (!ok) { + hr = HRESULT_FROM_WIN32(GetLastError()); + dprintf("LED 15070: Failed to close EEPROM file '%S' handle: %x " + "(board %u)\n", path, (int) hr, board); + + return hr; + } + + return S_OK; +} diff --git a/board/led15070.h b/board/led15070.h new file mode 100644 index 0000000..a2ad863 --- /dev/null +++ b/board/led15070.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include +#include + +struct led15070_config { + bool enable; + unsigned int port_no; + char board_number[8]; + uint8_t fw_ver; + uint16_t fw_sum; + wchar_t eeprom_path[MAX_PATH]; +}; + +typedef HRESULT (*io_led_init_t)(void); +typedef void (*io_led_set_fet_output_t)(const uint8_t *rgb); +typedef void (*io_led_dc_update_t)(const uint8_t *rgb); +typedef void (*io_led_gs_update_t)(const uint8_t *rgb); + +HRESULT led15070_hook_init( + const struct led15070_config *cfg, + io_led_init_t _led_init, + io_led_set_fet_output_t _led_set_fet_output, + io_led_dc_update_t _led_dc_update, + io_led_gs_update_t _led_gs_update, + unsigned int first_port, + unsigned int num_boards); diff --git a/board/meson.build b/board/meson.build index b0dd300..0bf4fbc 100644 --- a/board/meson.build +++ b/board/meson.build @@ -25,6 +25,11 @@ board_lib = static_library( 'led15093-frame.h', 'led15093.c', 'led15093.h', + 'led15070-cmd.h', + 'led15070-frame.c', + 'led15070-frame.h', + 'led15070.c', + 'led15070.h', 'sg-cmd.c', 'sg-cmd.h', 'sg-frame.c', diff --git a/dist/idac/segatools.ini b/dist/idac/segatools.ini index 8ed90a2..eab33e2 100644 --- a/dist/idac/segatools.ini +++ b/dist/idac/segatools.ini @@ -75,6 +75,15 @@ dipsw3=0 dipsw4=0 dipsw5=0 +; ----------------------------------------------------------------------------- +; LED settings +; ----------------------------------------------------------------------------- + +[led15070] +; Enable emulation of the 15070-02 controlled lights, which handle the cabinet +; and seat LEDs. +enable=1 + ; ----------------------------------------------------------------------------- ; Custom IO settings ; ----------------------------------------------------------------------------- diff --git a/idachook/config.c b/idachook/config.c index 5452bf6..f3bf349 100644 --- a/idachook/config.c +++ b/idachook/config.c @@ -13,6 +13,43 @@ #include "platform/config.h" #include "platform/platform.h" + +void led15070_config_load(struct led15070_config *cfg, const wchar_t *filename) +{ + assert(cfg != NULL); + assert(filename != NULL); + + wchar_t tmpstr[16]; + + cfg->enable = GetPrivateProfileIntW(L"led15070", L"enable", 1, filename); + cfg->port_no = GetPrivateProfileIntW(L"led15070", L"portNo", 0, filename); + cfg->fw_ver = GetPrivateProfileIntW(L"led15070", L"fwVer", 0x90, filename); + /* TODO: Unknown, no firmware file available */ + cfg->fw_sum = GetPrivateProfileIntW(L"led15070", L"fwSum", 0x0000, filename); + + GetPrivateProfileStringW( + L"led15070", + L"boardNumber", + L"15070-02", + tmpstr, + _countof(tmpstr), + filename); + + size_t n = wcstombs(cfg->board_number, tmpstr, sizeof(cfg->board_number)); + for (int i = n; i < sizeof(cfg->board_number); i++) + { + cfg->board_number[i] = ' '; + } + + GetPrivateProfileStringW( + L"led15070", + L"eepromPath", + L"DEVICE", + cfg->eeprom_path, + _countof(cfg->eeprom_path), + filename); +} + void idac_dll_config_load( struct idac_dll_config *cfg, const wchar_t *filename) @@ -42,6 +79,7 @@ void idac_hook_config_load( zinput_config_load(&cfg->zinput, filename); dvd_config_load(&cfg->dvd, filename); io4_config_load(&cfg->io4, filename); + led15070_config_load(&cfg->led15070, filename); } void zinput_config_load(struct zinput_config *cfg, const wchar_t *filename) diff --git a/idachook/config.h b/idachook/config.h index 9c7dfd7..2ff8d33 100644 --- a/idachook/config.h +++ b/idachook/config.h @@ -4,6 +4,7 @@ #include #include "board/config.h" +#include "board/led15070.h" #include "hooklib/dvd.h" @@ -19,6 +20,7 @@ struct idac_hook_config { struct io4_config io4; struct idac_dll_config dll; struct zinput_config zinput; + struct led15070_config led15070; }; void idac_dll_config_load( diff --git a/idachook/dllmain.c b/idachook/dllmain.c index accc661..bbd3f8a 100644 --- a/idachook/dllmain.c +++ b/idachook/dllmain.c @@ -67,24 +67,31 @@ static DWORD CALLBACK idac_pre_startup(void) goto fail; } - hr = sg_reader_hook_init(&idac_hook_cfg.aime, 3, 3, idac_hook_mod); - - if (FAILED(hr)) { - goto fail; - } - hr = idac_dll_init(&idac_hook_cfg.dll, idac_hook_mod); if (FAILED(hr)) { goto fail; } + hr = sg_reader_hook_init(&idac_hook_cfg.aime, 3, 3, idac_hook_mod); + + if (FAILED(hr)) { + goto fail; + } + hr = idac_io4_hook_init(&idac_hook_cfg.io4); if (FAILED(hr)) { goto fail; } + hr = led15070_hook_init(&idac_hook_cfg.led15070, idac_dll.led_init, + idac_dll.led_set_fet_output, NULL, idac_dll.led_gs_update, 2, 1); + + if (FAILED(hr)) { + goto fail; + } + /* Initialize debug helpers */ spike_hook_init(L".\\segatools.ini"); diff --git a/idachook/idac-dll.c b/idachook/idac-dll.c index b463a6d..3019608 100644 --- a/idachook/idac-dll.c +++ b/idachook/idac-dll.c @@ -24,6 +24,18 @@ const struct dll_bind_sym idac_dll_syms[] = { }, { .sym = "idac_io_get_analogs", .off = offsetof(struct idac_dll, get_analogs), + }, { + .sym = "idac_io_led_init", + .off = offsetof(struct idac_dll, led_init), + }, { + .sym = "idac_io_led_set_fet_output", + .off = offsetof(struct idac_dll, led_set_fet_output), + }, { + .sym = "idac_io_led_gs_update", + .off = offsetof(struct idac_dll, led_gs_update), + }, { + .sym = "idac_io_led_set_leds", + .off = offsetof(struct idac_dll, led_set_leds), } }; diff --git a/idachook/idac-dll.h b/idachook/idac-dll.h index cc4a492..acdce35 100644 --- a/idachook/idac-dll.h +++ b/idachook/idac-dll.h @@ -11,6 +11,10 @@ struct idac_dll { void (*get_gamebtns)(uint8_t *gamebtn); void (*get_shifter)(uint8_t *gear); void (*get_analogs)(struct idac_io_analog_state *out); + HRESULT (*led_init)(void); + void (*led_set_fet_output)(const uint8_t *rgb); + void (*led_gs_update)(const uint8_t *rgb); + void (*led_set_leds)(const uint8_t *rgb); }; struct idac_dll_config { diff --git a/idachook/idachook.def b/idachook/idachook.def index a8b05b5..32cdffc 100644 --- a/idachook/idachook.def +++ b/idachook/idachook.def @@ -17,3 +17,7 @@ EXPORTS idac_io_get_gamebtns idac_io_get_shifter idac_io_get_analogs + idac_io_led_init + idac_io_led_set_fet_output + idac_io_led_gs_update + idac_io_led_set_leds diff --git a/idachook/io4.c b/idachook/io4.c index b5e13be..5340f5a 100644 --- a/idachook/io4.c +++ b/idachook/io4.c @@ -11,10 +11,12 @@ #include "util/dprintf.h" static HRESULT idac_io4_poll(void *ctx, struct io4_state *state); +static HRESULT idac_io4_write_gpio(uint8_t* payload, size_t len); static uint16_t coins; static const struct io4_ops idac_io4_ops = { .poll = idac_io4_poll, + .write_gpio = idac_io4_write_gpio }; static const uint16_t idac_gear_signals[] = { @@ -128,3 +130,34 @@ static HRESULT idac_io4_poll(void *ctx, struct io4_state *state) return S_OK; } + +static HRESULT idac_io4_write_gpio(uint8_t* payload, size_t len) +{ + // Just fast fail if there aren't enough bytes in the payload + if (len < 3) + return S_OK; + + // This command is used for lights in IDAC, but it only contains button lights, + // and only in the first 3 bytes of the payload; everything else is padding to + // make the payload 62 bytes. The rest of the cabinet lights and the side button + // lights are handled separately, by the 15070 lights controller. + uint32_t lights_data = (uint32_t) ((uint8_t)(payload[0]) << 24 | + (uint8_t)(payload[1]) << 16 | + (uint8_t)(payload[2]) << 8); + + // Since Sega uses an odd ordering for the first part of the bitfield, + // let's normalize the data and just send over bytes for the receiver + // to interpret as ON/OFF values. + uint8_t rgb_out[6] = { + lights_data & IDAC_IO_LED_START ? 0xFF : 0x00, + lights_data & IDAC_IO_LED_VIEW_CHANGE ? 0xFF : 0x00, + lights_data & IDAC_IO_LED_UP ? 0xFF : 0x00, + lights_data & IDAC_IO_LED_DOWN ? 0xFF : 0x00, + lights_data & IDAC_IO_LED_RIGHT ? 0xFF : 0x00, + lights_data & IDAC_IO_LED_LEFT ? 0xFF : 0x00, + }; + + idac_dll.led_set_leds(rgb_out); + + return S_OK; +} diff --git a/idacio/dllmain.c b/idacio/dllmain.c index f524139..fbdc7d8 100644 --- a/idacio/dllmain.c +++ b/idacio/dllmain.c @@ -19,7 +19,7 @@ static bool idac_io_coin; uint16_t idac_io_get_api_version(void) { - return 0x0100; + return 0x0101; } HRESULT idac_io_init(void) @@ -118,3 +118,44 @@ void idac_io_get_analogs(struct idac_io_analog_state *out) out->accel = tmp.accel; out->brake = tmp.brake; } + +HRESULT idac_io_led_init(void) +{ + return S_OK; +} + +void idac_io_led_set_fet_output(const uint8_t *rgb) +{ +#if 0 + dprintf("IDAC LED: LEFT SEAT LED: %02X\n", rgb[0]); + dprintf("IDAC LED: RIGHT SEAT LED: %02X\n", rgb[1]); +#endif + + return; +} + +void idac_io_led_gs_update(const uint8_t *rgb) +{ +#if 0 + for (int i = 0; i < 9; i++) { + dprintf("IDAC LED: LED %d: %02X %02X %02X Speed: %02X\n", + i, rgb[i * 4], rgb[i * 4 + 1], rgb[i * 4 + 2], rgb[i * 4 + 3]); + } +#endif + + return; +} + +void idac_io_led_set_leds(const uint8_t *rgb) +{ +#if 0 + dprintf("IDAC LED: START: %02X\n", rgb[0]); + dprintf("IDAC LED: VIEW CHANGE: %02X\n", rgb[1]); + dprintf("IDAC LED: UP: %02X\n", rgb[2]); + dprintf("IDAC LED: DOWN: %02X\n", rgb[3]); + dprintf("IDAC LED: RIGHT: %02X\n", rgb[4]); + dprintf("IDAC LED: LEFT: %02X\n", rgb[5]); +#endif + + return; +} diff --git a/idacio/idacio.def b/idacio/idacio.def index e888510..6b447ac 100644 --- a/idacio/idacio.def +++ b/idacio/idacio.def @@ -6,3 +6,7 @@ EXPORTS idac_io_get_gamebtns idac_io_get_shifter idac_io_get_analogs + idac_io_led_init + idac_io_led_set_fet_output + idac_io_led_gs_update + idac_io_led_set_leds diff --git a/idacio/idacio.h b/idacio/idacio.h index 04c1b10..5e36b5b 100644 --- a/idacio/idacio.h +++ b/idacio/idacio.h @@ -19,6 +19,17 @@ enum { IDAC_IO_GAMEBTN_VIEW_CHANGE = 0x20, }; +enum { + /* These are the bitmasks to use when checking which + lights are triggered on incoming IO4 GPIO writes. */ + IDAC_IO_LED_START = 1 << 31, + IDAC_IO_LED_VIEW_CHANGE = 1 << 30, + IDAC_IO_LED_UP = 1 << 25, + IDAC_IO_LED_DOWN = 1 << 24, + IDAC_IO_LED_LEFT = 1 << 23, + IDAC_IO_LED_RIGHT = 1 << 22, +}; + struct idac_io_analog_state { /* Current steering wheel position, where zero is the centered position. @@ -92,4 +103,60 @@ void idac_io_get_analogs(struct idac_io_analog_state *out); Minimum API version: 0x0100 */ -void idac_io_get_shifter(uint8_t *gear); \ No newline at end of file +void idac_io_get_shifter(uint8_t *gear); + +/* Initialize LED emulation. This function will be called before any + other idac_io_led_*() function calls. + + All subsequent calls may originate from arbitrary threads and some may + overlap with each other. Ensuring synchronization inside your IO DLL is + your responsibility. + + Minimum API version: 0x0101 */ + +HRESULT idac_io_led_init(void); + +/* Update the FET outputs. rgb is a pointer to an array up to 3 bytes. + + The following bits are used to control the FET outputs: + [0]: LEFT SEAT LED + [1]: RIGHT SEAT LED + + The LED is truned on when the byte is 255 and turned off when the byte is 0. + + Minimum API version: 0x0101 */ + +void idac_io_led_set_fet_output(const uint8_t *rgb); + +/* Update the RGB LEDs. rgb is a pointer to an array up to 32 * 4 = 128 bytes. + + The LEDs are laid out as follows: + [0]: LEFT UP LED + [1-2]: LEFT CENTER LED + [3]: LEFT DOWN LED + [5]: RIGHT UP LED + [6-7]: RIGHT CENTER LED + [8]: RIGHT DOWN LED + + Each rgb value is comprised for 4 bytes in the order of R, G, B, Speed. + Speed is a value from 0 to 255, where 0 is the fastest speed and 255 is the slowest. + + Minimum API version: 0x0101 */ + +void idac_io_led_gs_update(const uint8_t *rgb); + +/* Update the cabinet button LEDs. rgb is a pointer to an array up to 6 bytes. + + The LEDs are laid out as follows: + [0]: START LED + [1]: VIEW CHANGE LED + [2]: UP LED + [3]: DOWN LED + [4]: RIGHT LED + [5]: LEFT LED + + The LED is turned on when the byte is 255 and turned off when the byte is 0. + + Minimum API version: 0x0101 */ + +void idac_io_led_set_leds(const uint8_t *rgb); diff --git a/idzhook/dllmain.c b/idzhook/dllmain.c index 82ae124..cb2fdde 100644 --- a/idzhook/dllmain.c +++ b/idzhook/dllmain.c @@ -1,3 +1,14 @@ +/* + "Initial D ARCADE STAGE Zero" (idz) hook + + Devices + + JVS: 837-15257 "Type 4" I/O Board + COM1: 838-15069 MOTOR DRIVE BD RS232/422 Board + COM10: 837-15286 "Gen 2" Aime Reader + COM11: 837-15070-02 IC BD LED Controller Board +*/ + #include #include