diff options
Diffstat (limited to 'shared/pb/spec.adoc')
-rw-r--r-- | shared/pb/spec.adoc | 127 |
1 files changed, 127 insertions, 0 deletions
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 + <<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 ``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; +} +``` + |