diff options
Diffstat (limited to 'lib')
36 files changed, 805 insertions, 295 deletions
diff --git a/lib/mpack/CMakeLists.txt b/lib/mpack/CMakeLists.txt index 0e4359d..4badc7b 100644 --- a/lib/mpack/CMakeLists.txt +++ b/lib/mpack/CMakeLists.txt @@ -7,6 +7,8 @@ cmake_minimum_required(VERSION 3.29) set(CMAKE_C_STANDARD 11) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) +# set(CMAKE_BUILD_TYPE Debug) + project(mpack C) add_library(mpack STATIC @@ -24,5 +26,5 @@ target_include_directories(mpack SYSTEM INTERFACE ) # causes some wild crashes, please leave off -add_compile_definitions(MPACK_READ_TRACKING=0) +target_compile_definitions(mpack PRIVATE MPACK_READ_TRACKING=0) diff --git a/lib/mpack/read-remaining.c b/lib/mpack/read-remaining.c index ebc9b56..46b5815 100644 --- a/lib/mpack/read-remaining.c +++ b/lib/mpack/read-remaining.c @@ -1,10 +1,10 @@ #include "mpack.h" size_t mpack_read_remaining_bytes(mpack_reader_t * reader, char * p, size_t count) { - size_t limit =mpack_reader_remaining(reader, NULL); + size_t limit = mpack_reader_remaining(reader, NULL); if (0 < count && count < limit) limit = count; - memcpy(p, reader->data, limit); + MPACK_MEMCPY(p, reader->data, limit); return limit; } diff --git a/lib/pbdrv/CMakeLists.txt b/lib/pbdrv/CMakeLists.txt index ca85b2b..998ed4d 100644 --- a/lib/pbdrv/CMakeLists.txt +++ b/lib/pbdrv/CMakeLists.txt @@ -12,17 +12,29 @@ project(pbdrv C CXX) add_subdirectory(lib/mpack) -# generic puzzle bus (de)serializer library -add_library(pbdrv STATIC pb-read.c pb-write.c) -target_link_libraries(pbdrv mpack) +# mpack-config.h +target_compile_definitions(mpack PRIVATE MPACK_HAS_CONFIG=1) +target_include_directories(mpack PRIVATE .) + +# generic puzzle bus message handling library functions +add_library(pbdrv STATIC + pb-msg.c + pb-serial.c + pb-buf.c + ) target_include_directories(pbdrv SYSTEM INTERFACE .) +target_link_libraries(pbdrv mpack) -# puzzle bus module specific code (superset of pbdrv) -add_library(pbdrv-mod STATIC pb-mod.c) -target_link_libraries(pbdrv-mod pbdrv) +# puzzle bus *module* specific code +add_library(pbdrv-mod OBJECT + pb-mod.c + pb-send.c + pb-route.c + ) target_include_directories(pbdrv-mod SYSTEM INTERFACE .) +target_link_libraries(pbdrv-mod pbdrv) -# supported puzzle bus drivers -include(drv/arduino/cfg.cmake) -include(drv/rp2040/cfg.cmake) +# puzzle bus drivers +include(drv/arduino/include.cmake) +# include(drv/rp2040/include.cmake) # please see /main/pbdrv.h diff --git a/lib/pbdrv/drv/arduino/cfg.cmake b/lib/pbdrv/drv/arduino/cfg.cmake deleted file mode 100644 index 36716e3..0000000 --- a/lib/pbdrv/drv/arduino/cfg.cmake +++ /dev/null @@ -1,7 +0,0 @@ -if(NOT DEFINED ARDUINO) - return() -endif() - -target_sources(pbdrv-mod PRIVATE "${CMAKE_CURRENT_LIST_DIR}/mod.cpp") -target_link_arduino_libraries(pbdrv-mod core Wire) - diff --git a/lib/pbdrv/drv/arduino/include.cmake b/lib/pbdrv/drv/arduino/include.cmake new file mode 100644 index 0000000..1e2ff08 --- /dev/null +++ b/lib/pbdrv/drv/arduino/include.cmake @@ -0,0 +1,11 @@ +if(NOT DEFINED ARDUINO) + return() +endif() + +target_sources(pbdrv-mod PRIVATE "${CMAKE_CURRENT_LIST_DIR}/mod.cpp") +target_link_arduino_libraries(pbdrv-mod core Wire) + +# freertos is used to defer the handling of i2c messages outside the receive +# interrupt service routine +include("${CMAKE_CURRENT_LIST_DIR}/../../ext/freertos/include.cmake") + diff --git a/lib/pbdrv/drv/arduino/mod.cpp b/lib/pbdrv/drv/arduino/mod.cpp index 8a38a5b..9130334 100644 --- a/lib/pbdrv/drv/arduino/mod.cpp +++ b/lib/pbdrv/drv/arduino/mod.cpp @@ -4,34 +4,81 @@ #include <Arduino.h> #include <Wire.h> +#include <avr/delay.h> -#include <stdlib.h> -#include <stdint.h> +#include <FreeRTOS.h> +#include <timers.h> +#include <task.h> #include "../../pb.h" #include "../../pb-mod.h" -#include "mod.h" +#include "../../pb-types.h" +#include "../../pb-buf.h" +#include "../../pb-mem.h" + +static void async_pb_i2c_recv(void * _msg, uint32_t _) { + pb_buf_t * msg = (pb_buf_t *) _msg; + pb_i2c_recv((uint8_t *) msg->data, msg->size); + pb_buf_free(msg); + pb_free(msg); +} static void recv_event(int bytes) { - uint8_t * data = (uint8_t *) malloc(bytes); - size_t size = 0; + pb_buf_t * msg = (pb_buf_t *) pb_malloc(sizeof(pb_buf_t)); + msg->data = (char *) pb_malloc(bytes); + msg->size = 0; while (Wire.available()) - data[size++] = Wire.read(); + msg->data[msg->size++] = Wire.read(); - pbdrv_i2c_recv(data, size); + // defer pb_i2c_recv call + xTimerPendFunctionCallFromISR(async_pb_i2c_recv, msg, 0, NULL); } -void pbdrv_setup() { - Wire.begin((int) PBDRV_MOD_ADDR); +static void pb_setup() { + Wire.begin((int) PB_MOD_ADDR); Wire.setWireTimeout(PB_TIMEOUT_US, true); Wire.setClock(PB_CLOCK_SPEED_HZ); + // TODO: check if onReceive replaces or appends a handler function Wire.onReceive(recv_event); } -__weak void pbdrv_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) { +__weak void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) { Wire.beginTransmission((int) addr); Wire.write(buf, sz); Wire.endTransmission(true); Wire.setWireTimeout(PB_TIMEOUT_US, true); } +//! Arduino setup function +extern void setup(void); +//! Arduino loop function +extern void loop(void); +//! Arduino internal initialization +void init(void); + +//! FreeRTOS loop task +void loop_task() { + for(;;) { + loop(); + if (serialEventRun) serialEventRun(); + } +} + +//! Application entrypoint +int main(void) { + init(); // call arduino internal setup + setup(); // call regular arduino setup + pb_setup(); // call pbdrv-mod setup + xTaskCreate((TaskFunction_t) loop_task, "loop", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); + vTaskStartScheduler(); // start freertos scheduler + return 0; +} + +/** + * \note I should really be able to use Arduino's initVariant function for + * this, but I can't seem to get it to link properly using the CMake setup in + * this repository. Overriding the main() function seems to work, and the + * USBCON thing in the default Arduino main() function isn't needed because + * puzzle modules are likely not using USB. + */ + diff --git a/lib/pbdrv/drv/arduino/mod.h b/lib/pbdrv/drv/arduino/mod.h deleted file mode 100644 index 079941a..0000000 --- a/lib/pbdrv/drv/arduino/mod.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \brief puzzle bus driver setup - * - * This function should be called from the Arduino \c setup() function. - */ -void pbdrv_setup(); - -#ifdef __cplusplus -} -#endif - diff --git a/lib/pbdrv/drv/rp2040/cfg.cmake b/lib/pbdrv/drv/rp2040/cfg.cmake deleted file mode 100644 index 0fbad18..0000000 --- a/lib/pbdrv/drv/rp2040/cfg.cmake +++ /dev/null @@ -1,7 +0,0 @@ -if(NOT PICO_PLATFORM STREQUAL "rp2040") - return() -endif() - -target_sources(pbdrv-mod PRIVATE "${CMAKE_CURRENT_LIST_DIR}/mod.c") -target_link_libraries(pbdrv-mod hardware_i2c pico_i2c_slave) - diff --git a/lib/pbdrv/drv/rp2040/mod.c b/lib/pbdrv/drv/rp2040/mod.c deleted file mode 100644 index 6becdee..0000000 --- a/lib/pbdrv/drv/rp2040/mod.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "mod.h" - -#include "../../pb.h" -#include "../../pb-types.h" -#include "../../pb-mod.h" - -#include <hardware/i2c.h> -#include <hardware/gpio.h> -#include <pico/i2c_slave.h> - -#define PBDRV_I2C i2c0 -#define BUF_SIZE 256 - -static volatile bool pbdrv_i2c_msg_avail = false; -static uint8_t data[BUF_SIZE]; -static size_t size = 0; - -// TODO: create event group instead of pbdrv_i2c_msg_avail -// TODO: create freertos task that monitors the pbdrv_i2c_msg_avail flag and -// calls pbdrv_i2c_recv when a message is received - - -/** - * \note this function is called from the I2C ISR, and should return as quickly - * as possible. - */ -static void recv_event(i2c_inst_t *i2c, i2c_slave_event_t event) { - // message needs to be handled first - if (pbdrv_i2c_msg_avail) return; - - switch (event) { - case I2C_SLAVE_RECEIVE: { - if (size == BUF_SIZE) return; - data[size++] = i2c_read_byte_raw(PBDRV_I2C); - break; - } - case I2C_SLAVE_FINISH: { - // TODO: handle this w/ queue mechanism instead? - // pbdrv_i2c_recv(data, size); - pbdrv_i2c_msg_avail = true; - size = 0; - break; - } - default: break; - } -} - -void pbdrv_setup() { - i2c_init(PBDRV_I2C, PB_CLOCK_SPEED_HZ); - i2c_slave_init(PBDRV_I2C, PBDRV_MOD_ADDR, &recv_event); -} - -/** - * While the RP2040's datasheet claims it supports multi-master configurations - * by implementing bus arbitration, it does not natively support a mode where - * it is configured as a (multi-)master with a slave address, such that it can - * be addressed by other multi-masters. This function includes a hacky - * workaround that teporarily sets the RP2040 to I2C master mode to send a - * message, and then restores it back to slave mode. - * - * This approach results in some received frames being (partially) dropped in - * the time period between the invocation of this function and the bus becoming - * idle (and the message is sent). - */ -__weak void pbdrv_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) { - i2c_set_slave_mode(PBDRV_I2C, false, PBDRV_MOD_ADDR); - - // false to write stop condition to i2c bus - i2c_write_timeout_us(PBDRV_I2C, addr, buf, sz, false, PB_TIMEOUT_US); - - i2c_set_slave_mode(PBDRV_I2C, true, PBDRV_MOD_ADDR); -} - diff --git a/lib/pbdrv/drv/rp2040/mod.h b/lib/pbdrv/drv/rp2040/mod.h deleted file mode 100644 index 0cf2e63..0000000 --- a/lib/pbdrv/drv/rp2040/mod.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -//! puzzle bus driver setup -void pbdrv_setup(); - -#ifdef __cplusplus -} -#endif - diff --git a/lib/pbdrv/ext/freertos/include.cmake b/lib/pbdrv/ext/freertos/include.cmake new file mode 100644 index 0000000..e7ab7fd --- /dev/null +++ b/lib/pbdrv/ext/freertos/include.cmake @@ -0,0 +1,9 @@ +target_sources(pbdrv PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pb-mem.c") +target_link_libraries(pbdrv + freertos_kernel + freertos_kernel_include + freertos_config + ) + +target_sources(pbdrv-mod PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pb-mod.c") + diff --git a/lib/pbdrv/ext/freertos/pb-mem.c b/lib/pbdrv/ext/freertos/pb-mem.c new file mode 100644 index 0000000..b18d79f --- /dev/null +++ b/lib/pbdrv/ext/freertos/pb-mem.c @@ -0,0 +1,31 @@ +#include <FreeRTOS.h> + +#include "../../pb-mem.h" +#include "../../pb-types.h" + +inline void * pb_malloc(size_t sz) { + return pvPortMalloc(sz); +} + +inline void pb_free(void * ptr) { + vPortFree(ptr); +} + +__weak inline void * pb_realloc(void * ptr, size_t sz) { + return NULL; // shit out of luck (don't use mpack_writer_init_growable) +} + +__weak void * pb_memcpy(void * dest, const void * src, size_t sz) { + for (size_t offset = 0; offset < sz; offset++) + *((char*) dest + offset) = *((char*) src + offset); + return dest; +} + +__weak int pb_memcmp(const void * a, const void * b, size_t sz) { + for (size_t offset = 0; offset < sz; offset++) { + int diff = *((char*) a + offset) - *((char*) b + offset); + if (diff != 0) return diff; + } + return 0; +} + diff --git a/lib/pbdrv/ext/freertos/pb-mod.c b/lib/pbdrv/ext/freertos/pb-mod.c new file mode 100644 index 0000000..75495be --- /dev/null +++ b/lib/pbdrv/ext/freertos/pb-mod.c @@ -0,0 +1,9 @@ +#include <FreeRTOS.h> +#include <task.h> + +#include "../../pb-types.h" + +__weak void pb_mod_blocking_delay_ms(unsigned long ms) { + vTaskDelay(ms / portTICK_PERIOD_MS); +} + diff --git a/lib/pbdrv/ext/stdlib/include.cmake b/lib/pbdrv/ext/stdlib/include.cmake new file mode 100644 index 0000000..67fe80e --- /dev/null +++ b/lib/pbdrv/ext/stdlib/include.cmake @@ -0,0 +1,2 @@ +target_sources(pbdrv PRIVATE "${CMAKE_CURRENT_LIST_DIR}/pb-mem.c") + diff --git a/lib/pbdrv/ext/stdlib/pb-mem.c b/lib/pbdrv/ext/stdlib/pb-mem.c new file mode 100644 index 0000000..b260c2c --- /dev/null +++ b/lib/pbdrv/ext/stdlib/pb-mem.c @@ -0,0 +1,25 @@ +#include <stdlib.h> +#include <string.h> + +#include "../../pb-mem.h" + +inline void * pb_malloc(size_t sz) { + return malloc(sz); +} + +inline void pb_free(void * ptr) { + free(ptr); +} + +inline void * pb_realloc(void * ptr, size_t sz) { + return realloc(ptr, sz); +} + +void * pb_memcpy(void * dest, const void * src, size_t sz) { + return memcpy(dest, src, sz); +} + +int pb_memcmp(const void * a, const void * b, size_t sz) { + return memcmp(a, b, sz); +} + diff --git a/lib/pbdrv/mod/main.h b/lib/pbdrv/mod/main.h index 535ce06..ec48acd 100644 --- a/lib/pbdrv/mod/main.h +++ b/lib/pbdrv/mod/main.h @@ -1,6 +1,6 @@ #pragma once -#include "../types.h" +#include "../pb-types.h" typedef struct { const i2c_addr_t mod_addr; diff --git a/lib/pbdrv/mpack-config.h b/lib/pbdrv/mpack-config.h new file mode 100644 index 0000000..994d9b7 --- /dev/null +++ b/lib/pbdrv/mpack-config.h @@ -0,0 +1,23 @@ +#pragma once + +#include "pb-mem.h" + +// use pb_* functions +#define MPACK_STDLIB 0 + +// pb_* memory management functions +#define MPACK_FREE pb_free +#define MPACK_MALLOC pb_malloc +#define MPACK_REALLOC pb_realloc +#define MPACK_MEMCPY pb_memcpy +#define MPACK_MEMCMP pb_memcmp + +// more reasonable buffer size (all messages are small) +#define MPACK_BUFFER_SIZE 256 +#define MPACK_STACK_SIZE MPACK_BUFFER_SIZE +#define MPACK_PAGE_SIZE MPACK_BUFFER_SIZE + +// disable unused features (causes errors?) +#define MPACK_NODE 0 +#define MPACK_BUILDER 0 + diff --git a/lib/pbdrv/pb-buf.c b/lib/pbdrv/pb-buf.c new file mode 100644 index 0000000..3d6cb8a --- /dev/null +++ b/lib/pbdrv/pb-buf.c @@ -0,0 +1,9 @@ +#include "pb-buf.h" +#include "pb-mem.h" + +void pb_buf_free(pb_buf_t * buf) { + if (buf->data == NULL) return; + pb_free(buf->data); + buf->data = NULL; +} + diff --git a/lib/pbdrv/pb-buf.h b/lib/pbdrv/pb-buf.h new file mode 100644 index 0000000..78ee380 --- /dev/null +++ b/lib/pbdrv/pb-buf.h @@ -0,0 +1,20 @@ +#pragma once + +#include "pb-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//! binary buffer struct +typedef struct { + char * data; //! pointer to data + size_t size; //! size of data +} pb_buf_t; + +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 new file mode 100644 index 0000000..72be214 --- /dev/null +++ b/lib/pbdrv/pb-mem.h @@ -0,0 +1,19 @@ +#pragma once + +#include "pb-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void * pb_malloc(size_t sz); +void pb_free(void * ptr); +void * pb_realloc(void * ptr, size_t sz); + +void * pb_memcpy(void * dest, const void * src, size_t sz); +int pb_memcmp(const void * a, const void * b, size_t sz); + +#ifdef __cplusplus +} +#endif + diff --git a/lib/pbdrv/pb-mod.c b/lib/pbdrv/pb-mod.c index 740f2a5..c27194e 100644 --- a/lib/pbdrv/pb-mod.c +++ b/lib/pbdrv/pb-mod.c @@ -1,26 +1,34 @@ +#include "pb-buf.h" +#include "pb-msg.h" #include "pb-types.h" -#include "pb.h" +#include "pb-mod.h" +#include "pb-route.h" //! fallback module name -__weak const char * PBDRV_MOD_NAME = "???"; +__weak const char * PB_MOD_NAME = "???"; //! [private] placeholder global state variable static pb_global_state_t _global_state = PB_GS_NOINIT; -//! [private] main controller global state -static pb_global_state_t _main_state = PB_GS_NOINIT; +__weak pb_global_state_t pb_hook_mod_state_read() { + return _global_state; +} -// __weak enum pb_state pbdrv_hook_mod_state_read() { -// return _global_state; -// } +__weak void pb_hook_mod_state_write(pb_global_state_t state) { + _global_state = state; +} -// __weak void pbdrv_hook_mod_state_write(enum pb_state state) { -// _global_state = state; -// } +__weak void pb_i2c_recv(const uint8_t * data, size_t sz) { + pb_buf_t buf = { + .data = (char *) data, + .size = sz, + }; + pb_msg_t * msg = pb_msg_read(&buf); + if (msg == NULL) return; // invalid message + if (msg->cmd == NULL) return; // invalid message + + pb_route_msg(msg); -__weak void pbdrv_i2c_recv(const uint8_t * buf, size_t sz) { - return; + pb_msg_free(msg); } - -__weak void pbdrv_hook_main_state_update(pb_global_state_t state) { } diff --git a/lib/pbdrv/pb-mod.h b/lib/pbdrv/pb-mod.h index fa290bf..ae36d22 100644 --- a/lib/pbdrv/pb-mod.h +++ b/lib/pbdrv/pb-mod.h @@ -3,17 +3,13 @@ /** * \file puzzle bus driver implementation * - * Most \c pbdrv_* functions have a weak implementation, which may be + * 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 <stdint.h> -#include <stddef.h> -#include <stdbool.h> - #include "pb-types.h" #ifdef __cplusplus @@ -21,12 +17,22 @@ extern "C" { #endif //! puzzle module name (optional, default = "???") -extern const char * PBDRV_MOD_NAME; +extern const char * PB_MOD_NAME; //! puzzle module bus address (required) -extern const i2c_addr_t PBDRV_MOD_ADDR; +extern const i2c_addr_t PB_MOD_ADDR; + +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); + +pb_global_state_t pb_hook_mod_state_read(); +void pb_hook_mod_state_write(pb_global_state_t state); -void pbdrv_i2c_recv(const uint8_t * buf, size_t sz); -void pbdrv_i2c_send(i2c_addr_t i2c_addr, const uint8_t * buf, size_t sz); +/** + * \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.c b/lib/pbdrv/pb-msg.c new file mode 100644 index 0000000..7fd6662 --- /dev/null +++ b/lib/pbdrv/pb-msg.c @@ -0,0 +1,46 @@ +#include "mpack-config.h" + +#include <mpack.h> + +#include "pb-msg.h" +#include "pb-serial.h" +#include "pb-mem.h" + +pb_buf_t pb_msg_write(const pb_msg_t * msg) { + pb_buf_t buf = { 0 }; + if (msg == NULL) return buf; + + buf.data = pb_malloc(MPACK_BUFFER_SIZE); + if (buf.data == NULL) return buf; + buf.size = MPACK_BUFFER_SIZE; + + mpack_writer_t writer; + mpack_writer_init(&writer, buf.data, buf.size); + + pb_ser_w(&writer, msg); + + buf.size = mpack_writer_buffer_used(&writer); + mpack_writer_destroy(&writer); + return buf; +} + +pb_msg_t * pb_msg_read(const pb_buf_t * buf) { + mpack_reader_t reader; + mpack_reader_init_data(&reader, buf->data, buf->size); + + pb_msg_t * msg = pb_malloc(sizeof(pb_msg_t)); + + pb_ser_r(&reader, msg); + + mpack_reader_destroy(&reader); + return msg; +} + +void pb_msg_free(pb_msg_t * msg) { + // free message fields recursively + pb_ser_free(msg); + + // free message container that was created in \p pb_msg_read + pb_free(msg); +} + diff --git a/lib/pbdrv/pb-msg.h b/lib/pbdrv/pb-msg.h new file mode 100644 index 0000000..f27d4c4 --- /dev/null +++ b/lib/pbdrv/pb-msg.h @@ -0,0 +1,17 @@ +#pragma once + +#include "pb-types.h" +#include "pb-buf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +pb_buf_t pb_msg_write(const pb_msg_t * msg); +pb_msg_t * pb_msg_read(const pb_buf_t * buf); +void pb_msg_free(pb_msg_t * msg); + +#ifdef __cplusplus +} +#endif + diff --git a/lib/pbdrv/pb-read.c b/lib/pbdrv/pb-read.c deleted file mode 100644 index 843420d..0000000 --- a/lib/pbdrv/pb-read.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "pb-read.h" - - diff --git a/lib/pbdrv/pb-read.h b/lib/pbdrv/pb-read.h deleted file mode 100644 index 7a6f0df..0000000 --- a/lib/pbdrv/pb-read.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef __cplusplus -} -#endif - diff --git a/lib/pbdrv/pb-route.c b/lib/pbdrv/pb-route.c new file mode 100644 index 0000000..ee47700 --- /dev/null +++ b/lib/pbdrv/pb-route.c @@ -0,0 +1,97 @@ +#include "pb-route.h" +#include "pb-mod.h" +#include "pb-send.h" +#include "pb-types.h" +#include "pb-mem.h" + +__weak bool pb_hook_route_msg(pb_msg_t * msg) { return false; } +__weak void pb_route_msg(pb_msg_t * msg) { + if (pb_hook_route_msg(msg)) return; + + switch (msg->type) { + case PB_CMD_PROP: return pb_route_cmd_prop(msg); + case PB_CMD_STATE: return pb_route_cmd_state(msg); + case PB_CMD_MAGIC: return pb_route_cmd_magic(msg); + default: return; + } +} + +__weak bool pb_hook_route_cmd_prop(pb_msg_t * msg) { return false; } +__weak void pb_route_cmd_prop(pb_msg_t * msg) { + if (pb_hook_route_cmd_prop(msg)) return; + + switch (msg->action) { + case PB_ACTION_REQ: return pb_route_cmd_prop_req(msg); + case PB_ACTION_RES: return pb_route_cmd_prop_res(msg); + case PB_ACTION_SET: return pb_route_cmd_prop_set(msg); + default: return; + } +} + +__weak bool pb_hook_route_cmd_state(pb_msg_t * msg) { return false; } +__weak void pb_route_cmd_state(pb_msg_t * msg) { + if (pb_hook_route_cmd_state(msg)) return; + + switch (msg->action) { + case PB_ACTION_REQ: return pb_route_cmd_state_req(msg); + case PB_ACTION_RES: return pb_route_cmd_state_res(msg); + case PB_ACTION_SET: return pb_route_cmd_state_set(msg); + default: return; + } +} + +__weak bool pb_hook_route_cmd_magic(pb_msg_t * msg) { return false; } +__weak void pb_route_cmd_magic(pb_msg_t * msg) { + if (pb_hook_route_cmd_magic(msg)) return; + + switch (msg->action) { + case PB_ACTION_REQ: return pb_route_cmd_magic_req(msg); + case PB_ACTION_RES: return pb_route_cmd_magic_res(msg); + default: return; + } +} + +// all properties are user-defined +__weak void pb_route_cmd_prop_req(pb_msg_t * msg) {} +__weak void pb_route_cmd_prop_res(pb_msg_t * msg) {} +__weak void pb_route_cmd_prop_set(pb_msg_t * msg) {} + +static pb_global_state_t _main_state = PB_GS_NOINIT; +__weak void pb_hook_main_state_update(pb_global_state_t state) {} +__weak void pb_route_cmd_state_req(pb_msg_t * msg) { + pb_global_state_t own_state = pb_hook_mod_state_read(); + pb_buf_t buf = pb_send_state_res(own_state); + pb_send_reply(msg, &buf); + pb_buf_free(&buf); + + // notify of new global state variable + pb_cmd_state_t * cmd = msg->cmd; + if (cmd->state != _main_state) + pb_hook_main_state_update(cmd->state); + _main_state = cmd->state; +} + +__weak void pb_route_cmd_state_res(pb_msg_t * msg) {} + +__weak void pb_route_cmd_state_set(pb_msg_t * msg) { + pb_cmd_state_t * cmd = msg->cmd; + pb_hook_mod_state_write(cmd->state); +} + +__weak void pb_route_cmd_magic_req(pb_msg_t * msg) { + pb_cmd_magic_t * cmd = msg->cmd; + // return early if magic has wrong size + if (cmd->_magic_size != sizeof(pb_cmd_magic_req)) return; + // // return early if magic doesn't match + if (pb_memcmp(cmd->magic, pb_cmd_magic_req, sizeof(pb_cmd_magic_req)) != 0) return; + + // FIXME: this should be removed (see handover: RP2040 I2C limitations) + pb_mod_blocking_delay_ms(2000); + + pb_buf_t buf = pb_send_magic_res(); + pb_send_reply(msg, &buf); + pb_buf_free(&buf); +} + +__weak void pb_route_cmd_magic_res(pb_msg_t * msg) { } + diff --git a/lib/pbdrv/pb-route.h b/lib/pbdrv/pb-route.h new file mode 100644 index 0000000..41c009a --- /dev/null +++ b/lib/pbdrv/pb-route.h @@ -0,0 +1,37 @@ +#pragma once + +#include "pb-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +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); + +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_hook_main_state_update(pb_global_state_t state); + +void pb_route_cmd_magic_req(pb_msg_t * msg); +void pb_route_cmd_magic_res(pb_msg_t * msg); + +#ifdef __cplusplus +} +#endif + diff --git a/lib/pbdrv/pb-send.c b/lib/pbdrv/pb-send.c new file mode 100644 index 0000000..66c43c1 --- /dev/null +++ b/lib/pbdrv/pb-send.c @@ -0,0 +1,120 @@ +#include "pb-send.h" +#include "pb-mod.h" +#include "pb-msg.h" + +__weak void pb_send_reply(pb_msg_t * msg, pb_buf_t * reply) { + return pb_i2c_send(msg->sender, (uint8_t *) reply->data, reply->size); +} + +pb_buf_t pb_send_read_req(uint8_t propid) { + pb_cmd_prop_t cmd = { + .propid = propid, + .value = NULL, + ._value_size = 0, + }; + pb_msg_t msg = { + .type = PB_CMD_PROP, + .action = PB_ACTION_REQ, + .sender = PB_MOD_ADDR, + .cmd = &cmd, + }; + return pb_msg_write(&msg); +} + +pb_buf_t pb_send_read_res(uint8_t propid, uint8_t * value, size_t size) { + pb_cmd_prop_t cmd = { + .propid = propid, + .value = value, + ._value_size = size, + }; + pb_msg_t msg = { + .type = PB_CMD_PROP, + .action = PB_ACTION_RES, + .sender = PB_MOD_ADDR, + .cmd = &cmd, + }; + return pb_msg_write(&msg); +} + +pb_buf_t pb_send_write_req(uint8_t propid, uint8_t * value, size_t size) { + pb_cmd_prop_t cmd = { + .propid = propid, + .value = value, + ._value_size = size, + }; + pb_msg_t msg = { + .type = PB_CMD_PROP, + .action = PB_ACTION_REQ, + .sender = PB_MOD_ADDR, + .cmd = &cmd, + }; + return pb_msg_write(&msg); +} + +pb_buf_t pb_send_state_req() { + pb_cmd_state_t cmd = { + .state = pb_hook_mod_state_read(), + }; + pb_msg_t msg = { + .type = PB_CMD_STATE, + .action = PB_ACTION_REQ, + .sender = PB_MOD_ADDR, + .cmd = &cmd, + }; + return pb_msg_write(&msg); +} + +pb_buf_t pb_send_state_res(pb_global_state_t state) { + pb_cmd_state_t cmd = { + .state = state, + }; + pb_msg_t msg = { + .type = PB_CMD_STATE, + .action = PB_ACTION_RES, + .sender = PB_MOD_ADDR, + .cmd = &cmd, + }; + return pb_msg_write(&msg); +} + +pb_buf_t pb_send_state_set(pb_global_state_t state) { + pb_cmd_state_t cmd = { + .state = state, + }; + pb_msg_t msg = { + .type = PB_CMD_STATE, + .action = PB_ACTION_SET, + .sender = PB_MOD_ADDR, + .cmd = &cmd, + }; + return pb_msg_write(&msg); +} + +pb_buf_t pb_send_magic_req() { + pb_cmd_magic_t cmd = { + .magic = (char *) &pb_cmd_magic_req[0], + ._magic_size = sizeof(pb_cmd_magic_req), + }; + pb_msg_t msg = { + .type = PB_CMD_MAGIC, + .action = PB_ACTION_REQ, + .sender = PB_MOD_ADDR, + .cmd = &cmd, + }; + return pb_msg_write(&msg); +} + +pb_buf_t pb_send_magic_res() { + pb_cmd_magic_t cmd = { + .magic = (char *) &pb_cmd_magic_res[0], + ._magic_size = sizeof(pb_cmd_magic_res), + }; + pb_msg_t msg = { + .type = PB_CMD_MAGIC, + .action = PB_ACTION_RES, + .sender = PB_MOD_ADDR, + .cmd = &cmd, + }; + return pb_msg_write(&msg); +} + diff --git a/lib/pbdrv/pb-send.h b/lib/pbdrv/pb-send.h new file mode 100644 index 0000000..2f8be1e --- /dev/null +++ b/lib/pbdrv/pb-send.h @@ -0,0 +1,24 @@ +#pragma once + +#include "pb-types.h" +#include "pb-buf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void pb_send_reply(pb_msg_t * msg, pb_buf_t * reply); + +pb_buf_t pb_send_read_req(uint8_t propid); +pb_buf_t pb_send_read_res(uint8_t propid, uint8_t * value, size_t size); +pb_buf_t pb_send_write_req(uint8_t propid, uint8_t * value, size_t size); +pb_buf_t pb_send_state_req(); +pb_buf_t pb_send_state_res(pb_global_state_t state); +pb_buf_t pb_send_state_set(pb_global_state_t state); +pb_buf_t pb_send_magic_req(); +pb_buf_t pb_send_magic_res(); + +#ifdef __cplusplus +} +#endif + diff --git a/lib/pbdrv/pb-serial.c b/lib/pbdrv/pb-serial.c new file mode 100644 index 0000000..b9ee4b1 --- /dev/null +++ b/lib/pbdrv/pb-serial.c @@ -0,0 +1,111 @@ +#include <mpack.h> + +#include "pb-mem.h" +#include "pb-serial.h" +#include "pb-types.h" + +void pb_ser_w(mpack_writer_t * writer, const pb_msg_t * cmd) { + if (cmd == NULL) return; + + mpack_write_u8(writer, cmd->type); + mpack_write_u8(writer, cmd->action); + mpack_write_u16(writer, cmd->sender); + if (cmd->cmd == NULL) return; + + switch (cmd->type) { + case PB_CMD_PROP: return pb_ser_w_cmd_prop(writer, cmd); + case PB_CMD_STATE: return pb_ser_w_cmd_state(writer, cmd); + case PB_CMD_MAGIC: return pb_ser_w_cmd_magic(writer, cmd); + default: break; + } +} +void pb_ser_r(mpack_reader_t * reader, pb_msg_t * cmd) { + cmd->type = mpack_expect_u8(reader); + cmd->action = mpack_expect_u8(reader); + cmd->sender = mpack_expect_u16(reader); + + switch (cmd->type) { + case PB_CMD_PROP: return pb_ser_r_cmd_prop(reader, cmd); + case PB_CMD_STATE: return pb_ser_r_cmd_state(reader, cmd); + case PB_CMD_MAGIC: return pb_ser_r_cmd_magic(reader, cmd); + default: break; + } +} +void pb_ser_free(pb_msg_t * cmd) { + if (cmd == NULL) return; + + switch (cmd->type) { + case PB_CMD_PROP: return pb_ser_free_cmd_prop(cmd); + case PB_CMD_STATE: return pb_ser_free_cmd_state(cmd); + case PB_CMD_MAGIC: return pb_ser_free_cmd_magic(cmd); + default: break; + } +} + +void pb_ser_w_cmd_prop(mpack_writer_t * writer, const pb_msg_t * _msg) { + pb_cmd_prop_t * cmd = _msg->cmd; + + mpack_write_u8(writer, cmd->propid); + mpack_write_bin(writer, (char *) cmd->value, cmd->_value_size); +} +void pb_ser_r_cmd_prop(mpack_reader_t * reader, pb_msg_t * _msg) { + pb_cmd_prop_t * cmd = _msg->cmd = pb_malloc(sizeof(pb_cmd_prop_t)); + + cmd->propid = mpack_expect_u8(reader); + cmd->_value_size = mpack_expect_bin(reader); + cmd->value = (uint8_t *) mpack_read_bytes_alloc(reader, cmd->_value_size); + mpack_done_bin(reader); +} +void pb_ser_free_cmd_prop(pb_msg_t * _msg) { + if (_msg->cmd != NULL) { + pb_cmd_prop_t * cmd = _msg->cmd; + if (cmd->value != NULL) { + MPACK_FREE(cmd->value); + cmd->value = NULL; + } + pb_free(_msg->cmd); + _msg->cmd = NULL; + } +} + +void pb_ser_w_cmd_state(mpack_writer_t * writer, const pb_msg_t * _msg) { + pb_cmd_state_t * cmd = _msg->cmd; + + mpack_write_u8(writer, cmd->state); +} +void pb_ser_r_cmd_state(mpack_reader_t * reader, pb_msg_t * _msg) { + pb_cmd_state_t * cmd = _msg->cmd = pb_malloc(sizeof(pb_cmd_state_t)); + + cmd->state = mpack_expect_u8(reader); +} +void pb_ser_free_cmd_state(pb_msg_t * _msg) { + if (_msg->cmd != NULL) { + pb_free(_msg->cmd); + _msg->cmd = NULL; + } +} + +void pb_ser_w_cmd_magic(mpack_writer_t * writer, const pb_msg_t * _msg) { + pb_cmd_magic_t * cmd = _msg->cmd; + + mpack_write_bin(writer, (char *) cmd->magic, cmd->_magic_size); +} +void pb_ser_r_cmd_magic(mpack_reader_t * reader, pb_msg_t * _msg) { + pb_cmd_magic_t * cmd = _msg->cmd = pb_malloc(sizeof(pb_cmd_magic_t)); + + cmd->_magic_size = mpack_expect_bin(reader); + cmd->magic = mpack_read_bytes_alloc(reader, cmd->_magic_size); + mpack_done_bin(reader); +} +void pb_ser_free_cmd_magic(pb_msg_t * _msg) { + if (_msg->cmd != NULL) { + pb_cmd_magic_t * cmd = _msg->cmd; + if (cmd->magic != NULL) { + MPACK_FREE(cmd->magic); + cmd->magic = NULL; + } + pb_free(_msg->cmd); + _msg->cmd = NULL; + } +} + diff --git a/lib/pbdrv/pb-serial.h b/lib/pbdrv/pb-serial.h new file mode 100644 index 0000000..d3d0007 --- /dev/null +++ b/lib/pbdrv/pb-serial.h @@ -0,0 +1,32 @@ +#pragma once + +#include <mpack.h> + +#include "pb-types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define __pb_cmd(name) \ + pb_ser_r_t pb_ser_r_##name; \ + pb_ser_w_t pb_ser_w_##name; \ + pb_ser_free_t pb_ser_free_##name; + +typedef void pb_ser_w_t(mpack_writer_t * writer, const pb_msg_t * msg); +pb_ser_w_t pb_ser_w; + +typedef void pb_ser_r_t(mpack_reader_t * reader, pb_msg_t * msg); +pb_ser_r_t pb_ser_r; + +typedef void pb_ser_free_t(pb_msg_t * msg); +pb_ser_free_t pb_ser_free; + +__pb_cmd(cmd_prop) +__pb_cmd(cmd_state) +__pb_cmd(cmd_magic) + +#ifdef __cplusplus +} +#endif + diff --git a/lib/pbdrv/pb-types.h b/lib/pbdrv/pb-types.h index 96ffc37..4d085f9 100644 --- a/lib/pbdrv/pb-types.h +++ b/lib/pbdrv/pb-types.h @@ -1,4 +1,6 @@ #pragma once + +#include <stdbool.h> #include <stdint.h> #include <stddef.h> @@ -14,24 +16,24 @@ extern "C" { #define __weak #endif +//! I2C address (10 or 7 bit) typedef uint16_t i2c_addr_t; //! puzzle bus command types enum pb_cmd_id { - PB_CMD_REQ_READ, //!< request a puzzle module property - PB_CMD_RES_READ, //!< respond to a puzzle module property request - PB_CMD_REQ_WRITE, //!< request to write a puzzle module property - PB_CMD_REQ_STATE, //!< request global state - PB_CMD_RES_STATE, //!< respond to a global state request - PB_CMD_REQ_SET_STATE, //!< request to overwrite module global state - PB_CMD_MAGIC, //!< magic message (regular i2c command) + PB_CMD_PROP, //!< puzzle module property + PB_CMD_STATE, //!< global state + PB_CMD_MAGIC, //!< magic (handshake) }; typedef enum pb_cmd_id pb_cmd_id_t; -//! magic sent from main controller to puzzle module -static const char pb_cmd_magic_msg[] = { 0x70, 0x75, 0x7a, 0x62, 0x75, 0x73 }; -//! magic reply from puzzle module back to main controller -static const char pb_cmd_magic_res[] = { 0x67, 0x61, 0x6d, 0x69, 0x6e, 0x67 }; +//! puzzle bus command action types +enum pb_action { + PB_ACTION_REQ, //!< request + PB_ACTION_RES, //!< response + PB_ACTION_SET, //!< (over)write +}; +typedef enum pb_action pb_action_t; //! puzzle bus global states enum pb_global_state { @@ -42,54 +44,35 @@ enum pb_global_state { }; typedef enum pb_global_state pb_global_state_t; -//! puzzle bus message header (shared by all commands) -typedef struct { - const pb_cmd_id_t type; //!< command type - const i2c_addr_t sender; //!< i2c address of sender -} pb_msg_header_t; - -//! PB_CMD_REQ_READ data -typedef struct { - const pb_msg_header_t header; - const uint8_t propid; //!< state property id to return -} pb_cmd_req_read_t; - -//! PB_CMD_RES_READ data -typedef struct { - const pb_msg_header_t header; - const uint8_t propid; //!< id of returned state property - const uint8_t value[]; -} pb_cmd_res_read_t; - -//! PB_CMD_REQ_WRITE data -typedef struct { - const pb_msg_header_t header; - const uint8_t propid; //!< state property id to write - const uint8_t value[]; //!< new value of property -} pb_cmd_req_write_t; +//! magic sent from main controller to puzzle module +static const char pb_cmd_magic_req[] = { 0x70, 0x75, 0x7a, 0x62, 0x75, 0x73 }; +//! magic reply from puzzle module back to main controller +static const char pb_cmd_magic_res[] = { 0x67, 0x61, 0x6d, 0x69, 0x6e, 0x67 }; -//! PB_CMD_REQ_STATE data +//! puzzle bus message header (shared by all commands) typedef struct { - const pb_msg_header_t header; - const pb_global_state_t state; //!< global state of sender -} pb_cmd_req_state_t; + pb_cmd_id_t type; //!< command type + pb_action_t action; //!< command action + i2c_addr_t sender; //!< i2c address of sender + void * cmd; //!< command data (type dependant) +} pb_msg_t; -//! PB_CMD_RES_STATE data +//! PB_CMD_PROP data typedef struct { - const pb_msg_header_t header; - const pb_global_state_t state; //!< global state of sender -} pb_cmd_res_state_t; + uint8_t propid; //!< id of state property + uint8_t * value; //!< new or current value + size_t _value_size; //!< [META] size of \p value +} pb_cmd_prop_t; -//! PB_CMD_REQ_SET_STATE data +//! PB_CMD_STATE data typedef struct { - const pb_msg_header_t header; - const pb_global_state_t state; //!< new global state -} pb_cmd_req_set_state_t; + pb_global_state_t state; //!< global state +} pb_cmd_state_t; //! PB_CMD_MAGIC data typedef struct { - const pb_msg_header_t header; - const char magic[]; //!< magic value + char * magic; //!< magic value + size_t _magic_size; //!< [META] size of \p magic } pb_cmd_magic_t; #ifdef __cplusplus diff --git a/lib/pbdrv/pb-write.c b/lib/pbdrv/pb-write.c deleted file mode 100644 index 752a4ac..0000000 --- a/lib/pbdrv/pb-write.c +++ /dev/null @@ -1,35 +0,0 @@ -#include <mpack.h> - -#include "pb-write.h" - -typedef struct { - mpack_writer_t writer; - pbdrv_buf_t buf; -} pbdrv_writer_t; - -static pbdrv_writer_t pbdrv_write_init() { - pbdrv_writer_t writer; - mpack_writer_init_growable(&writer.writer, &writer.buf.data, &writer.buf.size); - return writer; -} - -static pbdrv_buf_t pbdrv_write_finish(pbdrv_writer_t * writer) { - if (mpack_writer_destroy(&writer->writer) != mpack_ok) { - writer->buf.data = NULL; - writer->buf.size = 0; - } - return writer->buf; -} - -static void pbdrv_write_msg_header(pbdrv_writer_t * writer, pb_msg_header_t header) { - mpack_write_u8(&writer->writer, header.type); - mpack_write_u16(&writer->writer, header.sender); -} - -pbdrv_buf_t pbdrv_write_cmd_req_set_state(pb_cmd_req_set_state_t data) { - pbdrv_writer_t writer = pbdrv_write_init(); - pbdrv_write_msg_header(&writer, data.header); - mpack_write_u8(&writer.writer, data.state); - return pbdrv_write_finish(&writer); -} - diff --git a/lib/pbdrv/pb-write.h b/lib/pbdrv/pb-write.h deleted file mode 100644 index 3245898..0000000 --- a/lib/pbdrv/pb-write.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "pb-types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//! binary buffer struct -typedef struct { - char * data; //! pointer to data - size_t size; //! size of data -} pbdrv_buf_t; - -pbdrv_buf_t pbdrv_write_cmd_req_read(pb_cmd_req_read_t data); -pbdrv_buf_t pbdrv_write_cmd_res_read(pb_cmd_res_read_t data); -pbdrv_buf_t pbdrv_write_cmd_req_write(pb_cmd_req_write_t data); -pbdrv_buf_t pbdrv_write_cmd_req_state(pb_cmd_req_state_t data); -pbdrv_buf_t pbdrv_write_cmd_res_state(pb_cmd_res_state_t data); -pbdrv_buf_t pbdrv_write_cmd_req_set_state(pb_cmd_req_set_state_t data); -pbdrv_buf_t pbdrv_write_cmd_magic(pb_cmd_magic_t data); - -#ifdef __cplusplus -} -#endif - diff --git a/lib/pbdrv/pb.h b/lib/pbdrv/pb.h index b6efed0..0f2e9d1 100644 --- a/lib/pbdrv/pb.h +++ b/lib/pbdrv/pb.h @@ -10,13 +10,14 @@ #define PB_ADDR_ADA_NEO_3 0x30 #define PB_ADDR_ADA_NEO_4 0x32 -// TODO: ??? -#define PB_ADDR_MOD_NEOTRELLIS 0 -#define PB_ADDR_MOD_SOFTWARE 0 -#define PB_ADDR_MOD_HARDWARE 0 -#define PB_ADDR_MOD_VAULT 0 -// #define BUSADDR_MOD_AUTOMATION 0 +// Main controller +#define PB_ADDR_MOD_MAIN 0x08 -// main controller -#define PB_ADDR_MOD_MAIN 0x00 +// Puzzle modules +#define PB_ADDR_MOD_NEOTRELLIS 0x21 +#define PB_ADDR_MOD_SOFTWARE 0x22 +#define PB_ADDR_MOD_HARDWARE 0x23 +#define PB_ADDR_MOD_VAULT 0x24 +// #define BUSADDR_MOD_AUTOMATION 0x25 +#define PB_ADDR_MOD_DUMMY 0x69 |