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