From 4ea1397af21956201db611ecad871dc4cd11f9f9 Mon Sep 17 00:00:00 2001 From: CrazyRedMachine Date: Sat, 1 May 2021 23:07:18 +0200 Subject: [PATCH] aciodrv: add PANB support (+ aciotest handler) add aciodrv-proc module explanation : PANB works differently from other acio devices ; you send one "start auto input" command without expecting a reply, and then the piano keeps spamming "recv poll" commands on the acio bus and never replies to any other commands.. failure to process these messages quickly enough will saturate the serial buffer and cause checksum errors after a while. for this reason, aciodrv-proc module was added in order to create a thread which manages these recv poll commands and keeps the latest known button state in memory so that it can be retrieved easily. --- Module.mk | 1 + src/main/acio/acio.h | 5 ++ src/main/acio/panb.h | 42 +++++++++++++++ src/main/aciodrv-proc/Module.mk | 7 +++ src/main/aciodrv-proc/panb.c | 90 +++++++++++++++++++++++++++++++++ src/main/aciodrv-proc/panb.h | 32 ++++++++++++ src/main/aciodrv/Module.mk | 1 + src/main/aciodrv/device.c | 32 +++++++++--- src/main/aciodrv/device.h | 29 +++++++++++ src/main/aciodrv/panb.c | 80 +++++++++++++++++++++++++++++ src/main/aciodrv/panb.h | 56 ++++++++++++++++++++ src/main/aciotest/Module.mk | 2 + src/main/aciotest/main.c | 8 +++ src/main/aciotest/panb.c | 76 ++++++++++++++++++++++++++++ src/main/aciotest/panb.h | 12 +++++ 15 files changed, 465 insertions(+), 8 deletions(-) create mode 100644 src/main/acio/panb.h create mode 100644 src/main/aciodrv-proc/Module.mk create mode 100644 src/main/aciodrv-proc/panb.c create mode 100644 src/main/aciodrv-proc/panb.h create mode 100644 src/main/aciodrv/panb.c create mode 100644 src/main/aciodrv/panb.h create mode 100644 src/main/aciotest/panb.c create mode 100644 src/main/aciotest/panb.h diff --git a/Module.mk b/Module.mk index 8ff59e0..03bb582 100644 --- a/Module.mk +++ b/Module.mk @@ -77,6 +77,7 @@ avsvers_64 := 1700 1603 1601 1509 1508 imps += avs avs-ea3 include src/main/aciodrv/Module.mk +include src/main/aciodrv-proc/Module.mk include src/main/acioemu/Module.mk include src/main/aciomgr/Module.mk include src/main/aciotest/Module.mk diff --git a/src/main/acio/acio.h b/src/main/acio/acio.h index 6a3d395..4ea3459 100644 --- a/src/main/acio/acio.h +++ b/src/main/acio/acio.h @@ -9,6 +9,7 @@ #include "acio/hdxs.h" #include "acio/icca.h" #include "acio/kfca.h" +#include "acio/panb.h" #define AC_IO_SOF 0xAA #define AC_IO_ESCAPE 0xFF @@ -38,6 +39,7 @@ enum ac_io_node_type { AC_IO_NODE_TYPE_KFCA = 0x09060000, AC_IO_NODE_TYPE_BI2A = 0x0d060000, AC_IO_NODE_TYPE_RVOL = 0x09060001, + AC_IO_NODE_TYPE_PANB = 0x090E0000, }; #pragma pack(push, 1) @@ -78,6 +80,9 @@ struct ac_io_message { struct ac_io_kfca_poll_in kfca_poll_in; struct ac_io_kfca_poll_out kfca_poll_out; + struct ac_io_panb_poll_in panb_poll_in; + struct ac_io_panb_poll_out panb_poll_out; + struct ac_io_hdxs_output hdxs_output; }; } cmd; diff --git a/src/main/acio/panb.h b/src/main/acio/panb.h new file mode 100644 index 0000000..0ce6af0 --- /dev/null +++ b/src/main/acio/panb.h @@ -0,0 +1,42 @@ +#ifndef AC_IO_PANB_H +#define AC_IO_PANB_H + +#include + +#define AC_IO_CMD_PANB_POLL_REPLY 0x0110 +#define AC_IO_CMD_PANB_SEND_LAMP 0x0111 +#define AC_IO_CMD_PANB_START_AUTO_INPUT 0x0115 + +#define AC_IO_PANB_NUM_NODES 4 +#define AC_IO_PANB_MAX_KEYS (7*AC_IO_PANB_NUM_NODES) +#define AC_IO_PANB_MAX_KEYPAIRS (AC_IO_PANB_MAX_KEYS/2) + +struct ac_io_panb_keypair { + uint8_t key2 : 4; + uint8_t key1 : 4; +}; + +#pragma pack(push, 1) +struct ac_io_panb_poll_in { + /* last received command sequence number (start_auto_input / send_lamp) */ + uint8_t sub_seq1; + /* auto-increment sequence number for autopoll reports */ + uint8_t sub_seq2; + struct ac_io_panb_keypair keypair[AC_IO_PANB_MAX_KEYPAIRS]; +}; +#pragma pack(pop) + +struct ac_io_panb_color { + uint8_t red; + uint8_t green; + uint8_t blue; +}; + +#pragma pack(push, 1) +struct ac_io_panb_poll_out { + struct ac_io_panb_color key[AC_IO_PANB_MAX_KEYS]; +}; +#pragma pack(pop) + + +#endif diff --git a/src/main/aciodrv-proc/Module.mk b/src/main/aciodrv-proc/Module.mk new file mode 100644 index 0000000..e347f77 --- /dev/null +++ b/src/main/aciodrv-proc/Module.mk @@ -0,0 +1,7 @@ +libs += aciodrv-proc + +libs_aciodrv-proc := \ + +src_aciodrv-proc := \ + panb.c \ + diff --git a/src/main/aciodrv-proc/panb.c b/src/main/aciodrv-proc/panb.c new file mode 100644 index 0000000..ed1c172 --- /dev/null +++ b/src/main/aciodrv-proc/panb.c @@ -0,0 +1,90 @@ +#define LOG_MODULE "aciodrv-proc-panb" + +#include + +#include "aciodrv/device.h" +#include "aciodrv/panb.h" + +#include "util/thread.h" +#include "util/log.h" + +static int auto_poll_proc(void *auto_poll_param); +static int auto_poll_threadid; + +static CRITICAL_SECTION keypair_lock; +static struct ac_io_panb_keypair _keypair[AC_IO_PANB_MAX_KEYPAIRS]; + +static CRITICAL_SECTION auto_poll_stop_lock; +static bool auto_poll_stop; + +static int auto_poll_proc(void * param) +{ + struct aciodrv_device_ctx * device = (struct aciodrv_device_ctx *) param; + struct ac_io_panb_poll_in poll_in; + bool stop; + + do { + aciodrv_panb_recv_poll(device, &poll_in); + + EnterCriticalSection(&keypair_lock); + memcpy(_keypair, poll_in.keypair, AC_IO_PANB_MAX_KEYPAIRS); + LeaveCriticalSection(&keypair_lock); + + EnterCriticalSection(&auto_poll_stop_lock); + stop = auto_poll_stop; + LeaveCriticalSection(&auto_poll_stop_lock); + } while (!stop); + + return 0; +} + +bool aciodrv_proc_panb_init(struct aciodrv_device_ctx *device) +{ + log_assert(device); + + if (!aciodrv_panb_start_auto_input(device, 0, AC_IO_PANB_NUM_NODES)) { + return false; + } + + auto_poll_stop = false; + InitializeCriticalSection(&keypair_lock); + InitializeCriticalSection(&auto_poll_stop_lock); + auto_poll_threadid = thread_create(auto_poll_proc, (void *)device, 0x4000, 0); + + return true; +} + +bool aciodrv_proc_panb_get_state(uint8_t *button_state) +{ + struct ac_io_panb_keypair keypair[AC_IO_PANB_MAX_KEYPAIRS]; + + EnterCriticalSection(&keypair_lock); + memcpy(keypair, _keypair, AC_IO_PANB_MAX_KEYPAIRS); + LeaveCriticalSection(&keypair_lock); + + /* splice the keypairs into separate button values */ + for (int i=0; ifd, send_buf, send_buf_pos) != send_buf_pos) { @@ -209,7 +209,7 @@ static int aciodrv_device_receive(struct aciodrv_device_ctx *device, uint8_t *bu } #ifdef AC_IO_MSG_LOG - aciodrv_device_log_buffer("Recv (1)", device, recv_buf, recv_size); + aciodrv_device_log_buffer(device, "Recv (1)", recv_buf, recv_size); log_warning("Expected %d got %d", max_resp_size - 6, recv_buf[4]); #endif @@ -226,7 +226,7 @@ static int aciodrv_device_receive(struct aciodrv_device_ctx *device, uint8_t *bu result_size = recv_size - 1; #ifdef AC_IO_MSG_LOG - aciodrv_device_log_buffer("Recv (2)", device, buffer, result_size); + aciodrv_device_log_buffer(device, "Recv (2)", buffer, result_size); #endif if (checksum != recv_buf[recv_size - 1]) { @@ -402,7 +402,7 @@ const struct aciodrv_device_node_version *aciodrv_device_get_node_product_versio return &device->node_versions[node_id]; } -bool aciodrv_send_and_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size) +bool aciodrv_send(struct aciodrv_device_ctx *device, struct ac_io_message *msg) { msg->cmd.seq_no = device->msg_counter++; int send_size = offsetof(struct ac_io_message, cmd.raw) + msg->cmd.nbytes; @@ -418,15 +418,31 @@ bool aciodrv_send_and_recv(struct aciodrv_device_ctx *device, struct ac_io_messa if (aciodrv_device_send(device, (uint8_t *) msg, send_size) <= 0) { return false; } + return true; +} - uint16_t req_code = msg->cmd.code; - +bool aciodrv_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size) +{ #ifdef AC_IO_MSG_LOG log_info("[%p] Beginning recv: (%d b)", device->fd, max_resp_size); #endif if (aciodrv_device_receive(device, (uint8_t *) msg, max_resp_size) <= 0) { return false; } + return true; +} + +bool aciodrv_send_and_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size) +{ + if (!aciodrv_send(device, msg)) { + return false; + } + + uint16_t req_code = msg->cmd.code; + + if (!aciodrv_recv(device, msg, max_resp_size)) { + return false; + } if (req_code != msg->cmd.code) { log_warning( diff --git a/src/main/aciodrv/device.h b/src/main/aciodrv/device.h index 52f5f41..d0e4b76 100644 --- a/src/main/aciodrv/device.h +++ b/src/main/aciodrv/device.h @@ -74,6 +74,35 @@ const struct aciodrv_device_node_version *aciodrv_device_get_node_product_versio */ bool aciodrv_send_and_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size); +/** + * Send a message to the ACIO bus. + * + * @param device Context of opened device + * @param msg Msg to send to the bus. + * @return True on success, false on error. + * @note Prefer the use of aciodrv_send_and_recv when possible. This is for commands which don't trigger a reply. + */ +bool aciodrv_send(struct aciodrv_device_ctx *device, struct ac_io_message *msg); + +/** + * Read a message from the ACIO bus. + * + * @param device Context of opened device + * @param msg Msg to send to the bus. Make sure that the buffer + * is big enough to receive the response. + * @return True on success, false on error. + * @note Prefer the use of aciodrv_send_and_recv when possible. This is for unsollicited incoming messages. + */ +bool aciodrv_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size); + +/** + * Reset an opened device. + * + * @param device Context of opened device + * @return Total num of nodes enumerated on the ACIO device. + */ +bool aciodrv_device_reset(struct aciodrv_device_ctx *device); + /** * Close the previously opened ACIO device. * diff --git a/src/main/aciodrv/panb.c b/src/main/aciodrv/panb.c new file mode 100644 index 0000000..b3123cc --- /dev/null +++ b/src/main/aciodrv/panb.c @@ -0,0 +1,80 @@ +#define LOG_MODULE "aciodrv-panb" + +#include + +#include "aciodrv/device.h" +#include "aciodrv/panb.h" + +#include "util/thread.h" +#include "util/log.h" + +bool aciodrv_panb_start_auto_input(struct aciodrv_device_ctx *device, uint8_t node_id, uint8_t node_count) +{ + log_assert(device); + struct ac_io_message msg; + + /* only the first node is handling the commands */ + if (node_id != 0) { + return true; + } + + msg.addr = node_id + 1; + msg.cmd.code = ac_io_u16(AC_IO_CMD_PANB_START_AUTO_INPUT); + msg.cmd.nbytes = 1; + msg.cmd.count = node_count; + + if (!aciodrv_send(device, &msg)) { + log_warning("Starting auto input failed"); + return false; + } + + log_info("Started auto input for %d nodes", node_count); + + return true; +} + +bool aciodrv_panb_recv_poll(struct aciodrv_device_ctx *device, struct ac_io_panb_poll_in *poll_in) +{ + log_assert(device); + struct ac_io_message msg; + struct ac_io_panb_poll_in *poll_res = (struct ac_io_panb_poll_in *) &msg.cmd.raw; + + msg.cmd.code = ac_io_u16(AC_IO_CMD_PANB_POLL_REPLY); + msg.cmd.nbytes = sizeof(struct ac_io_panb_poll_in); + + if (!aciodrv_recv(device, + &msg, offsetof(struct ac_io_message, cmd.raw) + msg.cmd.nbytes + 1)) { + log_warning("Getting state failed"); + return false; + } + + if (poll_in != NULL){ + memcpy(poll_in, poll_res, sizeof(struct ac_io_panb_poll_in)); + } + + return true; +} + +bool aciodrv_panb_send_lamp(struct aciodrv_device_ctx *device, + uint8_t node_id, struct ac_io_panb_poll_out *state) +{ + log_assert(device); + struct ac_io_message msg; + + /* only the first node is handling the commands */ + if (node_id != 0) { + return true; + } + + msg.addr = node_id + 1; + msg.cmd.code = ac_io_u16(AC_IO_CMD_PANB_SEND_LAMP); + msg.cmd.nbytes = 0x54; + memcpy(&msg.cmd.raw, state, sizeof(struct ac_io_panb_poll_out)); + + if (!aciodrv_send(device, &msg)) { + log_warning("Sending lamp state failed"); + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/main/aciodrv/panb.h b/src/main/aciodrv/panb.h new file mode 100644 index 0000000..e85ee4a --- /dev/null +++ b/src/main/aciodrv/panb.h @@ -0,0 +1,56 @@ +#ifndef ACIODRV_PANB_H +#define ACIODRV_PANB_H + +#include "acio/panb.h" + +/** + * Send the AC_IO_CMD_PANB_START_AUTO_INPUT command on a PANB device. + * + * @param device Context of opened device. + * @param node_id node_id Id of the node to query (0 based). + * @param node_count The number of nodes to poll. + * @return True if successful, false on error. + * @note node_id should be 0, PANB internally manages the other nodes. + * @note Upon calling, the device will stop replying to commands and just keep sending + * AC_IO_CMD_PANB_POLL_REPLY messages indefinitely. Failure to retrieve them fast enough + * will cause the device to malfunction. It is thus advised to make use of the aciodrv-proc + * module to spawn a thread that will handle these messages and provide an easy access to the + * latest input state. + * @note This module is supposed to be used in combination with the common + * device driver foundation. + * @see driver.h + */ +bool aciodrv_panb_start_auto_input(struct aciodrv_device_ctx *device, uint8_t node_id, uint8_t node_count); + +/** + * Retrieve a AC_IO_CMD_PANB_POLL_REPLY message from a PANB device. This assumes that there + * is such message incoming (ie. that start_auto_input has been called prior). + * + * @param device Context of opened device. + * @param poll_in Buffer to hold the received message, or NULL. + * @return True if successful, false on error. + * @note node_id should be 0, PANB internally manages the other nodes. + * @note Failure to retrieve the incoming messages fast enough will cause the device to malfunction. + * It is thus advised to make use of the aciodrv-proc module to spawn a thread that will handle these + * messages and provide an easy access to the latest input state. + * @note This module is supposed to be used in combination with the common + * device driver foundation. + * @see driver.h + */ +bool aciodrv_panb_recv_poll(struct aciodrv_device_ctx *device, struct ac_io_panb_poll_in *poll_in); + +/** + * Light lamps on a PANB device. + * + * @param node_id Id of the node to query (0 based). + * @param state Pointer to a lamp state struct + * (mandatory). + * @return True on success, false on error. + * @note node_id should be 0, PANB internally manages the other nodes. + * @note This module is supposed to be used in combination with the common + * device driver foundation. + * @see driver.h + */ +bool aciodrv_panb_send_lamp(struct aciodrv_device_ctx *device, uint8_t node_id, struct ac_io_panb_poll_out *state); + +#endif \ No newline at end of file diff --git a/src/main/aciotest/Module.mk b/src/main/aciotest/Module.mk index 339db10..1c66387 100644 --- a/src/main/aciotest/Module.mk +++ b/src/main/aciotest/Module.mk @@ -3,10 +3,12 @@ exes += aciotest libs_aciotest := \ bio2drv \ aciodrv \ + aciodrv-proc \ util \ src_aciotest := \ icca.c \ kfca.c \ bi2a-sdvx.c \ + panb.c \ main.c \ diff --git a/src/main/aciotest/main.c b/src/main/aciotest/main.c index 010f6f3..e773db7 100644 --- a/src/main/aciotest/main.c +++ b/src/main/aciotest/main.c @@ -10,6 +10,7 @@ #include "aciotest/handler.h" #include "aciotest/icca.h" #include "aciotest/kfca.h" +#include "aciotest/panb.h" #include "util/log.h" @@ -37,6 +38,13 @@ static bool aciotest_assign_handler( return true; } + if (!memcmp(product, "PANB", 4)) { + handler->init = aciotest_panb_handler_init; + handler->update = aciotest_panb_handler_update; + + return true; + } + if (!memcmp(product, "BI2A", 4)) { if (bi2a_mode == 0) { handler->init = aciotest_bi2a_sdvx_handler_init; diff --git a/src/main/aciotest/panb.c b/src/main/aciotest/panb.c new file mode 100644 index 0000000..0d6ae50 --- /dev/null +++ b/src/main/aciotest/panb.c @@ -0,0 +1,76 @@ +#include "aciotest/panb.h" + +#include +#include + +#include "aciodrv/panb.h" +#include "aciodrv-proc/panb.h" + +struct panb_handler_ctx { + bool running; +}; + +bool aciotest_panb_handler_init(struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx) +{ + if (node_id != 0) { + return true; + } + + *ctx = malloc(sizeof(struct panb_handler_ctx)); + struct panb_handler_ctx *panb_ctx = (struct panb_handler_ctx*)*ctx; + panb_ctx->running = true; + + return aciodrv_proc_panb_init(device); +} + +bool aciotest_panb_handler_update(struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx) +{ + uint8_t button[AC_IO_PANB_MAX_KEYS]; + struct ac_io_panb_poll_out state; + struct panb_handler_ctx *panb_ctx = (struct panb_handler_ctx *) ctx; + + if (node_id != 0) { + return true; + } + + if (!panb_ctx->running) { + printf(">>> PANB:\nDevice has been closed. Press Ctrl+C to exit."); + return true; + } + + if (!aciodrv_proc_panb_get_state(button)) { + return false; + } + + printf(">>> PANB:\nPress first and last keys to close device\n"); + + for (int i=0; irunning = false; + return true; + } + + /* light the panel */ + for (int i=0; i +#include + +#include "aciodrv/device.h" + +bool aciotest_panb_handler_init(struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx); +bool aciotest_panb_handler_update(struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx); + +#endif \ No newline at end of file