Add support for DDR X
This commit is contained in:
@@ -11,3 +11,8 @@ src_ddrhook-util := \
|
||||
guid.c \
|
||||
misc.c \
|
||||
monitor.c \
|
||||
extio.c \
|
||||
_com4.c \
|
||||
usbmem.c \
|
||||
spike.c \
|
||||
p3io.c \
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
// clang-format off
|
||||
// Don't format because the order is important here
|
||||
#include <windows.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntdef.h>
|
||||
#include <ntddser.h>
|
||||
// clang-format on
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "acio/hdxs.h"
|
||||
|
||||
#include "acioemu/addr.h"
|
||||
#include "acioemu/hdxs.h"
|
||||
#include "acioemu/icca.h"
|
||||
|
||||
#include "bemanitools/ddrio.h"
|
||||
|
||||
#include "ddrhook-util/_com4.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "p3ioemu/uart.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static struct ac_io_emu com4_ac_io_emu;
|
||||
static struct ac_io_emu_hdxs com4_hdxs;
|
||||
static struct ac_io_emu_icca com4_icca[2];
|
||||
|
||||
static uint32_t check_panel_light(
|
||||
const struct ac_io_hdxs_output *output,
|
||||
uint8_t panel_idx,
|
||||
uint8_t out_idx)
|
||||
{
|
||||
if (output->lights[panel_idx].bit) {
|
||||
return 1 << out_idx;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t upscale_light(uint8_t in_7bit) {
|
||||
if (in_7bit < 0x10) {
|
||||
return in_7bit * 2;
|
||||
} else {
|
||||
// so we can actually reach 0xFF
|
||||
return (in_7bit * 2) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void lights_dispatcher(
|
||||
struct ac_io_emu_hdxs *emu, const struct ac_io_message *req)
|
||||
{
|
||||
const struct ac_io_hdxs_output *output = &req->cmd.hdxs_output;
|
||||
|
||||
uint32_t lights = 0;
|
||||
lights |= check_panel_light(output, AC_IO_HDXS_OUT_P1_START, LIGHT_HD_P1_START);
|
||||
lights |= check_panel_light(output, AC_IO_HDXS_OUT_P1_UP_DOWN, LIGHT_HD_P1_UP_DOWN);
|
||||
lights |= check_panel_light(output, AC_IO_HDXS_OUT_P1_LEFT_RIGHT, LIGHT_HD_P1_LEFT_RIGHT);
|
||||
lights |= check_panel_light(output, AC_IO_HDXS_OUT_P2_START, LIGHT_HD_P2_START);
|
||||
lights |= check_panel_light(output, AC_IO_HDXS_OUT_P2_UP_DOWN, LIGHT_HD_P2_UP_DOWN);
|
||||
lights |= check_panel_light(output, AC_IO_HDXS_OUT_P2_LEFT_RIGHT, LIGHT_HD_P2_LEFT_RIGHT);
|
||||
|
||||
ddr_io_set_lights_hdxs_panel(lights);
|
||||
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
size_t light_idx = i * 3;
|
||||
|
||||
// these are 7 bit, upscale them to 8 bit
|
||||
uint8_t r = upscale_light(output->lights[light_idx + AC_IO_HDXS_RED].analog);
|
||||
uint8_t g = upscale_light(output->lights[light_idx + AC_IO_HDXS_GREEN].analog);
|
||||
uint8_t b = upscale_light(output->lights[light_idx + AC_IO_HDXS_BLUE].analog);
|
||||
|
||||
ddr_io_set_lights_hdxs_rgb(i, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
void com4_init(void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
ac_io_emu_init(&com4_ac_io_emu, L"COM4");
|
||||
ac_io_emu_hdxs_init(&com4_hdxs, &com4_ac_io_emu, lights_dispatcher);
|
||||
|
||||
for (i = 0; i < lengthof(com4_icca); i++) {
|
||||
ac_io_emu_icca_init(&com4_icca[i], &com4_ac_io_emu, i);
|
||||
}
|
||||
}
|
||||
|
||||
void com4_fini(void)
|
||||
{
|
||||
ac_io_emu_fini(&com4_ac_io_emu);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
com4_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
const struct ac_io_message *msg;
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (!ac_io_emu_match_irp(&com4_ac_io_emu, irp)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
hr = ac_io_emu_dispatch_irp(&com4_ac_io_emu, irp);
|
||||
|
||||
if (hr != S_OK) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
msg = ac_io_emu_request_peek(&com4_ac_io_emu);
|
||||
|
||||
switch (msg->addr) {
|
||||
case 0:
|
||||
ac_io_emu_cmd_assign_addrs(&com4_ac_io_emu, msg, 3);
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
ac_io_emu_icca_dispatch_request(&com4_icca[0], msg);
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
ac_io_emu_icca_dispatch_request(&com4_icca[1], msg);
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
ac_io_emu_hdxs_dispatch_request(&com4_hdxs, msg);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_BROADCAST:
|
||||
log_warning("Broadcast(?) message on p3io ACIO bus?");
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning(
|
||||
"p3io ACIO message on unhandled bus address: %d",
|
||||
msg->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ac_io_emu_request_pop(&com4_ac_io_emu);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef IIDXHOOK_COM4_H
|
||||
#define IIDXHOOK_COM4_H
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
void com4_init(void);
|
||||
void com4_fini(void);
|
||||
HRESULT com4_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,171 @@
|
||||
// clang-format off
|
||||
// Don't format because the order is important here
|
||||
#include <windows.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntdef.h>
|
||||
#include <ntddser.h>
|
||||
// clang-format on
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "bemanitools/ddrio.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static HRESULT extio_open(struct irp *irp);
|
||||
static HRESULT extio_close(struct irp *irp);
|
||||
static HRESULT extio_write(struct irp *irp);
|
||||
static HRESULT extio_read(struct irp *irp);
|
||||
static HRESULT extio_ioctl(struct irp *irp);
|
||||
|
||||
static HANDLE extio_fd;
|
||||
static bool extio_pending;
|
||||
|
||||
void extio_init(void)
|
||||
{
|
||||
log_assert(extio_fd == NULL);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
hr = iohook_open_nul_fd(&extio_fd);
|
||||
|
||||
if (hr != S_OK) {
|
||||
log_fatal("Opening nul fd failed: %08lx", hr);
|
||||
}
|
||||
}
|
||||
|
||||
void extio_fini(void)
|
||||
{
|
||||
if (extio_fd != NULL) {
|
||||
CloseHandle(extio_fd);
|
||||
}
|
||||
|
||||
extio_fd = NULL;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
extio_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != extio_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN:
|
||||
return extio_open(irp);
|
||||
case IRP_OP_CLOSE:
|
||||
return extio_close(irp);
|
||||
case IRP_OP_READ:
|
||||
return extio_read(irp);
|
||||
case IRP_OP_WRITE:
|
||||
return extio_write(irp);
|
||||
case IRP_OP_IOCTL:
|
||||
return extio_ioctl(irp);
|
||||
default:
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT extio_open(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (!wstr_eq(irp->open_filename, L"COM1")) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
log_info("EXTIO RS232 port opened");
|
||||
irp->fd = extio_fd;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT extio_close(struct irp *irp)
|
||||
{
|
||||
log_info("EXTIO RS232 port closed");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT extio_write(struct irp *irp)
|
||||
{
|
||||
const uint32_t *lights_be;
|
||||
uint32_t lights;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
log_assert(irp->write.bytes != NULL);
|
||||
|
||||
if (irp->write.nbytes >= sizeof(lights)) {
|
||||
lights_be = (const uint32_t *) irp->write.bytes;
|
||||
lights = _byteswap_ulong(*lights_be);
|
||||
ddr_io_set_lights_extio(lights);
|
||||
|
||||
extio_pending = true;
|
||||
} else {
|
||||
log_warning("Short EXTIO write");
|
||||
}
|
||||
|
||||
irp->write.pos = irp->write.nbytes;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT extio_read(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
log_assert(irp->read.bytes != NULL);
|
||||
|
||||
if (extio_pending && irp->read.nbytes > 0) {
|
||||
irp->read.bytes[0] = 0x11;
|
||||
irp->read.pos = 1;
|
||||
extio_pending = false;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT extio_ioctl(struct irp *irp)
|
||||
{
|
||||
SERIAL_STATUS *status;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_SERIAL_GET_COMMSTATUS:
|
||||
if (irp->read.bytes == NULL) {
|
||||
log_warning(
|
||||
"IOCTL_SERIAL_GET_COMMSTATUS: Output buffer is NULL");
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (irp->read.nbytes < sizeof(*status)) {
|
||||
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Buffer is too small");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
status = (SERIAL_STATUS *) irp->read.bytes;
|
||||
status->Errors = 0;
|
||||
status->AmountInInQueue = extio_pending ? 1 : 0;
|
||||
|
||||
irp->read.pos = sizeof(*status);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef DDRHOOK_EXTIO_H
|
||||
#define DDRHOOK_EXTIO_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
void extio_init(void);
|
||||
void extio_fini(void);
|
||||
HRESULT extio_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "bemanitools/ddrio.h"
|
||||
|
||||
#include "ddrhook-util/p3io.h"
|
||||
|
||||
#include "p3ioemu/emu.h"
|
||||
#include "p3ioemu/uart.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
extern bool _15khz;
|
||||
extern bool standard_def;
|
||||
|
||||
static HRESULT p3io_ddr_read_jamma(void *ctx, uint32_t *state);
|
||||
static HRESULT p3io_ddr_set_outputs(void *ctx, uint32_t outputs);
|
||||
static HRESULT p3io_ddr_get_cab_type(void *ctx, enum p3io_cab_type *type);
|
||||
static HRESULT p3io_ddr_get_video_freq(void *ctx, enum p3io_video_freq *freq);
|
||||
static HRESULT p3io_ddr_get_roundplug(
|
||||
void *ctx, uint8_t plug_id, uint8_t *rom, uint8_t *eeprom);
|
||||
|
||||
static const struct p3io_ops p3io_ddr_ops = {
|
||||
.read_jamma = p3io_ddr_read_jamma,
|
||||
.set_outputs = p3io_ddr_set_outputs,
|
||||
.get_cab_type = p3io_ddr_get_cab_type,
|
||||
.get_video_freq = p3io_ddr_get_video_freq,
|
||||
};
|
||||
|
||||
static const struct p3io_ops p3io_ddr_ops_with_plugs = {
|
||||
.read_jamma = p3io_ddr_read_jamma,
|
||||
.set_outputs = p3io_ddr_set_outputs,
|
||||
.get_cab_type = p3io_ddr_get_cab_type,
|
||||
.get_video_freq = p3io_ddr_get_video_freq,
|
||||
.get_roundplug = p3io_ddr_get_roundplug,
|
||||
};
|
||||
|
||||
static struct security_mcode p3io_ddr_mcode;
|
||||
static struct security_id p3io_ddr_pcbid;
|
||||
static struct security_id p3io_ddr_eamid;
|
||||
static struct security_rp_sign_key p3io_black_sign_key;
|
||||
static struct security_rp_sign_key p3io_white_sign_key;
|
||||
|
||||
void p3io_ddr_init(void)
|
||||
{
|
||||
/* COM4 isn't a real COM port, we configure the core P3IO emulator code to
|
||||
generate IRPs addressed to COM4 and then we possibly intercept them in
|
||||
_com4.c (or possibly don't, in which case the communications will be
|
||||
sent to the real COM4 on your system) */
|
||||
|
||||
p3io_uart_set_path(0, L"COM4");
|
||||
p3io_emu_init(&p3io_ddr_ops, NULL);
|
||||
}
|
||||
|
||||
void p3io_ddr_init_with_plugs(
|
||||
const struct security_mcode *mcode,
|
||||
const struct security_id *pcbid,
|
||||
const struct security_id *eamid,
|
||||
const struct security_rp_sign_key *black_sign_key,
|
||||
const struct security_rp_sign_key *white_sign_key)
|
||||
{
|
||||
memcpy(&p3io_ddr_mcode, mcode, sizeof(struct security_mcode));
|
||||
memcpy(&p3io_ddr_pcbid, pcbid, sizeof(struct security_id));
|
||||
memcpy(&p3io_ddr_eamid, eamid, sizeof(struct security_id));
|
||||
memcpy(&p3io_black_sign_key, black_sign_key, sizeof(struct security_rp_sign_key));
|
||||
memcpy(&p3io_white_sign_key, white_sign_key, sizeof(struct security_rp_sign_key));
|
||||
|
||||
/* COM4 isn't a real COM port, we configure the core P3IO emulator code to
|
||||
generate IRPs addressed to COM4 and then we possibly intercept them in
|
||||
_com4.c (or possibly don't, in which case the communications will be
|
||||
sent to the real COM4 on your system) */
|
||||
|
||||
p3io_uart_set_path(0, L"COM4");
|
||||
p3io_emu_init(&p3io_ddr_ops_with_plugs, NULL);
|
||||
}
|
||||
|
||||
void p3io_ddr_fini(void)
|
||||
{
|
||||
p3io_emu_fini();
|
||||
}
|
||||
|
||||
static HRESULT p3io_ddr_read_jamma(void *ctx, uint32_t *state)
|
||||
{
|
||||
log_assert(state != NULL);
|
||||
|
||||
*state = ddr_io_read_pad();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p3io_ddr_set_outputs(void *ctx, uint32_t outputs)
|
||||
{
|
||||
ddr_io_set_lights_p3io(outputs);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p3io_ddr_get_cab_type(void *ctx, enum p3io_cab_type *type)
|
||||
{
|
||||
if (standard_def) {
|
||||
*type = P3IO_CAB_TYPE_SD;
|
||||
} else {
|
||||
*type = P3IO_CAB_TYPE_HD;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p3io_ddr_get_video_freq(void *ctx, enum p3io_video_freq *freq)
|
||||
{
|
||||
if (_15khz) {
|
||||
*freq = P3IO_VIDEO_FREQ_15KHZ;
|
||||
} else {
|
||||
*freq = P3IO_VIDEO_FREQ_31KHZ;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p3io_ddr_get_roundplug(
|
||||
void *ctx, uint8_t plug_id, uint8_t *rom, uint8_t *eeprom)
|
||||
{
|
||||
struct security_rp3_eeprom eeprom_out;
|
||||
|
||||
if (plug_id == 0) {
|
||||
/* black */
|
||||
memcpy(rom, p3io_ddr_pcbid.id, sizeof(p3io_ddr_pcbid.id));
|
||||
security_rp3_generate_signed_eeprom_data(
|
||||
SECURITY_RP_UTIL_RP_TYPE_BLACK,
|
||||
&p3io_black_sign_key,
|
||||
&p3io_ddr_mcode,
|
||||
&p3io_ddr_pcbid,
|
||||
&eeprom_out);
|
||||
} else {
|
||||
/* white */
|
||||
memcpy(rom, p3io_ddr_eamid.id, sizeof(p3io_ddr_eamid.id));
|
||||
security_rp3_generate_signed_eeprom_data(
|
||||
SECURITY_RP_UTIL_RP_TYPE_WHITE,
|
||||
&p3io_white_sign_key,
|
||||
&security_mcode_eamuse,
|
||||
&p3io_ddr_eamid,
|
||||
&eeprom_out);
|
||||
}
|
||||
|
||||
memcpy(eeprom, &eeprom_out, sizeof(struct security_rp3_eeprom));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// TODO coinstock
|
||||
@@ -0,0 +1,23 @@
|
||||
#ifndef DDRHOOK_P3IO_H
|
||||
#define DDRHOOK_P3IO_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "security/rp-sign-key.h"
|
||||
#include "security/rp3.h"
|
||||
|
||||
extern const wchar_t p3io_dev_node_prefix[];
|
||||
extern const wchar_t p3io_dev_node[];
|
||||
|
||||
void p3io_ddr_init(void);
|
||||
void p3io_ddr_init_with_plugs(
|
||||
const struct security_mcode *mcode,
|
||||
const struct security_id *pcbid,
|
||||
const struct security_id *eamid,
|
||||
const struct security_rp_sign_key *black_sign_key,
|
||||
const struct security_rp_sign_key *white_sign_key);
|
||||
void p3io_ddr_fini(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,178 @@
|
||||
#define LOG_MODULE "spike"
|
||||
|
||||
// clang-format off
|
||||
// Don't format because the order is important here
|
||||
#include <windows.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntdef.h>
|
||||
#include <ntddser.h>
|
||||
// clang-format on
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "acioemu/addr.h"
|
||||
#include "acioemu/emu.h"
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
static struct ac_io_emu spike_ac_io_emu;
|
||||
|
||||
static void spike_handle_broadcast(const struct ac_io_message *bcast);
|
||||
static void spike_handle_get_version(const struct ac_io_message *req);
|
||||
static void spike_handle_status(const struct ac_io_message *req);
|
||||
static void spike_send_empty(const struct ac_io_message *req);
|
||||
|
||||
void spike_init(void)
|
||||
{
|
||||
ac_io_emu_init(&spike_ac_io_emu, L"COM2");
|
||||
}
|
||||
|
||||
void spike_fini(void)
|
||||
{
|
||||
ac_io_emu_fini(&spike_ac_io_emu);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
spike_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
const struct ac_io_message *msg;
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (!ac_io_emu_match_irp(&spike_ac_io_emu, irp)) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
hr = ac_io_emu_dispatch_irp(&spike_ac_io_emu, irp);
|
||||
|
||||
if (hr != S_OK) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
msg = ac_io_emu_request_peek(&spike_ac_io_emu);
|
||||
|
||||
switch (msg->addr) {
|
||||
case 0:
|
||||
ac_io_emu_cmd_assign_addrs(&spike_ac_io_emu, msg, 7);
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
switch (ac_io_u16(msg->cmd.code)) {
|
||||
case AC_IO_CMD_GET_VERSION:
|
||||
spike_handle_get_version(msg);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_START_UP:
|
||||
spike_handle_status(msg);
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_CMD_KEEPALIVE:
|
||||
spike_send_empty(msg);
|
||||
|
||||
break;
|
||||
|
||||
case 0x100:
|
||||
case 0x110:
|
||||
case 0x112:
|
||||
case 0x128:
|
||||
spike_handle_status(msg);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning(
|
||||
"Spike ACIO unhandled cmd: %04X",
|
||||
ac_io_u16(msg->cmd.code));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case AC_IO_BROADCAST:
|
||||
spike_handle_broadcast(msg);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning(
|
||||
"Spike ACIO message on unhandled bus address: %d",
|
||||
msg->addr);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ac_io_emu_request_pop(&spike_ac_io_emu);
|
||||
}
|
||||
}
|
||||
|
||||
static void spike_handle_broadcast(const struct ac_io_message *bcast)
|
||||
{
|
||||
/* The payload consists of 7x10 byte chunks. Chunk 0 seems to correspond
|
||||
to the front LED strip, and chunks 1-6 are the 2x3 spike bundles on the
|
||||
side of a dedicab. No idea about the format of this data other than
|
||||
that, try figuring it out if you're bored. */
|
||||
}
|
||||
|
||||
static void spike_handle_get_version(const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.version);
|
||||
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_LED_SPIKE);
|
||||
resp.cmd.version.flag = 0x00;
|
||||
resp.cmd.version.major = 0x01;
|
||||
resp.cmd.version.minor = 0x01;
|
||||
resp.cmd.version.revision = 0x00;
|
||||
memcpy(
|
||||
resp.cmd.version.product_code,
|
||||
"DDRS",
|
||||
sizeof(resp.cmd.version.product_code));
|
||||
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
|
||||
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
|
||||
|
||||
ac_io_emu_response_push(&spike_ac_io_emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void spike_handle_status(const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = sizeof(resp.cmd.status);
|
||||
resp.cmd.status = 0x00;
|
||||
|
||||
ac_io_emu_response_push(&spike_ac_io_emu, &resp, 0);
|
||||
}
|
||||
|
||||
static void spike_send_empty(const struct ac_io_message *req)
|
||||
{
|
||||
struct ac_io_message resp;
|
||||
|
||||
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
|
||||
resp.cmd.code = req->cmd.code;
|
||||
resp.cmd.seq_no = req->cmd.seq_no;
|
||||
resp.cmd.nbytes = 0;
|
||||
|
||||
ac_io_emu_response_push(&spike_ac_io_emu, &resp, 0);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef DDRHOOK_SPIKE_H
|
||||
#define DDRHOOK_SPIKE_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
void spike_init(void);
|
||||
void spike_fini(void);
|
||||
HRESULT spike_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,204 @@
|
||||
#define LOG_MODULE "usbmem"
|
||||
|
||||
// clang-format off
|
||||
// Don't format because the order is important here
|
||||
#include <windows.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntdef.h>
|
||||
#include <ntddser.h>
|
||||
// clang-format on
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
#define USBMEM_BUF_SIZE 128
|
||||
|
||||
static HANDLE usbmem_fd;
|
||||
static char usbmem_response[USBMEM_BUF_SIZE];
|
||||
static bool usbmem_pending;
|
||||
|
||||
static HRESULT usbmem_open(struct irp *irp);
|
||||
static HRESULT usbmem_close(struct irp *irp);
|
||||
static HRESULT usbmem_write(struct irp *irp);
|
||||
static HRESULT usbmem_read(struct irp *irp);
|
||||
static HRESULT usbmem_ioctl(struct irp *irp);
|
||||
|
||||
void usbmem_init(void)
|
||||
{
|
||||
log_assert(usbmem_fd == NULL);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
hr = iohook_open_nul_fd(&usbmem_fd);
|
||||
|
||||
if (hr != S_OK) {
|
||||
log_fatal("Opening nul fd failed: %08lx", hr);
|
||||
}
|
||||
}
|
||||
|
||||
void usbmem_fini(void)
|
||||
{
|
||||
if (usbmem_fd != NULL) {
|
||||
CloseHandle(usbmem_fd);
|
||||
}
|
||||
|
||||
usbmem_fd = NULL;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
usbmem_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != usbmem_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN:
|
||||
return usbmem_open(irp);
|
||||
case IRP_OP_CLOSE:
|
||||
return usbmem_close(irp);
|
||||
case IRP_OP_READ:
|
||||
return usbmem_read(irp);
|
||||
case IRP_OP_WRITE:
|
||||
return usbmem_write(irp);
|
||||
case IRP_OP_IOCTL:
|
||||
return usbmem_ioctl(irp);
|
||||
default:
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT usbmem_open(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (!wstr_eq(irp->open_filename, L"COM3")) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
irp->fd = usbmem_fd;
|
||||
log_info("USB edit data PCB opened");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT usbmem_close(struct irp *irp)
|
||||
{
|
||||
log_info("USB edit data PCB closed");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT usbmem_write(struct irp *irp)
|
||||
{
|
||||
struct const_iobuf *src;
|
||||
char request[USBMEM_BUF_SIZE];
|
||||
uint32_t nbytes;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
log_assert(irp->write.bytes != NULL);
|
||||
|
||||
src = &irp->write;
|
||||
nbytes = src->nbytes > USBMEM_BUF_SIZE ? USBMEM_BUF_SIZE : src->nbytes;
|
||||
memcpy(request, src->bytes, nbytes);
|
||||
request[nbytes - 1] = '\0'; /* This is always a CR. */
|
||||
|
||||
log_misc(">%s", request);
|
||||
|
||||
if (strlen(request) > 0) {
|
||||
if (str_eq(request, "sver")) {
|
||||
str_cpy(
|
||||
usbmem_response,
|
||||
sizeof(usbmem_response),
|
||||
"done GQHDXJAA DJHACKRS");
|
||||
} else if (
|
||||
str_eq(request, "on_a") || str_eq(request, "on_b") ||
|
||||
str_eq(request, "offa") || str_eq(request, "offb")) {
|
||||
str_cpy(usbmem_response, sizeof(usbmem_response), "done");
|
||||
} else if (
|
||||
strncmp(request, "lma ", 4) == 0 ||
|
||||
strncmp(request, "lmb ", 4) == 0) {
|
||||
str_cpy(usbmem_response, sizeof(usbmem_response), "done");
|
||||
} else {
|
||||
str_cpy(usbmem_response, sizeof(usbmem_response), "not connected");
|
||||
}
|
||||
}
|
||||
|
||||
usbmem_pending = true;
|
||||
src->pos = nbytes;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT usbmem_read(struct irp *irp)
|
||||
{
|
||||
struct iobuf *dest;
|
||||
uint32_t rlength;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
log_assert(irp->read.bytes != NULL);
|
||||
|
||||
dest = &irp->read;
|
||||
|
||||
if (usbmem_pending) {
|
||||
if (strlen(usbmem_response) > 0) {
|
||||
log_misc("%s", usbmem_response);
|
||||
}
|
||||
|
||||
str_cat(usbmem_response, sizeof(usbmem_response), "\r>");
|
||||
usbmem_pending = false;
|
||||
}
|
||||
|
||||
rlength = strlen(usbmem_response);
|
||||
memcpy(dest->bytes, usbmem_response, rlength);
|
||||
memset(usbmem_response, 0, USBMEM_BUF_SIZE);
|
||||
|
||||
dest->pos = rlength;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT usbmem_ioctl(struct irp *irp)
|
||||
{
|
||||
SERIAL_STATUS *status;
|
||||
|
||||
log_assert(irp != NULL);
|
||||
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_SERIAL_GET_COMMSTATUS:
|
||||
if (irp->read.bytes == NULL) {
|
||||
log_warning(
|
||||
"IOCTL_SERIAL_GET_COMMSTATUS: Output buffer is NULL");
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
if (irp->read.nbytes < sizeof(*status)) {
|
||||
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Buffer is too small");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
status = (SERIAL_STATUS *) irp->read.bytes;
|
||||
status->Errors = 0;
|
||||
status->AmountInInQueue = usbmem_pending ? 1 : 0;
|
||||
|
||||
irp->read.pos = sizeof(*status);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#ifndef HOOK_USBMEM_H
|
||||
#define HOOK_USBMEM_H
|
||||
|
||||
void usbmem_init(void);
|
||||
void usbmem_fini(void);
|
||||
HRESULT usbmem_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user