aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Doxyfile8
-rw-r--r--lib/pbdrv/index.dox39
-rw-r--r--lib/pbdrv/pb-buf.h16
-rw-r--r--lib/pbdrv/pb-mem.h55
-rw-r--r--lib/pbdrv/pb-mod.h83
-rw-r--r--lib/pbdrv/pb-msg.h40
-rw-r--r--lib/pbdrv/pb-route.h27
-rw-r--r--lib/pbdrv/pb.h9
-rw-r--r--lib/pbdrv/spec.adoc133
9 files changed, 248 insertions, 162 deletions
diff --git a/Doxyfile b/Doxyfile
index c3623cc..53e75c2 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -3,6 +3,7 @@ DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "puzzlebox"
OUTPUT_DIRECTORY = doxygen
+INPUT += readme.md
INPUT += client
INPUT += lib/mpack
INPUT += lib/i2ctcp
@@ -14,8 +15,13 @@ EXCLUDE = lib/mpack/src
EXCLUDE_PATTERNS = **/build
EXCLUDE_SYMLINKS = YES
-FILE_PATTERNS = *.c *.cpp *.h *.hpp *.md
+FILE_PATTERNS = *.c *.cpp *.h *.hpp *.md *.dox
RECURSIVE = YES
GENERATE_LATEX = NO
+ALIASES += I2C="I²C"
+INPUT_FILTER = "sed -e 's/\<I2C\>\|\<I<sup>2<\/sup>C\>/\\I2C/g'"
+
+USE_MDFILE_AS_MAINPAGE = readme.md
+
diff --git a/lib/pbdrv/index.dox b/lib/pbdrv/index.dox
new file mode 100644
index 0000000..ad05078
--- /dev/null
+++ b/lib/pbdrv/index.dox
@@ -0,0 +1,39 @@
+// vim:ft=doxygen
+/**
+
+\defgroup pbdrv pbdrv
+\brief Standalone puzzle bus driver
+
+pbdrv is a standalone portable static library for handling (i.e.
+(de)serialization) of puzzle bus messages.
+
+\defgroup pbdrv-mod pbdrv-mod
+\brief Puzzle module driver (superset of \ref pbdrv)
+
+pbdrv-mod is a superset of pbdrv, and includes functions specific to puzzle bus
+modules. pbdrv-mod compiles to an object file instead of a static library
+because it may depend on functions that rely on external libraries. pbdrv-mod
+is still considered standalone, but requires either using an existing driver,
+or (partially) implementing the driver functions.
+
+\note Most \c pb_* 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.
+
+TODO: where to find drivers
+TODO: what are extensions
+TODO: what to do if there is no driver / extension
+
+\{
+
+\defgroup hook
+\brief Functions for (partially) overriding default behavior
+
+Hooks are functions with a default (weak) implementation in pbdrv. These
+functions can be overwritten by the user to implement custom behavior, without
+needing to understand the internals of pbdrv.
+
+\}
+
+*/
diff --git a/lib/pbdrv/pb-buf.h b/lib/pbdrv/pb-buf.h
index 78ee380..049f516 100644
--- a/lib/pbdrv/pb-buf.h
+++ b/lib/pbdrv/pb-buf.h
@@ -6,14 +6,30 @@
extern "C" {
#endif
+/**
+ * \ingroup pbdrv
+ * \ingroup pbdrv-mod
+ * \defgroup pb_buf Buffer
+ * \brief Binary data buffer type used in pbdrv
+ * \{
+ */
+
//! binary buffer struct
typedef struct {
char * data; //! pointer to data
size_t size; //! size of data
} pb_buf_t;
+/**
+ * \brief free a \c pb_buf_t
+ *
+ * This function calls \c pb_free() on the \c data member of a \c pb_buf_t
+ * struct if it is not equal to \c NULL.
+ */
void pb_buf_free(pb_buf_t * buf);
+/// \}
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/pbdrv/pb-mem.h b/lib/pbdrv/pb-mem.h
index 72be214..4d0f995 100644
--- a/lib/pbdrv/pb-mem.h
+++ b/lib/pbdrv/pb-mem.h
@@ -6,13 +6,68 @@
extern "C" {
#endif
+/**
+ * \ingroup pbdrv
+ * \ingroup pbdrv-mod
+ * \defgroup pb_mem Memory
+ * \brief Platform-specific memory management functions
+ *
+ * \note This header only declares the memory management functions, and it is
+ * up to \ref pb_ext "extensions" to implement the underlying memory management
+ * functions.
+ *
+ * TODO: ref to extensions
+ *
+ * \{
+ */
+
+/**
+ * \brief Allocate a contiguous chunk of memory
+ * \param sz Requested size of memory area
+ * \return Pointer to memory area, or \c NULL on failure
+ * \note The allocated memory must be free'd again using \c pb_free()
+ */
void * pb_malloc(size_t sz);
+/**
+ * \brief Free a chunk of memory previously allocated with \c pb_malloc()
+ * \param ptr Pointer to memory area
+ */
void pb_free(void * ptr);
+/**
+ * \brief Resize the memory area \c ptr to size \c sz
+ * \param ptr Pointer to allocated memory area
+ * \param sz Requested new size of memory area
+ * \return Pointer to memory area, or \c NULL on failure
+ * \warning This function is not available on FreeRTOS, and should be avoided
+ * if possible. MPack's \c mpack_writer_init_growable() is known to use \c
+ * realloc(), and should not be used for this reason.
+ */
void * pb_realloc(void * ptr, size_t sz);
+/**
+ * \brief copy a memory region
+ * \param dest Pointer to destination memory
+ * \param src Pointer to source memory
+ * \param sz Number of bytes to copy
+ * \return Pointer to \c dest
+ *
+ * This function has a portable implementation, and is always available.
+ */
void * pb_memcpy(void * dest, const void * src, size_t sz);
+/**
+ * \brief compare two memory regions
+ * \param a Pointer to first memory region
+ * \param b Pointer to second memory region
+ * \param sz Number of bytes to compare
+ * \return 0 if the memory regions are identical, or the difference between the
+ * first non-matching byte
+ *
+ * This function has a portable implementation, and is always available.
+ */
int pb_memcmp(const void * a, const void * b, size_t sz);
+/// \}
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/pbdrv/pb-mod.h b/lib/pbdrv/pb-mod.h
index c4629a6..2ff1908 100644
--- a/lib/pbdrv/pb-mod.h
+++ b/lib/pbdrv/pb-mod.h
@@ -1,31 +1,63 @@
#pragma once
-/**
- * \file puzzle bus driver implementation
- *
- * Most \c pb_* 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 "pb-types.h"
#ifdef __cplusplus
extern "C" {
#endif
-//! puzzle module name (optional, default = "???")
+/**
+ * \ingroup pbdrv-mod
+ * \defgroup pb_mod Module
+ * \brief Metadata and auxiliary utility functions
+ * \{
+ */
+
+/**
+ * \brief Puzzle module name
+ *
+ * Optional to define, default value is "???"
+ */
extern const char * PB_MOD_NAME;
-//! puzzle module bus address (required)
+/**
+ * \brief Puzzle module bus address
+ *
+ * **Required** to define
+ */
extern const i2c_addr_t PB_MOD_ADDR;
/**
+ * \brief Platform-specific blocking delay function
+ *
+ * FIXME: this should be removed (see handover: RP2040 I2C limitations)
+ */
+void pb_mod_blocking_delay_ms(unsigned long ms);
+
+/// \}
+
+/**
+ * \ingroup pbdrv-mod
+ * \defgroup pb_i2c I2C
+ * \brief I2C send/receive handlers
+ *
+ * If there is no existing \ref pb_drv "driver" for the microcontroller on
+ * which you want to use the puzzle bus driver, you may implement the following
+ * in order to use pbdrv:
+ *
+ * - The \c pb_i2c_recv() function must be **called** for every received I2C
+ * message
+ * - The \c pb_i2c_send() function must be **implemented** using the
+ * platform/device-specific I2C write function
+ *
+ * \{
+ */
+
+/**
* \brief handle a received message from the I2C bus (puzzle bus)
*
* This function attempts to parse an I2C message as a puzzle bus message, and
* calls the appropriate handler for the message if it is considered valid.
+ * Invalid messages are silently ignored.
*
* \param buf pointer to message content
* \param sz size of \p buf
@@ -46,25 +78,36 @@ void pb_i2c_recv(const uint8_t * buf, size_t sz);
*/
void pb_i2c_send(i2c_addr_t i2c_addr, const uint8_t * buf, size_t sz);
+/// \}
+
+/// \ingroup hook
+/// \{
+
+/**
+ * \defgroup pb_hook_mod_state State
+ * \brief Provide your own global state variable
+ *
+ * If your puzzle module defines its own global \c pb_global_state_t, you can
+ * tell the driver to use it by implementing these functions. These functions
+ * are also used internally by the driver when creating \c STATE \c REQ and \c
+ * STATE \c RES commands.
+ *
+ * \{
+ */
+
/**
* \brief global state read hook
- * \ingroup hook
* \return current value of global state enum
*/
pb_global_state_t pb_hook_mod_state_read();
/**
* \brief global state write hook
- * \ingroup hook
* \param state new value of global state enum
*/
void pb_hook_mod_state_write(pb_global_state_t state);
-/**
- * \brief platform-specific blocking delay function
- *
- * FIXME: this should be removed (see handover: RP2040 I2C limitations)
- */
-void pb_mod_blocking_delay_ms(unsigned long ms);
+/// \}
+/// \}
#ifdef __cplusplus
}
diff --git a/lib/pbdrv/pb-msg.h b/lib/pbdrv/pb-msg.h
index f27d4c4..33b697a 100644
--- a/lib/pbdrv/pb-msg.h
+++ b/lib/pbdrv/pb-msg.h
@@ -7,10 +7,50 @@
extern "C" {
#endif
+/**
+ * \ingroup pbdrv
+ * \ingroup pbdrv-mod
+ * \defgroup pb_msg Message
+ * \brief Message (de)serialization
+ * \{
+ */
+
+/**
+ * \brief Serialize a message into a binary buffer
+ *
+ * \note This function allocates a \c pb_buf_t that should be free'd using \c
+ * pb_buf_free()
+ *
+ * \param msg Message to serialize
+ *
+ * \warning The type of \c msg->cmd is inferred from \c msg->type. If the
+ * message is not correctly formatted, this function may cause undefined
+ * behavior. If possible, use functions from \ref pb_send instead.
+ *
+ * \return \c pb_buf_t containing the serialized message, or an empty struct if
+ * serialization failed
+ */
pb_buf_t pb_msg_write(const pb_msg_t * msg);
+/**
+ * \brief Read a binary buffer and attempt to deserialize it as a puzzle bus
+ * message
+ *
+ * \note This function allocates a \c pb_msg_t pointer that should be free'd
+ * using \c pb_msg_free()
+ *
+ * \param buf Binary data to interpret as puzzle bus message
+ *
+ * \return \c pb_msg_t pointer containing the deserialized message, or NULL if
+ * serialization failed
+ */
pb_msg_t * pb_msg_read(const pb_buf_t * buf);
+/**
+ * \brief Recursively free fields of a \c pb_msg_t
+ */
void pb_msg_free(pb_msg_t * msg);
+/// \}
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/pbdrv/pb-route.h b/lib/pbdrv/pb-route.h
index 967c4a9..233a087 100644
--- a/lib/pbdrv/pb-route.h
+++ b/lib/pbdrv/pb-route.h
@@ -8,16 +8,10 @@ extern "C" {
void pb_route_msg(pb_msg_t * msg);
-bool pb_hook_route_msg(pb_msg_t * msg);
-
void pb_route_cmd_prop(pb_msg_t * msg);
void pb_route_cmd_state(pb_msg_t * msg);
void pb_route_cmd_magic(pb_msg_t * msg);
-bool pb_hook_route_cmd_prop(pb_msg_t * msg);
-bool pb_hook_route_cmd_state(pb_msg_t * msg);
-bool pb_hook_route_cmd_magic(pb_msg_t * msg);
-
void pb_route_cmd_prop_req(pb_msg_t * msg);
void pb_route_cmd_prop_res(pb_msg_t * msg);
void pb_route_cmd_prop_set(pb_msg_t * msg);
@@ -26,11 +20,28 @@ void pb_route_cmd_state_req(pb_msg_t * msg);
void pb_route_cmd_state_res(pb_msg_t * msg);
void pb_route_cmd_state_set(pb_msg_t * msg);
+void pb_route_cmd_magic_req(pb_msg_t * msg);
+void pb_route_cmd_magic_res(pb_msg_t * msg);
+
+/// \ingroup hook
+/// \{
+
+/// \defgroup hook_route Routing
+/// \brief Use a custom message handler
+/// \{
+
+bool pb_hook_route_msg(pb_msg_t * msg);
+
+bool pb_hook_route_cmd_prop(pb_msg_t * msg);
+bool pb_hook_route_cmd_state(pb_msg_t * msg);
+bool pb_hook_route_cmd_magic(pb_msg_t * msg);
+
+/// \}
+
void pb_hook_main_state_update(pb_global_state_t state);
void pb_hook_module_init();
-void pb_route_cmd_magic_req(pb_msg_t * msg);
-void pb_route_cmd_magic_res(pb_msg_t * msg);
+/// \}
#ifdef __cplusplus
}
diff --git a/lib/pbdrv/pb.h b/lib/pbdrv/pb.h
index e8037ae..cef04d8 100644
--- a/lib/pbdrv/pb.h
+++ b/lib/pbdrv/pb.h
@@ -1,5 +1,13 @@
#pragma once
+/**
+ * \ingroup pbdrv
+ * \ingroup pbdrv-mod
+ * \defgroup pb Bus
+ * \brief Constants for the puzzle bus hardware
+ * \{
+ */
+
//! I2C bus speed in hertz (100 KHz)
#define PB_CLOCK_SPEED_HZ 100000
//! I2C bus timeout delay in milliseconds
@@ -32,3 +40,4 @@
//! Dummy puzzle module I2C address
#define PB_ADDR_MOD_DUMMY 0x69
+/// \}
diff --git a/lib/pbdrv/spec.adoc b/lib/pbdrv/spec.adoc
deleted file mode 100644
index 3172e84..0000000
--- a/lib/pbdrv/spec.adoc
+++ /dev/null
@@ -1,133 +0,0 @@
-= 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].
-
-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)
-
-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
- <<sec:state-global,global state hooks>> 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 <<sec:state-aux,auxiliary state hooks>>.
-- If you want to implement custom puzzle bus commands, you can implement the
- <<sec:cmd,command hook>>.
-
-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 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).
-
-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, enum pb_cmd cmd, const char * buf, size_t sz) {
- if (cmd == 0x54) {
- printf("custom command received!\n");
- return true;
- }
-
- return false;
-}
-```
-