From a9a38d387f98b29b68f97b8a9c6858d2b399caf0 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 29 May 2024 17:41:41 +0200 Subject: WIP puzzle bus driver rework --- shared/pb/bus.h | 18 ++++++++++++++++++ shared/pb/driver.c | 31 +++++++++++++++++++++++++++++++ shared/pb/driver.h | 24 ++++++++++++++++++++++++ shared/pb/types.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 shared/pb/bus.h create mode 100644 shared/pb/driver.c create mode 100644 shared/pb/driver.h create mode 100644 shared/pb/types.h (limited to 'shared/pb') diff --git a/shared/pb/bus.h b/shared/pb/bus.h new file mode 100644 index 0000000..6f464c3 --- /dev/null +++ b/shared/pb/bus.h @@ -0,0 +1,18 @@ +#pragma once + +// Adafruit NeoTrellis modules +#define BUSADDR_ADA_NEO_1 0x2E +#define BUSADDR_ADA_NEO_2 0x2F +#define BUSADDR_ADA_NEO_3 0x30 +#define BUSADDR_ADA_NEO_4 0x32 + +// TODO: ??? +#define BUSADDR_MOD_NEOTRELLIS 0 +#define BUSADDR_MOD_SOFTWARE 0 +#define BUSADDR_MOD_HARDWARE 0 +#define BUSADDR_MOD_VAULT 0 +// #define BUSADDR_MOD_AUTOMATION 0 + +// main controller +#define BUSADDR_MAIN 0x00 + diff --git a/shared/pb/driver.c b/shared/pb/driver.c new file mode 100644 index 0000000..2552b61 --- /dev/null +++ b/shared/pb/driver.c @@ -0,0 +1,31 @@ +#include "types.h" +#include "driver.h" + +__weak bool pbdrv_hook_cmd() { + return false; +} + +__weak void pbdrv_i2c_recv(uint16_t addr, const char * buf, size_t sz) { + if (sz == 0) return; + enum pb_cmd cmd = (enum pb_cmd) buf[0]; + + // shift buffer pointer to only contain the puzzle bus message buf + buf++; + sz--; + + // allow user to override command handler while still using this weak + // function + if (pbdrv_hook_cmd(cmd, buf, sz)) return; + + switch (cmd) { + case PB_CMD_READ: return pbdrv_handle_read(buf, sz); + // case PB_CMD_WRITE: return pbdrv_handle_write(buf, sz); + // case PB_CMD_MAGIC: return pbdrv_handle_magic(buf, sz); + default: return; + } +} + +__weak void pbdrv_i2c_send(uint16_t addr, const char * buf, size_t sz) { + return; +} + diff --git a/shared/pb/driver.h b/shared/pb/driver.h new file mode 100644 index 0000000..1c9c3a3 --- /dev/null +++ b/shared/pb/driver.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void pbdrv_i2c_recv(uint16_t addr, const char * buf, size_t sz); +void pbdrv_i2c_send(uint16_t addr, const char * buf, size_t sz); + +void pbdrv_hook_state(enum pb_global_state * state, bool rw); +bool pbdrv_hook_cmd(enum pb_cmd cmd, const char * buf, size_t sz); + +void pbdrv_handle_read(const char * buf, size_t sz); + +#ifdef __cplusplus +} +#endif + diff --git a/shared/pb/types.h b/shared/pb/types.h new file mode 100644 index 0000000..8150194 --- /dev/null +++ b/shared/pb/types.h @@ -0,0 +1,52 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#define __packed __attribute__((packed)) +#define __weak __attribute__((weak)) +#endif +#ifndef __packed +#error Could not determine packed attribute for current compiler +#define __packed +#endif +#ifndef __weak +#error Could not determine weak attribute for current compiler +#define __weak +#endif + +/** + * \brief puzzle bus command types + * + * The first byte of a puzzle bus message's data indicates the command type. + */ +enum __packed pb_cmd { + PB_CMD_READ, //!< read a puzzle module property + PB_CMD_WRITE, //!< write to a puzzle module property + // PB_CMD_UPDATE, //!< request an update +}; + +/** \brief Puzzle bus global states */ +enum __packed pb_global_state { + PB_GS_NOINIT, //!< uninitialized (only used by puzzle modules) + PB_GS_IDLE, //!< puzzle not started yet + PB_GS_PLAYING, //!< puzzle actively being solved + PB_GS_SOLVED, //!< puzzle completed +}; + +typedef struct __packed { + uint8_t address; +} pb_cmd_read_t; + +typedef struct __packed { + uint8_t address; + uint8_t data[]; +} pb_cmd_write_t; + +#ifdef __cplusplus +} +#endif + -- cgit v1.2.3 From 2f443639c159f510b121fb8b3d7ce6aae61b06fb Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 30 May 2024 12:38:28 +0200 Subject: implement pbdrv --- puzzle/spec.adoc | 158 ---------------------------------------------------- shared/pb/driver.c | 102 +++++++++++++++++++++++++++++---- shared/pb/driver.h | 52 ++++++++++++++--- shared/pb/spec.adoc | 127 +++++++++++++++++++++++++++++++++++++++++ shared/pb/types.h | 20 +++++-- 5 files changed, 279 insertions(+), 180 deletions(-) delete mode 100644 puzzle/spec.adoc create mode 100644 shared/pb/spec.adoc (limited to 'shared/pb') diff --git a/puzzle/spec.adoc b/puzzle/spec.adoc deleted file mode 100644 index fbd1033..0000000 --- a/puzzle/spec.adoc +++ /dev/null @@ -1,158 +0,0 @@ -= Puzzle module specification - -This document contains a subset of the puzzle bus protocol specification, and -is targeted at puzzle module developers. It describes the required -implementation steps for integrating a new game into the puzzle module -framework. All constants and types mentioned in this document are defined in a -C header: link:../shared/puzbus.h[]. - -== The bus - -The puzzle bus carries data over a standard I^2^C bus. Additional details about -this bus can be found in the link:../docs/design.adoc[Design document]. This -document only describes the *data* that is sent over the I^2^C bus. Addresses -also influence the puzzle box's behavior, and are also further explained in the -Design document. - -== Messages - -All puzzle bus messages follow the following format: - -[%autowidth] -|=== -| ``pb_cmd_t command`` | ``uint8_t data[]`` -|=== - -The format of ``data[]`` is determined by the message type. These messages are -the core of the puzzle bus framework, and follow these rules: - -- Only the main controller takes initiative to send messages (i.e. puzzle - modules wait until they are asked to give information, and do not immediately - send new information when internal updates occur, etc.) -- Puzzle modules can only reply to messages sent from the main controller -- Messages from the main controller to puzzle modules are sent as *I^2^C read* - commands (even ``PB_CMD_WRITE`` commands) -- Reponses from the puzzle modules back to the main contorller are sent as - *I^2^C write* commands - -In order to properly integrate with the puzzle module framework, a puzzle -module must do the following: - -- <> -- Keep a global state variable of type ``pb_state_t`` -- <> -- <> - -[[sec:cmd-magic]] -=== Magic - -The puzzle module will receive a message of type ``PB_CMD_MAGIC`` with data -being equal to ``pb_magic_msg``. Upon verifying that the message data indeed -matches ``pb_magic_msg``, the puzzle module sends back a message of type -``PB_CMD_MAGIC`` with data ``pb_magic_res``. This concludes the puzzle module -registration process. - -Example C code: - -```c -#include "puzbus.h" - -void pb_cmd_magic_handler(const char * data, size_t sz) { - if (strncmp(buf, pb_magic_msg, sizeof(pb_magic_msg)) != 0) return; - - const char res[] = { - PB_CMD_MAGIC, - pb_magic_res, - }; - - i2c_write(BUSADDR_MAIN, res, sizeof(res)); -} - -void i2c_read_handle(uint16_t addr, const char * buf, size_t sz) { - if (sz < 1) return; - pb_cmd_t cmd = (pb_cmd_t) buf[0]; - - // shift buffer pointer to only contain the puzzle bus message data - buf++; sz--; - - if (cmd == PB_CMD_MAGIC) pb_cmd_magic_handler(buf, sz); -} -``` - -[[sec:state-global]] -=== Global state - -Each puzzle module keeps a single 'global state' variable of type -``pb_state_t`` that represents the abstracted state of the implemented game. -Games may implement additional state variables, but the global state is -required for integration with the puzzle framework. - -IMPORTANT: Keep in mind that the global state may be overwritten by a game -operator, which the puzzle module must be able to handle and react to -accordingly. Writing to state variables is explained further in the section -about <>. - -The main controller periodically informs each puzzle module of the combined -puzzle box state, and simultaniously requests each module's state back. This -message has type ``PB_CMD_EXCHANGE``, and the ``data[]`` of this message -includes a single ``pb_state_t`` representing the combined box state. The -puzzle module responds to this message with a message of type -``PB_CMD_EXCHANGE``, where ``data[]`` is the puzzle module's own global state. - -Example C code: - -```c -#include "puzbus.h" -pb_state_t module_state = PB_STATE_NOINIT; - -void pb_cmd_exchange_handler(const char * data, size_t sz) { - if (sz < 1) return; - pb_state_t main_state = (pb_state_t) data[0]; - - // printf("main state is now %d\n", main_state); - - const char res[] = { - PB_CMD_EXCHANGE, - (char) module_state, - }; - - i2c_write(BUSADDR_MAIN, res, sizeof(res)); -} - -// if (cmd == PB_CMD_EXCHANGE) pb_cmd_exchange_handler(buf, sz); -// (see above listing) -``` - -[[sec:state-aux]] -=== Auxiliary state - -The ``PB_CMD_READ`` command may be used to read game state variables exposed to -the puzzle bus interface directly. This command has the following format: - -[%autowidth] -|=== -| ``PB_CMD_READ`` | ``uint8_t address`` -|=== - -Similarly, a ``PB_CMD_WRITE`` command exists for writing to these variables: - -[%autowidth] -|=== -| ``PB_CMD_WRITE`` | ``uint8_t address`` | ``char data[]`` -|=== - -The format of ``data[]`` is determined by the data type at ``address``. Puzzle -modules are required to have a <> variable, and -the global state *must* be readable and writeable on address 0. This results in -``address[0]`` always having a data type of ``pb_state_t``. All other addresses -are unused by the puzzle module framework, and may be used for implementing -puzzle-specific functionality. - -When a puzzle module receives a ``PB_CMD_READ`` command, it will reply with -``PB_CMD_WRITE`` to the main controller with the same address, but with -``data[]`` now filled with the corresponding data at ``address``. - -NOTE: The ``PB_CMD_WRITE`` reply must be an *I^2^C write* command to avoid -being interpreted as a write requests from the puzzle module to the main -controller. - diff --git a/shared/pb/driver.c b/shared/pb/driver.c index 6b675ca..f43d5c1 100644 --- a/shared/pb/driver.c +++ b/shared/pb/driver.c @@ -1,11 +1,29 @@ +#include + #include "types.h" #include "driver.h" -__weak bool pbdrv_hook_cmd() { +/** \brief [private] placeholder global state variable */ +static pb_state_t _global_state = PB_GS_NOINIT; + +/** \brief [private] main controller global state */ +static pb_state_t _main_state = PB_GS_NOINIT; + +__weak pb_state_t pbdrv_hook_mod_state_read() { + return _global_state; +} + +__weak void pbdrv_hook_mod_state_write(pb_state_t state) { + _global_state = state; +} + +__weak void pbdrv_hook_main_state_update(pb_state_t state) { } + +__weak bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz) { return false; } -__weak void pbdrv_i2c_recv(uint16_t addr, const char * buf, size_t sz) { +__weak void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz) { if (sz == 0) return; pb_cmd_t cmd = (enum pb_cmd) buf[0]; @@ -13,19 +31,83 @@ __weak void pbdrv_i2c_recv(uint16_t addr, const char * buf, size_t sz) { buf++; sz--; - // allow user to override command handler while still using this weak - // function - if (pbdrv_hook_cmd(cmd, buf, sz)) return; + // allow user to implement custom commands + if (pbdrv_hook_cmd(i2c_addr, cmd, buf, sz)) + return; switch (cmd) { - case PB_CMD_READ: return pbdrv_handle_read(buf, sz); - // case PB_CMD_WRITE: return pbdrv_handle_write(buf, sz); - // case PB_CMD_MAGIC: return pbdrv_handle_magic(buf, sz); + case PB_CMD_READ: return pbdrv_handle_read(i2c_addr, buf, sz); + case PB_CMD_WRITE: return pbdrv_handle_write(i2c_addr, buf, sz); + case PB_CMD_MAGIC: return pbdrv_handle_magic(i2c_addr, buf, sz); + case PB_CMD_SEX: return pbdrv_handle_sex(i2c_addr, buf, sz); + default: return; + } +} + +__weak void pbdrv_handle_read(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz == 0) return; + pb_cmd_read_t * cmd = (pb_cmd_read_t *) buf; + + // allow user to addrimplement custom read handlers + if (pbdrv_hook_read(i2c_addr, cmd->address)) + return; + + switch (cmd->address) { + case PB_ADDR_GS: { + char res[] = { + PB_CMD_READ, + PB_ADDR_GS, + pbdrv_hook_mod_state_read(), + }; + return pbdrv_i2c_send(i2c_addr, res, sizeof(res)); + } + default: return; + } +} + +__weak void pbdrv_handle_write(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz < 2) return; // must have address and at least 1 byte data + pb_cmd_write_t * cmd = (pb_cmd_write_t *) buf; + + // allow user to implement custom read handlers + if (pbdrv_hook_write(i2c_addr, cmd->address, (char *) cmd->data, sz - 1)) + return; + + switch (cmd->address) { + case PB_ADDR_GS: + pbdrv_hook_mod_state_write(cmd->data[0]); + break; default: return; } } -__weak void pbdrv_i2c_send(uint16_t addr, const char * buf, size_t sz) { - return; +__weak void pbdrv_handle_magic(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz != sizeof(pb_magic_msg)) return; + if (memcmp(buf, pb_magic_msg, sizeof(pb_magic_msg)) != 0) return; + + size_t res_size = sizeof(pb_cmd_t) + sizeof(pb_magic_res); + char res[res_size]; + res[0] = PB_CMD_MAGIC; + memcpy(res, pb_magic_res, sizeof(pb_magic_res)); + + pbdrv_i2c_send(i2c_addr, res, res_size); +} + +__weak void pbdrv_handle_sex(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz == 0) return; + pb_cmd_sex_t * cmd = (pb_cmd_sex_t *) buf; + + // send own state + char res[] = { + PB_CMD_SEX, + pbdrv_hook_mod_state_read(), + }; + pbdrv_i2c_send(i2c_addr, res, sizeof(res)); + + if (cmd->main_state == _main_state) return; + // keep main controller state + _main_state = cmd->main_state; + // call update if main state changed + pbdrv_hook_main_state_update(_main_state); } diff --git a/shared/pb/driver.h b/shared/pb/driver.h index b5b2784..c4e1167 100644 --- a/shared/pb/driver.h +++ b/shared/pb/driver.h @@ -1,22 +1,60 @@ #pragma once +/** + * \file puzzle bus driver implementation + * + * Most \c pbdrv_* functions have a weak implementation, which may be + * overwritten by a custom implementation. This allows you to use the default + * implementation where possible, and only implement extensions required for + * your puzzle module. Please see spec.adoc for more information about how to + * use the puzzle bus driver library. + */ + #include #include #include #include "types.h" +#ifndef PBDRV_MOD_NAME +#define PBDRV_MOD_NAME "???" +#endif + #ifdef __cplusplus extern "C" { #endif -void pbdrv_i2c_recv(uint16_t addr, const char * buf, size_t sz); -void pbdrv_i2c_send(uint16_t addr, const char * buf, size_t sz); - -void pbdrv_hook_state(pb_state_t * state, bool rw); -bool pbdrv_hook_cmd(pb_cmd_t cmd, const char * buf, size_t sz); - -void pbdrv_handle_read(const char * buf, size_t sz); +void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz); +void pbdrv_i2c_send(uint16_t i2c_addr, const char * buf, size_t sz); + +pb_state_t pbdrv_hook_mod_state_read(); +void pbdrv_hook_mod_state_write(pb_state_t state); +void pbdrv_hook_main_state_update(pb_state_t state); + +/** + * \name hooks + * + * Implementing this function allows you to use the weak implementation of \c + * pbdrv_i2c_recv() while being able to implement custom command handlers. + * + * \return true if the cmd was recognized, or false to forward the command to + * the default handlers + * + * \{ + */ + +/** \brief cmd receive hook */ +bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz); +/** \brief read cmd hook */ +bool pbdrv_hook_read(uint16_t i2c_addr, uint8_t addr); +/** \brief write cmd hook */ +bool pbdrv_hook_write(uint16_t i2c_addr, uint8_t addr, const char * buf, size_t sz); +//! \} + +void pbdrv_handle_read(uint16_t i2c_addr, const char * buf, size_t sz); +void pbdrv_handle_write(uint16_t i2c_addr, const char * buf, size_t sz); +void pbdrv_handle_magic(uint16_t i2c_addr, const char * buf, size_t sz); +void pbdrv_handle_sex(uint16_t i2c_addr, const char * buf, size_t sz); #ifdef __cplusplus } diff --git a/shared/pb/spec.adoc b/shared/pb/spec.adoc new file mode 100644 index 0000000..a99497b --- /dev/null +++ b/shared/pb/spec.adoc @@ -0,0 +1,127 @@ += Puzzle module specification + +This folder contains an implementation of the puzzle bus protocol +specification, and is targeted at puzzle module developers. This document +describes the required implementation steps for integrating a new game into the +puzzle module framework. + +== The bus + +The puzzle bus carries data over a standard I^2^C bus. Additional details about +this bus can be found in the link:../../docs/design.adoc[Design document]. + +NOTE: Addresses influence the puzzle box's behavior, as the order of puzzles is +determined by the puzzle module address. Two puzzle modules may use the same +address, but this will mean that they cannot be used simultaniously in the same +puzzle box. Known addresses are documented in link:bus.h[]. + +== Puzzle bus driver (pbdrv) + +The library in this folder is a partial implementation of the puzzle bus +specification *for puzzle modules*. Most functions in the driver are marked +with the 'weak' attribute, which allows you to override them by providing an +implementation. + +In order to utilize this driver, the following must be done: + +- The ``pbdrv_i2c_recv`` function must be *called* for every received *I^2^C + read* frame +- The ``pbdrv_i2c_send`` function must be *implemented* with the + platform-specific *I^2^C write* function + +This is enough to get the puzzle module registered. You may also want to +implement some of the following integrations: + +- If your game uses the global state variable, you should implement the + <> to point the driver to your own + global state variable, and be notified of reads/writes to it. +- If you want to expose additional game state variables over the puzzle bus, + you should implement the <>. +- If you want to implement custom puzzle bus commands, you can implement the + <>. + +All other kinds of integrations/hooks can likely be realized by overriding the +default implementations, but this is discouraged. + +[[sec:state-global]] +== Global state + +If your puzzle module defines its own ``pb_state_t``, you can tell the driver +to use it by implementing the ``pbdrv_hook_state_read`` and +``pbdrv_hook_state_write`` functions. These functions are also used by the +default implementation of the read/write commands to address 0 (global state). + +Example: + +```c +pb_state_t global_state = PB_GS_NOINIT; + +pb_state_t pbdrv_hook_mod_state_read() { + return global_state; +} + +void pbdrv_hook_mod_state_write(pb_state_t state) { + global_state = state; +} +``` + +[[sec:state-aux]] +== Auxiliary state + +You can expose additional state variables by implementing the +``pbdrv_hook_read`` and ``pbdrv_hook_write`` functions. These functions should +return ``true`` for state addresses you want to override. + +Example: + +```c +#define CUSTOM_VAR_ADDR 0x01 +uint8_t my_custom_variable = 10; + +bool pbdrv_hook_read(uint16_t i2c_addr, uint8_t addr) { + switch (addr) { + case CUSTOM_VAR_ADDR: { + char res[] = { PB_CMD_READ, addr, my_custom_variable }; + pbdrv_i2c_send(i2c_addr, res, sizeof(res)); + break; + } + default: return false; + } + + return true; +} + +bool pbdrv_hook_write(uint16_t i2c_addr, uint8_t addr, const char * buf, size_t sz) { + switch (addr) { + case CUSTOM_VAR_ADDR: { + if (sz != 1) return false; + my_custom_variable = buf[0]; + break; + } + default: return false; + } + + return true; +} +``` + +[[sec:cmd]] +== Custom commands + +Similar to the auxiliary state, custom commands can be added by implementing +the ``pbdrv_hook_cmd`` function, which should return ``true`` for the +command(s) that you want to overwrite. + +Example: + +```c +bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz) { + if (cmd == 0x54) { + printf("custom command received!\n"); + return true; + } + + return false; +} +``` + diff --git a/shared/pb/types.h b/shared/pb/types.h index 93e2f0c..7996a19 100644 --- a/shared/pb/types.h +++ b/shared/pb/types.h @@ -26,12 +26,14 @@ extern "C" { enum __packed pb_cmd { PB_CMD_READ, //!< read a puzzle module property PB_CMD_WRITE, //!< write to a puzzle module property - // PB_CMD_UPDATE, //!< request an update - PB_CMD_MAGIC = 0x69, //!< magic message + PB_CMD_SEX, //!< state exchange + PB_CMD_MAGIC, //!< magic message }; typedef enum pb_cmd pb_cmd_t; +/** \brief magic sent from main controller to puzzle module */ static const char pb_magic_msg[] = { 0x70, 0x75, 0x7a, 0x62, 0x75, 0x73 }; +/** \brief magic reply from puzzle module back to main controller */ static const char pb_magic_res[] = { 0x67, 0x61, 0x6d, 0x69, 0x6e, 0x67 }; /** \brief Puzzle bus global states */ @@ -44,14 +46,22 @@ enum __packed pb_state { typedef enum pb_state pb_state_t; typedef struct __packed { - uint8_t address; + const uint8_t address; } pb_cmd_read_t; typedef struct __packed { - uint8_t address; - uint8_t data[]; + const uint8_t address; + const uint8_t data[]; } pb_cmd_write_t; +typedef struct __packed { + const pb_state_t main_state; +} pb_cmd_sex_t; + +enum __packed { + PB_ADDR_GS = 0x00, //!< global state address +}; + #ifdef __cplusplus } #endif -- cgit v1.2.3 From b231e9d808f40aef0895787ea09624787b10addd Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 30 May 2024 15:51:08 +0200 Subject: more client i2c shuffling --- client/CMakeLists.txt | 2 +- client/cmd.cpp | 3 +- client/i2c.cpp | 37 ++++++++++++++++ client/i2c.h | 8 ++++ client/sock.cpp | 31 +------------ client/sock.h | 3 -- shared/include.cmake | 2 +- shared/pb/driver.c | 113 ----------------------------------------------- shared/pb/driver.h | 62 -------------------------- shared/pb/moddrv.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++ shared/pb/moddrv.h | 62 ++++++++++++++++++++++++++ 11 files changed, 229 insertions(+), 212 deletions(-) create mode 100644 client/i2c.cpp create mode 100644 client/i2c.h delete mode 100644 shared/pb/driver.c delete mode 100644 shared/pb/driver.h create mode 100644 shared/pb/moddrv.c create mode 100644 shared/pb/moddrv.h (limited to 'shared/pb') diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 50d3cd7..cb891a6 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -20,11 +20,11 @@ add_executable(pbc cmd.cpp parse.cpp xxd.c + i2c.cpp ) target_link_libraries(pbc i2ctcp - puzbus mpack readline # this is such a common library that I did not bother adding it as a submodule ) diff --git a/client/cmd.cpp b/client/cmd.cpp index e3c9eb9..5ac2ff3 100644 --- a/client/cmd.cpp +++ b/client/cmd.cpp @@ -4,10 +4,9 @@ #include #include "cmd.h" -#include "i2ctcpv1.h" #include "pb/types.h" #include "rl.h" -#include "sock.h" +#include "i2c.h" #include "parse.h" #include "pb/bus.h" diff --git a/client/i2c.cpp b/client/i2c.cpp new file mode 100644 index 0000000..fd29e1e --- /dev/null +++ b/client/i2c.cpp @@ -0,0 +1,37 @@ +#include +#include + +#include "i2ctcpv1.h" +#include "sock.h" +#include "xxd.h" + +bool i2c_dump_send = false; +bool i2c_dump_recv = true; + +void i2c_send(uint16_t addr, const char * data, size_t data_size) { + i2ctcp_msg_t msg = { + .addr = addr, + .data = (char *) data, + .length = data_size, + }; + + char* packed; + size_t size; + if (!i2ctcp_write(&msg, &packed, &size)) return; + + sock->send(packed, size); + if (i2c_dump_send) { + printf("[%s] addr(0x%02x) data(0x%02lx):\n", __FUNCTION__, addr, data_size); + xxd(data, data_size); + } + + free(packed); +} + +void i2c_recv(uint16_t addr, const char * data, size_t data_size) { + if (i2c_dump_recv) { + printf("[%s] addr(0x%02x) data(0x%02lx):\n", __FUNCTION__, addr, data_size); + xxd(data, data_size); + } +} + diff --git a/client/i2c.h b/client/i2c.h new file mode 100644 index 0000000..f9f58f9 --- /dev/null +++ b/client/i2c.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +void i2c_send(uint16_t addr, const char * data, size_t data_size); +void i2c_recv(uint16_t addr, const char * data, size_t data_size); + diff --git a/client/sock.cpp b/client/sock.cpp index f5bd564..638bf9d 100644 --- a/client/sock.cpp +++ b/client/sock.cpp @@ -7,13 +7,12 @@ #include #include #include - #include #include "i2ctcpv1.h" #include "sock.h" #include "rl.h" -#include "xxd.h" +#include "i2c.h" using std::logic_error; using std::thread; @@ -106,31 +105,3 @@ void PBSocket::sock_task() { sock_close(); } -bool i2c_dump_send = false; -bool i2c_dump_recv = true; - -void i2c_send(uint16_t addr, const char * data, size_t data_size) { - i2ctcp_msg_t msg = { - .addr = addr, - .data = (char *) data, - .length = data_size, - }; - - char* packed; - size_t size; - if (!i2ctcp_write(&msg, &packed, &size)) return; - - sock->send(packed, size); - if (i2c_dump_send) { - printf("[i2c send] data(0x%02lx):\n", data_size); - xxd(data, data_size); - } -} - -void i2c_recv(uint16_t addr, const char * data, size_t data_size) { - if (i2c_dump_recv) { - printf("[i2c recv] data(0x%02lx):\n", data_size); - xxd(data, data_size); - } -} - diff --git a/client/sock.h b/client/sock.h index 42eba3b..0dee09e 100644 --- a/client/sock.h +++ b/client/sock.h @@ -29,6 +29,3 @@ private: extern PBSocket* sock; -void i2c_send(uint16_t addr, const char * data, size_t data_size); -void i2c_recv(uint16_t addr, const char * data, size_t data_size); - diff --git a/shared/include.cmake b/shared/include.cmake index f07a78b..c4b01c2 100644 --- a/shared/include.cmake +++ b/shared/include.cmake @@ -1,5 +1,5 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}) add_library(puzbus STATIC - ${CMAKE_CURRENT_LIST_DIR}/pb/driver.c + ${CMAKE_CURRENT_LIST_DIR}/pb/moddrv.c ) diff --git a/shared/pb/driver.c b/shared/pb/driver.c deleted file mode 100644 index f43d5c1..0000000 --- a/shared/pb/driver.c +++ /dev/null @@ -1,113 +0,0 @@ -#include - -#include "types.h" -#include "driver.h" - -/** \brief [private] placeholder global state variable */ -static pb_state_t _global_state = PB_GS_NOINIT; - -/** \brief [private] main controller global state */ -static pb_state_t _main_state = PB_GS_NOINIT; - -__weak pb_state_t pbdrv_hook_mod_state_read() { - return _global_state; -} - -__weak void pbdrv_hook_mod_state_write(pb_state_t state) { - _global_state = state; -} - -__weak void pbdrv_hook_main_state_update(pb_state_t state) { } - -__weak bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz) { - return false; -} - -__weak void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz) { - if (sz == 0) return; - pb_cmd_t cmd = (enum pb_cmd) buf[0]; - - // shift buffer pointer to only contain the puzzle bus message buf - buf++; - sz--; - - // allow user to implement custom commands - if (pbdrv_hook_cmd(i2c_addr, cmd, buf, sz)) - return; - - switch (cmd) { - case PB_CMD_READ: return pbdrv_handle_read(i2c_addr, buf, sz); - case PB_CMD_WRITE: return pbdrv_handle_write(i2c_addr, buf, sz); - case PB_CMD_MAGIC: return pbdrv_handle_magic(i2c_addr, buf, sz); - case PB_CMD_SEX: return pbdrv_handle_sex(i2c_addr, buf, sz); - default: return; - } -} - -__weak void pbdrv_handle_read(uint16_t i2c_addr, const char * buf, size_t sz) { - if (sz == 0) return; - pb_cmd_read_t * cmd = (pb_cmd_read_t *) buf; - - // allow user to addrimplement custom read handlers - if (pbdrv_hook_read(i2c_addr, cmd->address)) - return; - - switch (cmd->address) { - case PB_ADDR_GS: { - char res[] = { - PB_CMD_READ, - PB_ADDR_GS, - pbdrv_hook_mod_state_read(), - }; - return pbdrv_i2c_send(i2c_addr, res, sizeof(res)); - } - default: return; - } -} - -__weak void pbdrv_handle_write(uint16_t i2c_addr, const char * buf, size_t sz) { - if (sz < 2) return; // must have address and at least 1 byte data - pb_cmd_write_t * cmd = (pb_cmd_write_t *) buf; - - // allow user to implement custom read handlers - if (pbdrv_hook_write(i2c_addr, cmd->address, (char *) cmd->data, sz - 1)) - return; - - switch (cmd->address) { - case PB_ADDR_GS: - pbdrv_hook_mod_state_write(cmd->data[0]); - break; - default: return; - } -} - -__weak void pbdrv_handle_magic(uint16_t i2c_addr, const char * buf, size_t sz) { - if (sz != sizeof(pb_magic_msg)) return; - if (memcmp(buf, pb_magic_msg, sizeof(pb_magic_msg)) != 0) return; - - size_t res_size = sizeof(pb_cmd_t) + sizeof(pb_magic_res); - char res[res_size]; - res[0] = PB_CMD_MAGIC; - memcpy(res, pb_magic_res, sizeof(pb_magic_res)); - - pbdrv_i2c_send(i2c_addr, res, res_size); -} - -__weak void pbdrv_handle_sex(uint16_t i2c_addr, const char * buf, size_t sz) { - if (sz == 0) return; - pb_cmd_sex_t * cmd = (pb_cmd_sex_t *) buf; - - // send own state - char res[] = { - PB_CMD_SEX, - pbdrv_hook_mod_state_read(), - }; - pbdrv_i2c_send(i2c_addr, res, sizeof(res)); - - if (cmd->main_state == _main_state) return; - // keep main controller state - _main_state = cmd->main_state; - // call update if main state changed - pbdrv_hook_main_state_update(_main_state); -} - diff --git a/shared/pb/driver.h b/shared/pb/driver.h deleted file mode 100644 index c4e1167..0000000 --- a/shared/pb/driver.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -/** - * \file puzzle bus driver implementation - * - * Most \c pbdrv_* functions have a weak implementation, which may be - * overwritten by a custom implementation. This allows you to use the default - * implementation where possible, and only implement extensions required for - * your puzzle module. Please see spec.adoc for more information about how to - * use the puzzle bus driver library. - */ - -#include -#include -#include - -#include "types.h" - -#ifndef PBDRV_MOD_NAME -#define PBDRV_MOD_NAME "???" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz); -void pbdrv_i2c_send(uint16_t i2c_addr, const char * buf, size_t sz); - -pb_state_t pbdrv_hook_mod_state_read(); -void pbdrv_hook_mod_state_write(pb_state_t state); -void pbdrv_hook_main_state_update(pb_state_t state); - -/** - * \name hooks - * - * Implementing this function allows you to use the weak implementation of \c - * pbdrv_i2c_recv() while being able to implement custom command handlers. - * - * \return true if the cmd was recognized, or false to forward the command to - * the default handlers - * - * \{ - */ - -/** \brief cmd receive hook */ -bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz); -/** \brief read cmd hook */ -bool pbdrv_hook_read(uint16_t i2c_addr, uint8_t addr); -/** \brief write cmd hook */ -bool pbdrv_hook_write(uint16_t i2c_addr, uint8_t addr, const char * buf, size_t sz); -//! \} - -void pbdrv_handle_read(uint16_t i2c_addr, const char * buf, size_t sz); -void pbdrv_handle_write(uint16_t i2c_addr, const char * buf, size_t sz); -void pbdrv_handle_magic(uint16_t i2c_addr, const char * buf, size_t sz); -void pbdrv_handle_sex(uint16_t i2c_addr, const char * buf, size_t sz); - -#ifdef __cplusplus -} -#endif - diff --git a/shared/pb/moddrv.c b/shared/pb/moddrv.c new file mode 100644 index 0000000..4c897e0 --- /dev/null +++ b/shared/pb/moddrv.c @@ -0,0 +1,118 @@ +#include + +#include "types.h" +#include "moddrv.h" + +/** \brief [private] placeholder global state variable */ +static pb_state_t _global_state = PB_GS_NOINIT; + +/** \brief [private] main controller global state */ +static pb_state_t _main_state = PB_GS_NOINIT; + +__weak pb_state_t pbdrv_hook_mod_state_read() { + return _global_state; +} + +__weak void pbdrv_hook_mod_state_write(pb_state_t state) { + _global_state = state; +} + +__weak void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz == 0) return; + pb_cmd_t cmd = (enum pb_cmd) buf[0]; + + // shift buffer pointer to only contain the puzzle bus message buf + buf++; + sz--; + + // allow user to implement custom commands + if (pbdrv_hook_cmd(i2c_addr, cmd, buf, sz)) + return; + + switch (cmd) { + case PB_CMD_READ: return pbdrv_handle_read(i2c_addr, buf, sz); + case PB_CMD_WRITE: return pbdrv_handle_write(i2c_addr, buf, sz); + case PB_CMD_MAGIC: return pbdrv_handle_magic(i2c_addr, buf, sz); + case PB_CMD_SEX: return pbdrv_handle_sex(i2c_addr, buf, sz); + default: return; + } +} + +__weak void pbdrv_handle_read(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz == 0) return; + pb_cmd_read_t * cmd = (pb_cmd_read_t *) buf; + + // allow user to addrimplement custom read handlers + if (pbdrv_hook_read(i2c_addr, cmd->address)) + return; + + switch (cmd->address) { + case PB_ADDR_GS: { + char res[] = { + PB_CMD_READ, + PB_ADDR_GS, + pbdrv_hook_mod_state_read(), + }; + return pbdrv_i2c_send(i2c_addr, res, sizeof(res)); + } + default: return; + } +} + +__weak void pbdrv_handle_write(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz < 2) return; // must have address and at least 1 byte data + pb_cmd_write_t * cmd = (pb_cmd_write_t *) buf; + + // allow user to implement custom read handlers + if (pbdrv_hook_write(i2c_addr, cmd->address, (char *) cmd->data, sz - 1)) + return; + + switch (cmd->address) { + case PB_ADDR_GS: + pbdrv_hook_mod_state_write(cmd->data[0]); + break; + default: return; + } +} + +__weak void pbdrv_handle_magic(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz != sizeof(pb_magic_msg)) return; + if (memcmp(buf, pb_magic_msg, sizeof(pb_magic_msg)) != 0) return; + + size_t res_size = sizeof(pb_cmd_t) + sizeof(pb_magic_res); + char res[res_size]; + res[0] = PB_CMD_MAGIC; + memcpy(res, pb_magic_res, sizeof(pb_magic_res)); + + pbdrv_i2c_send(i2c_addr, res, res_size); +} + +__weak void pbdrv_handle_sex(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz == 0) return; + pb_cmd_sex_t * cmd = (pb_cmd_sex_t *) buf; + + // send own state + char res[] = { + PB_CMD_SEX, + pbdrv_hook_mod_state_read(), + }; + pbdrv_i2c_send(i2c_addr, res, sizeof(res)); + + if (cmd->main_state == _main_state) return; + // keep main controller state + _main_state = cmd->main_state; + // call update if main state changed + pbdrv_hook_main_state_update(_main_state); +} + +__weak void pbdrv_hook_main_state_update(pb_state_t state) { } +__weak bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz) { + return false; +} +__weak bool pbdrv_hook_read(uint16_t i2c_addr, uint8_t addr) { + return false; +} +__weak bool pbdrv_hook_write(uint16_t i2c_addr, uint8_t addr, const char * buf, size_t sz) { + return false; +} + diff --git a/shared/pb/moddrv.h b/shared/pb/moddrv.h new file mode 100644 index 0000000..c4e1167 --- /dev/null +++ b/shared/pb/moddrv.h @@ -0,0 +1,62 @@ +#pragma once + +/** + * \file puzzle bus driver implementation + * + * Most \c pbdrv_* functions have a weak implementation, which may be + * overwritten by a custom implementation. This allows you to use the default + * implementation where possible, and only implement extensions required for + * your puzzle module. Please see spec.adoc for more information about how to + * use the puzzle bus driver library. + */ + +#include +#include +#include + +#include "types.h" + +#ifndef PBDRV_MOD_NAME +#define PBDRV_MOD_NAME "???" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz); +void pbdrv_i2c_send(uint16_t i2c_addr, const char * buf, size_t sz); + +pb_state_t pbdrv_hook_mod_state_read(); +void pbdrv_hook_mod_state_write(pb_state_t state); +void pbdrv_hook_main_state_update(pb_state_t state); + +/** + * \name hooks + * + * Implementing this function allows you to use the weak implementation of \c + * pbdrv_i2c_recv() while being able to implement custom command handlers. + * + * \return true if the cmd was recognized, or false to forward the command to + * the default handlers + * + * \{ + */ + +/** \brief cmd receive hook */ +bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz); +/** \brief read cmd hook */ +bool pbdrv_hook_read(uint16_t i2c_addr, uint8_t addr); +/** \brief write cmd hook */ +bool pbdrv_hook_write(uint16_t i2c_addr, uint8_t addr, const char * buf, size_t sz); +//! \} + +void pbdrv_handle_read(uint16_t i2c_addr, const char * buf, size_t sz); +void pbdrv_handle_write(uint16_t i2c_addr, const char * buf, size_t sz); +void pbdrv_handle_magic(uint16_t i2c_addr, const char * buf, size_t sz); +void pbdrv_handle_sex(uint16_t i2c_addr, const char * buf, size_t sz); + +#ifdef __cplusplus +} +#endif + -- cgit v1.2.3 From 18d06c79b9f6a625eb218a15c8216556fb99dc02 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 30 May 2024 18:58:13 +0200 Subject: WIP parsing the connected puzzle module list --- client/i2c.cpp | 30 ++++++++++++++++++++++++++++++ client/readme.md | 10 ++++++++++ shared/pb/mod/main.h | 13 +++++++++++++ shared/pb/moddrv.c | 14 +++++++------- shared/pb/moddrv.h | 8 ++++---- shared/pb/spec.adoc | 20 +++++++++++++------- shared/pb/types.h | 12 +++++++++--- 7 files changed, 86 insertions(+), 21 deletions(-) create mode 100644 shared/pb/mod/main.h (limited to 'shared/pb') diff --git a/client/i2c.cpp b/client/i2c.cpp index fd29e1e..ee57e20 100644 --- a/client/i2c.cpp +++ b/client/i2c.cpp @@ -5,6 +5,11 @@ #include "sock.h" #include "xxd.h" +#include "pb/bus.h" +#include "pb/types.h" + +#include "pb/mod/main.h" + bool i2c_dump_send = false; bool i2c_dump_recv = true; @@ -28,10 +33,35 @@ void i2c_send(uint16_t addr, const char * data, size_t data_size) { free(packed); } +static void i2c_handle_cmd_read(uint16_t, const char *, size_t); + void i2c_recv(uint16_t addr, const char * data, size_t data_size) { if (i2c_dump_recv) { printf("[%s] addr(0x%02x) data(0x%02lx):\n", __FUNCTION__, addr, data_size); xxd(data, data_size); } + + if (data_size == 0) return; + enum pb_cmd cmd = (enum pb_cmd) data[0]; + data++; data_size--; + + switch (cmd) { + case PB_CMD_READ: return i2c_handle_cmd_read(addr, data, data_size); + default: return; + } +} + +static void i2c_handle_cmd_read(uint16_t i2c_addr, const char * buf, size_t sz) { + if (sz < 2) return; // require data address + 1 byte of data + pb_cmd_read_t * cmd = (pb_cmd_read_t *) buf; + sz--; // sz now represents size of cmd->data + + if (i2c_addr == BUSADDR_MAIN && cmd->address == 0x01) { + if (sz % 2 != 0) return; // invalid data + for (size_t offset = 0; offset < sz; offset += sizeof(pb_mod_main_mod_t)) { + pb_mod_main_mod_t * mod = (pb_mod_main_mod_t *) (cmd->data + offset); + printf("module at addr 0x%02x with state %d\n", mod->addr, mod->state); + } + } } diff --git a/client/readme.md b/client/readme.md index ea3e034..da48cf1 100644 --- a/client/readme.md +++ b/client/readme.md @@ -7,6 +7,16 @@ game operator to control and monitor the state of a puzzle box, but is also a useful debugging tool when developing puzzle modules, as it allows you to send arbitrary data over the puzzle bus. +## WIP TODO + +- cleanup + - separate ../shared/pb/moddrv.c into a puzzle module specific and 'common' bit + - use the common bit in i2c.cpp instead + - cast to structs in ../shared/pb/moddrv.c +- functionality + - print pretty tree of connected puzzle modules + - add enum to string functions in CLIENT ONLY + ## Features - List detected puzzle modules diff --git a/shared/pb/mod/main.h b/shared/pb/mod/main.h new file mode 100644 index 0000000..56ccd3d --- /dev/null +++ b/shared/pb/mod/main.h @@ -0,0 +1,13 @@ +#pragma once + +#include "../types.h" + +typedef struct __packed { + const uint8_t addr; + const enum pb_state state; +} pb_mod_main_mod_t; + +enum __packed { + PB_MOD_MAIN_ADDR_MODS = 0x01, //!< connected puzzle modules +}; + diff --git a/shared/pb/moddrv.c b/shared/pb/moddrv.c index 4c897e0..1f7fab8 100644 --- a/shared/pb/moddrv.c +++ b/shared/pb/moddrv.c @@ -4,22 +4,22 @@ #include "moddrv.h" /** \brief [private] placeholder global state variable */ -static pb_state_t _global_state = PB_GS_NOINIT; +static enum pb_state _global_state = PB_GS_NOINIT; /** \brief [private] main controller global state */ -static pb_state_t _main_state = PB_GS_NOINIT; +static enum pb_state _main_state = PB_GS_NOINIT; -__weak pb_state_t pbdrv_hook_mod_state_read() { +__weak enum pb_state pbdrv_hook_mod_state_read() { return _global_state; } -__weak void pbdrv_hook_mod_state_write(pb_state_t state) { +__weak void pbdrv_hook_mod_state_write(enum pb_state state) { _global_state = state; } __weak void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz) { if (sz == 0) return; - pb_cmd_t cmd = (enum pb_cmd) buf[0]; + enum pb_cmd cmd = (enum pb_cmd) buf[0]; // shift buffer pointer to only contain the puzzle bus message buf buf++; @@ -105,8 +105,8 @@ __weak void pbdrv_handle_sex(uint16_t i2c_addr, const char * buf, size_t sz) { pbdrv_hook_main_state_update(_main_state); } -__weak void pbdrv_hook_main_state_update(pb_state_t state) { } -__weak bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz) { +__weak void pbdrv_hook_main_state_update(enum pb_state state) { } +__weak bool pbdrv_hook_cmd(uint16_t i2c_addr, enum pb_state cmd, const char * buf, size_t sz) { return false; } __weak bool pbdrv_hook_read(uint16_t i2c_addr, uint8_t addr) { diff --git a/shared/pb/moddrv.h b/shared/pb/moddrv.h index c4e1167..ecfc13a 100644 --- a/shared/pb/moddrv.h +++ b/shared/pb/moddrv.h @@ -27,9 +27,9 @@ extern "C" { void pbdrv_i2c_recv(uint16_t i2c_addr, const char * buf, size_t sz); void pbdrv_i2c_send(uint16_t i2c_addr, const char * buf, size_t sz); -pb_state_t pbdrv_hook_mod_state_read(); -void pbdrv_hook_mod_state_write(pb_state_t state); -void pbdrv_hook_main_state_update(pb_state_t state); +enum pb_state pbdrv_hook_mod_state_read(); +void pbdrv_hook_mod_state_write(enum pb_state state); +void pbdrv_hook_main_state_update(enum pb_state state); /** * \name hooks @@ -44,7 +44,7 @@ void pbdrv_hook_main_state_update(pb_state_t state); */ /** \brief cmd receive hook */ -bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz); +bool pbdrv_hook_cmd(uint16_t i2c_addr, enum pb_state cmd, const char * buf, size_t sz); /** \brief read cmd hook */ bool pbdrv_hook_read(uint16_t i2c_addr, uint8_t addr); /** \brief write cmd hook */ diff --git a/shared/pb/spec.adoc b/shared/pb/spec.adoc index a99497b..3172e84 100644 --- a/shared/pb/spec.adoc +++ b/shared/pb/spec.adoc @@ -10,10 +10,16 @@ puzzle module framework. The puzzle bus carries data over a standard I^2^C bus. Additional details about this bus can be found in the link:../../docs/design.adoc[Design document]. -NOTE: Addresses influence the puzzle box's behavior, as the order of puzzles is -determined by the puzzle module address. Two puzzle modules may use the same -address, but this will mean that they cannot be used simultaniously in the same -puzzle box. Known addresses are documented in link:bus.h[]. +The following details are important to puzzle module developers, as they may +cause unexpected behavior: + +- *Addresses influence the puzzle box's behavior*. The order of puzzles is + determined by the puzzle module address. Two puzzle modules may use the same + address, but this will mean that they cannot be used simultaniously in the + same puzzle box. Known addresses are documented in link:bus.h[]. +- *The read/write bit of an I^2^C frame determines how it's handled*. I^2^C + *read* frames are treated as requests, while *write* frames are treated as + responses. == Puzzle bus driver (pbdrv) @@ -46,8 +52,8 @@ default implementations, but this is discouraged. [[sec:state-global]] == Global state -If your puzzle module defines its own ``pb_state_t``, you can tell the driver -to use it by implementing the ``pbdrv_hook_state_read`` and +If your puzzle module defines its own global ``enum pb_state``, you can tell +the driver to use it by implementing the ``pbdrv_hook_state_read`` and ``pbdrv_hook_state_write`` functions. These functions are also used by the default implementation of the read/write commands to address 0 (global state). @@ -115,7 +121,7 @@ command(s) that you want to overwrite. Example: ```c -bool pbdrv_hook_cmd(uint16_t i2c_addr, pb_cmd_t cmd, const char * buf, size_t sz) { +bool pbdrv_hook_cmd(uint16_t i2c_addr, enum pb_cmd cmd, const char * buf, size_t sz) { if (cmd == 0x54) { printf("custom command received!\n"); return true; diff --git a/shared/pb/types.h b/shared/pb/types.h index 7996a19..f2e2078 100644 --- a/shared/pb/types.h +++ b/shared/pb/types.h @@ -29,7 +29,7 @@ enum __packed pb_cmd { PB_CMD_SEX, //!< state exchange PB_CMD_MAGIC, //!< magic message }; -typedef enum pb_cmd pb_cmd_t; +// typedef enum pb_cmd pb_cmd_t; /** \brief magic sent from main controller to puzzle module */ static const char pb_magic_msg[] = { 0x70, 0x75, 0x7a, 0x62, 0x75, 0x73 }; @@ -43,10 +43,16 @@ enum __packed pb_state { PB_GS_PLAYING, //!< puzzle actively being solved PB_GS_SOLVED, //!< puzzle completed }; -typedef enum pb_state pb_state_t; +// typedef enum pb_state pb_state_t; + +typedef struct __packed { + const enum pb_cmd cmd; + const uint8_t data[]; +} pb_cmd_t; typedef struct __packed { const uint8_t address; + const uint8_t data[]; } pb_cmd_read_t; typedef struct __packed { @@ -55,7 +61,7 @@ typedef struct __packed { } pb_cmd_write_t; typedef struct __packed { - const pb_state_t main_state; + const enum pb_state main_state; } pb_cmd_sex_t; enum __packed { -- cgit v1.2.3