diff options
61 files changed, 1228 insertions, 447 deletions
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 7d492b0..989b837 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -13,6 +13,7 @@ project(pbc C CXX) add_subdirectory(lib/mpack) add_subdirectory(lib/i2ctcp) add_subdirectory(lib/pbdrv) +include(lib/pbdrv/ext/stdlib/include.cmake) add_executable(pbc main.cpp diff --git a/client/i2c.cpp b/client/i2c.cpp index 4dbc724..78e5585 100644 --- a/client/i2c.cpp +++ b/client/i2c.cpp @@ -10,7 +10,7 @@ // #include "pb/mod/main.h" -bool i2c_dump_send = false; +bool i2c_dump_send = true; bool i2c_dump_recv = true; void i2c_send(uint16_t addr, const char * data, size_t data_size) { diff --git a/client/readme.md b/client/readme.md index da48cf1..fcde40d 100644 --- a/client/readme.md +++ b/client/readme.md @@ -45,3 +45,8 @@ send 0x39 68:65:6c:6c:6f 44 0x20 'world' 33 The data is concatenated, and may contain mixed types of literals +## known bugs (TODO) + +- tab completion for `dump` seems to print garbage sometimes +- the send command with an address but no data causes a segmentation fault + diff --git a/docs/handover.adoc b/docs/handover.adoc index 8c86e1b..016dfa0 100644 --- a/docs/handover.adoc +++ b/docs/handover.adoc @@ -8,10 +8,24 @@ went. We found the previous handover documents to be unhelpful when determining the 'actual' state of the project in the first few weeks, and felt they did not address the pitfalls of this project. -== Incidents - == Project History +=== Garbage workarounds + +This section details unelegant workarounds that should be removed from the +code. All workarounds are marked with ``FIXME:`` comments referring to one of +the workarounds mentioned in this section. + +RP2040 I^2^C limitations:: +- All puzzle module drivers have a hard-coded 2 second delay between receiving + the MAGIC handshake request and the MAGIC handshake response handler. This + was done to ensure responses are not ignored by the RP2040 (main controller) + while it is still in I^2^C master mode. + +=== Incidents + +== Group History + === 19-20 .19-20 group composition 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 diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 7a0b136..d6cbf33 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -10,6 +10,7 @@ include(lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_impor add_subdirectory(lib/mpack) add_subdirectory(lib/i2ctcp) add_subdirectory(lib/pbdrv) +include(lib/pbdrv/ext/stdlib/include.cmake) # pico-stdlib is compatible with C stdlib project(puzzlebox_main C CXX ASM) @@ -23,18 +24,20 @@ add_executable(main mod.c tasks.c blink.c + pbdrv.c ) pico_enable_stdio_usb(main 1) pico_enable_stdio_uart(main 0) pico_add_extra_outputs(main) -include_directories(lib/pico-sdk/lib/lwip/contrib/ports/freertos/include) +# include_directories(lib/pico-sdk/lib/lwip/contrib/ports/freertos/include) target_include_directories(main PRIVATE .) target_link_libraries(main pico_cyw43_arch_lwip_sys_freertos pico_stdlib + pico_i2c_slave hardware_i2c FreeRTOS-Kernel FreeRTOS-Kernel-Heap4 diff --git a/main/config.def.h b/main/config.def.h index 3d325fe..e9503ed 100644 --- a/main/config.def.h +++ b/main/config.def.h @@ -56,11 +56,10 @@ #endif /** \} */ -#ifndef CFG_LED_PIN -//! status LED pin -#define CFG_LED_PIN CYW43_WL_GPIO_LED_PIN -#endif - +/** + * \name I2C configuration + * \{ + */ #ifndef CFG_SDA_PIN //! I^2^C SDA pin #define CFG_SDA_PIN 16 @@ -69,4 +68,15 @@ //! I^2^C SCL pin #define CFG_SCL_PIN 17 #endif +/** \} */ + +#ifndef CFG_LED_PIN +//! status LED pin +#define CFG_LED_PIN CYW43_WL_GPIO_LED_PIN +#endif + +#ifndef CFG_PB_MOD_MAX +//! maximum number of simultaniously connected puzzle modules +#define CFG_PB_MOD_MAX 8 +#endif @@ -3,69 +3,47 @@ #include <stdio.h> #include <stddef.h> #include <stdint.h> -#include <string.h> #include <pico/stdlib.h> #include <hardware/i2c.h> #include "i2c.h" #include "pb-mod.h" - -// uint8_t* scan_bus(uint8_t *array) { -// int ret; -// int i = 0; -// uint8_t rxdata; -// -// for(int addr = 0; addr < (1<<7); addr++) { -// // ignore reserved addresses -// // These are any addresses of the form 000 0xxx or 111 1xxx -// // ret = i2c_read_blocking(I2C_PORT, addr, &rxdata, 1, false); -// -// // if acknowledged -> ret == number of bytes sent -// if(ret > 0){ -// printf("found i2c slave on addr: %d\n", addr); -// array[i] = addr; -// i++; -// } -// } -// -// return array; -// } - -void pbdrv_i2c_recv(const uint8_t * a, size_t b) { - printf("%.*s", b, a); +#include "pbdrv.h" +#include "config.h" +#include "pb-buf.h" +#include "pb-send.h" + +i2c_addr_t modules[CFG_PB_MOD_MAX]; +size_t modules_size = 0; + +static void state_exchange() { + for (size_t i = 0; i < modules_size; i++) { + pb_buf_t buf = pb_send_state_req(); + + pb_buf_free(&buf); + } } void bus_task() { - // scan bus for slaves - // send updates at regular intervals - vTaskDelay(1000 / portTICK_PERIOD_MS); - - // int i = 0; - // uint8_t found[MAX_SLAVES]; - // init_addr_array(found, MAX_SLAVES); - - while (true) { - vTaskDelay(10 / portTICK_PERIOD_MS); - pbdrv_i2c_send(0x69, (uint8_t *) "bbbbbbbb", 9); - } + // do a scan of the bus + bus_scan(); - // while(1) { - // // printf("Bus scan!"); - // scan_bus(found); + // FIXME: this should be removed (see handover: RP2040 I2C limitations) + // wait for 5 seconds until all handshake responses are received + pb_mod_blocking_delay_ms(5e3); - // for(int i = 0; i < MAX_SLAVES; i++){ - // if( found[i] == 0x00 ) - // break; - // - // uint8_t data = 0x01; - // // send data to found slave address - // write_i2c(found[i], &data, 1); + while(1) { + // send my state to all puzzle modules + state_exchange(); + + // wait 1 second + pb_mod_blocking_delay_ms(1e3); + } +} - // data = 0x02; - // write_i2c(found[i], &data, 1); - // // request update from slave addr at found[i] - // //write_i2c(); - // } - // } +void pb_route_cmd_magic_res(pb_msg_t * msg) { + if (modules_size == CFG_PB_MOD_MAX) return; + modules[modules_size++] = msg->sender; + printf("i2c: registered puzzle module w/ address 0x%02x\n", msg->sender); } @@ -1,9 +1,4 @@ #pragma once -// https://github.com/raspberrypi/pico-examples/tree/master/i2c -// https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html - -#define MAX_SLAVES 10 -#define MAX_TIMEOUT_TIME 50 //ms //! looking for slave addresses and requesting updates void bus_task(); diff --git a/main/init.c b/main/init.c index bd00c04..6d29d19 100644 --- a/main/init.c +++ b/main/init.c @@ -7,7 +7,7 @@ #include "config.h" #include "init.h" #include "tasks.h" -#include "drv/rp2040/mod.h" +#include "pbdrv.h" static void init_stdio() { stdio_init_all(); @@ -32,7 +32,10 @@ static void init_i2c() { gpio_set_function(CFG_SDA_PIN, GPIO_FUNC_I2C); gpio_set_function(CFG_SCL_PIN, GPIO_FUNC_I2C); - pbdrv_setup(); + gpio_pull_up(CFG_SDA_PIN); + gpio_pull_up(CFG_SCL_PIN); + + pb_setup(); } static void async_init() { diff --git a/main/main.c b/main/main.c index 7c1bb6a..1c615fc 100644 --- a/main/main.c +++ b/main/main.c @@ -6,12 +6,5 @@ int main() { init(); vTaskStartScheduler(); - - while(1) { - // we should have never gotten here - printf("Why are we here?!\n"); - } - - return 0; } @@ -1,5 +1,6 @@ +#include "pb.h" #include "pb-mod.h" -const char * PBDRV_MOD_NAME = "main controller"; -const i2c_addr_t PBDRV_MOD_ADDR = 0x20; +const char * PB_MOD_NAME = "main controller"; +const i2c_addr_t PB_MOD_ADDR = PB_ADDR_MOD_MAIN; diff --git a/main/pbdrv.c b/main/pbdrv.c new file mode 100644 index 0000000..0624897 --- /dev/null +++ b/main/pbdrv.c @@ -0,0 +1,95 @@ +#include "pb.h" + +#include "pb.h" +#include "pb-types.h" +#include "pb-mod.h" +#include "pb-send.h" + +#include <hardware/i2c.h> +#include <hardware/gpio.h> +#include <pico/i2c_slave.h> +#include <pico/stdio.h> + +#include <FreeRTOS.h> +#include <stdio.h> +#include <timers.h> + +#define PB_I2C i2c0 +#define BUF_SIZE 256 +#define MSGS_MAX 4 + +typedef struct { + uint8_t data[BUF_SIZE]; + size_t size; +} i2c_msg_buf_t; + +static i2c_msg_buf_t msgs[MSGS_MAX]; +static size_t msg_index = 0; + +static void async_pb_i2c_recv(void * _msg, uint32_t _) { + i2c_msg_buf_t * msg = _msg; + pb_i2c_recv(msg->data, msg->size); +} + +static void msg_complete(i2c_msg_buf_t * msg) { + // defer pb_i2c_recv call to FreeRTOS scheduler as pb_i2c_recv takes + // too long to return from an ISR + xTimerPendFunctionCallFromISR(async_pb_i2c_recv, msg, 0, NULL); + + // prepare next message for use + msg_index = (msg_index + 1) % MSGS_MAX; + msgs[msg_index].size = 0; +} + +// This function is called from the I2C ISR +static void recv_event(i2c_inst_t *i2c, i2c_slave_event_t event) { + i2c_msg_buf_t * msg = &msgs[msg_index]; + + switch (event) { + case I2C_SLAVE_RECEIVE: { + if (msg->size == BUF_SIZE) return; + msg->data[msg->size++] = i2c_read_byte_raw(PB_I2C); + break; + } + case I2C_SLAVE_FINISH: { + msg_complete(msg); + break; + } + default: break; + } +} + +void pb_setup() { + i2c_init(PB_I2C, PB_CLOCK_SPEED_HZ); + i2c_slave_init(PB_I2C, PB_MOD_ADDR, &recv_event); +} + +__weak void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) { + i2c_set_slave_mode(PB_I2C, false, PB_MOD_ADDR); + + // false to write stop condition to i2c bus + i2c_write_timeout_us(PB_I2C, addr, buf, sz, false, PB_TIMEOUT_US); + + i2c_set_slave_mode(PB_I2C, true, PB_MOD_ADDR); +} + +void bus_scan() { + i2c_set_slave_mode(PB_I2C, false, PB_MOD_ADDR); + + pb_buf_t buf = pb_send_magic_req(); + + // check for all 7-bit addresses + uint16_t addr_max = 1 << 7; + for (uint16_t addr = 0x00; addr < addr_max; addr++) { + i2c_write_timeout_us(PB_I2C, addr, (uint8_t *) buf.data, buf.size, false, PB_TIMEOUT_US); + } + + pb_buf_free(&buf); + + i2c_set_slave_mode(PB_I2C, true, PB_MOD_ADDR); +} + +void pb_mod_blocking_delay_ms(unsigned long ms) { + vTaskDelay(ms / portTICK_PERIOD_MS); +} + diff --git a/main/pbdrv.h b/main/pbdrv.h new file mode 100644 index 0000000..a751000 --- /dev/null +++ b/main/pbdrv.h @@ -0,0 +1,52 @@ +#pragma once + +#include "pb-types.h" + +/** + * This is the RP2040 puzzle bus driver. This file is no longer inside + * lib/pb//rp2040 as it is tightly coupled to both the pico-sdk and + * freertos functions. I have tried to get FreeRTOS to play nicely with the + * CMake subproject layout, but the pico-sdk and the rp2040 port of freertos + * both rely on CMake's import() functionality, which makes using FreeRTOS in a + * libary context extremely messy. + * + * The workaround implemented in this driver was already kind of messy, and a + * different microcontroller should be used for the main controller instead. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +//! puzzle bus driver setup +void pb_setup(); + +/** + * 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). + */ +void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz); + +/** + * \brief Scan the bus for I2C slaves, and send handshake messages to ACK-ing + * slaves. + * + * As a result of the RP2040 hardware limitations detailed at the top of this + * file, this function is also implemented in this file, even through it does + * not belong to the puzzle bus driver. In order to not miss any handshake + * responses, the bus should remain busy during the entire scan. + */ +void bus_scan(); + +#ifdef __cplusplus +} +#endif + diff --git a/main/sock.c b/main/sock.c index 5d75e8f..33a6111 100644 --- a/main/sock.c +++ b/main/sock.c @@ -9,10 +9,8 @@ #include "config.h" #include "i2ctcpv1.h" #include "sock.h" -#include "i2c.h" -#include <hardware/i2c.h> +#include "pb-mod.h" -extern uint8_t found[MAX_SLAVES]; struct netconn* current_connection = NULL; i2ctcp_msg_t recv_msg; @@ -37,31 +35,30 @@ void i2c_send(uint16_t addr, const char * data, size_t data_size) { free(buf); } -void i2c_recv(uint16_t addr, const char * data, size_t data_size) { - // TODO: this function should forward the recieved message onto the puzzle - // bus instead of printing/replying - - printf("Addr: %lu, Data: %c, Data_size: %lu\n", addr, data[0], data_size); - i2c_write_blocking(i2c0, addr, data, data_size, false); +static void i2c_recv_fwd(uint16_t addr, const uint8_t * data, size_t data_size) { + if (addr == PB_MOD_ADDR) { + // addressed to me = act as recieved + pb_i2c_recv(data, data_size); + } else { + // addressed to other puzzle module = forward + pb_i2c_send(addr, data, data_size); + } } void recv_handler(struct netconn* conn, struct netbuf* buf) { - // i2ctcp_read_reset(&recv_msg); + i2ctcp_read_reset(&recv_msg); do { char* data; uint16_t len; netbuf_data(buf, (void**)&data, &len); - printf("now scanning the bus!!!!\n"); - scan_bus(found); - // continue early if more data is needed to complete message - // if (i2ctcp_read(&recv_msg, data, len) > 0) continue; + if (i2ctcp_read(&recv_msg, data, len) != 0) continue; // forward received message to puzzle bus - // i2c_recv(recv_msg.addr, recv_msg.data, recv_msg.length); - // free(recv_msg.data); + i2c_recv_fwd(recv_msg.addr, (uint8_t *) recv_msg.data, recv_msg.length); + free(recv_msg.data); } while (netbuf_next(buf) >= 0); netbuf_delete(buf); diff --git a/main/sock.h b/main/sock.h index 61828fb..38fca01 100644 --- a/main/sock.h +++ b/main/sock.h @@ -6,6 +6,3 @@ //! start listening for TCP socket requests void serve_task(); -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/puzzle/dummy/CMakeLists.txt b/puzzle/dummy/CMakeLists.txt index c485e74..6acc4c8 100644 --- a/puzzle/dummy/CMakeLists.txt +++ b/puzzle/dummy/CMakeLists.txt @@ -7,10 +7,19 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS 1) # enable debug features set(CMAKE_BUILD_TYPE Debug) add_compile_definitions(DEBUG) +# add_compile_options(-O0) # no optimizations # arduino set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/lib/Arduino-CMake-Toolchain/Arduino-toolchain.cmake) -set(ARDUINO_BOARD "Arduino Uno [avr.uno]") +# set(ARDUINO_BOARD "Arduino Uno [avr.uno]") +set(ARDUINO_BOARD "Arduino Mega or Mega 2560 [avr.mega]") + +# freertos +add_library(freertos_config INTERFACE) +target_include_directories(freertos_config SYSTEM INTERFACE .) +# set(FREERTOS_PORT GCC_ATMEGA) # Arduino Uno +set(FREERTOS_PORT GCC_ATMEGA) # Arduino Uno +set(FREERTOS_HEAP 4) # used for testing # set(ARDUINO_BOARD "Raspberry Pi Pico W [rp2040.rpipicow]") @@ -20,9 +29,11 @@ set(ARDUINO_BOARD "Arduino Uno [avr.uno]") project(pb_mod_dummy C CXX) add_subdirectory(lib/pbdrv) +add_subdirectory(lib/FreeRTOS-Kernel) add_executable(main main.cpp + mod.c ) target_link_libraries(main diff --git a/puzzle/dummy/FreeRTOSConfig.h b/puzzle/dummy/FreeRTOSConfig.h new file mode 100644 index 0000000..1cfdd71 --- /dev/null +++ b/puzzle/dummy/FreeRTOSConfig.h @@ -0,0 +1,52 @@ +#pragma once + +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ((configSTACK_DEPTH_TYPE) 192) +#define configUSE_16_BIT_TICKS 1 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 0 +#define configUSE_TIME_SLICING 1 +#define configSTACK_DEPTH_TYPE uint16_t +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +// #define configTOTAL_HEAP_SIZE (1024) +#define configTOTAL_HEAP_SIZE (7 * 1024) +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 0 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 92 + +#include <assert.h> +#define configASSERT(x) assert(x) + +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + diff --git a/puzzle/dummy/main.cpp b/puzzle/dummy/main.cpp index 3d84679..d611014 100644 --- a/puzzle/dummy/main.cpp +++ b/puzzle/dummy/main.cpp @@ -1,36 +1,5 @@ #include <Arduino.h> -#include <Wire.h> -#include "drv/arduino/mod.h" -#include "pb-mod.h" - -#ifdef TEST_A -#define ADDR_RX 0x69 -#define ADDR_TX 0x20 -#define MSG "aa" -#define MSG_SIZE 3 -#define MSG_DELAY 10 -#endif - -#ifdef TEST_B -#define ADDR_TX 0x69 -#define ADDR_RX 0x20 -#define MSG "bbbbbbbb" -#define MSG_SIZE 9 -#define MSG_DELAY 10 -#endif - -const char * PBDRV_MOD_NAME = "dummy"; -const i2c_addr_t PBDRV_MOD_ADDR = ADDR_RX; - -void setup() { - pbdrv_setup(); -} - -void loop() { - pbdrv_i2c_send(ADDR_TX, (uint8_t *) MSG, MSG_SIZE); - delay(MSG_DELAY); -} - -void pbdrv_i2c_recv(const uint8_t * data, size_t size) { } +void setup() { } +void loop() { } diff --git a/puzzle/dummy/mod.c b/puzzle/dummy/mod.c new file mode 100644 index 0000000..058a585 --- /dev/null +++ b/puzzle/dummy/mod.c @@ -0,0 +1,6 @@ +#include "pb.h" +#include "pb-mod.h" + +const char * PB_MOD_NAME = "dummy"; +const i2c_addr_t PB_MOD_ADDR = PB_ADDR_MOD_DUMMY; + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d5d6e0d..1ec0cc6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,21 +4,26 @@ set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) +set(CMAKE_BUILD_TYPE Debug) + project(pbtest C CXX ASM) add_executable(test - i2ctcp/main.cpp - pbdrv/main.cpp + # i2ctcp/main.cpp + pbdrv/pb-route.c + pbdrv/msg.cpp + pbdrv/mod.c ) add_subdirectory(lib/googletest) add_subdirectory(lib/pbdrv) +include(lib/pbdrv/ext/stdlib/include.cmake) add_subdirectory(lib/i2ctcp) target_link_libraries(test gtest_main i2ctcp mpack - pbdrv + pbdrv-mod ) diff --git a/test/pbdrv/main.cpp b/test/pbdrv/main.cpp deleted file mode 100644 index de7e88a..0000000 --- a/test/pbdrv/main.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include <gtest/gtest.h> - -#include "pb-write.h" - -TEST(pbdrv, write) { - - pbdrv_buf_t buf = pbdrv_write_cmd_req_set_state({ - .header = { .sender = 0xf0, }, - .state = PB_GS_PLAYING, - }); - for (size_t i = 0; i < buf.size; i++) { - printf("%02x ", buf.data[i] & 0xff); - } - printf("\n"); - - ASSERT_TRUE(true); -} - diff --git a/test/pbdrv/mod.c b/test/pbdrv/mod.c new file mode 100644 index 0000000..5c4c95e --- /dev/null +++ b/test/pbdrv/mod.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +#include "pb-mod.h" +#include "pb-types.h" + +const char * PB_MOD_NAME = "test"; +const i2c_addr_t PB_MOD_ADDR = 0x08; + +void pb_i2c_send(i2c_addr_t addr, const uint8_t * buf, size_t sz) { + printf("[0x%02x]: buf[%lu]\n", addr & 0x7f, sz); +} + diff --git a/test/pbdrv/msg.cpp b/test/pbdrv/msg.cpp new file mode 100644 index 0000000..ca23bc7 --- /dev/null +++ b/test/pbdrv/msg.cpp @@ -0,0 +1,56 @@ +#include <gtest/gtest.h> + +#include "pb-msg.h" +#include "pb-send.h" +#include "pb-mod.h" + +TEST(pb_msg_rw, cmd_req_read) { + pb_cmd_prop_t cmd = { + .propid = 2, + }; + pb_msg_t msg_write = { + .type = PB_CMD_PROP, + .action = PB_ACTION_REQ, + .sender = 0xff, + .cmd = &cmd, + }; + pb_buf_t buf = pb_msg_write(&msg_write); + + ASSERT_NE(buf.data, nullptr); + ASSERT_GE(buf.size, 0); + + pb_msg_t * msg_read = pb_msg_read(&buf); + pb_buf_free(&buf); + + ASSERT_EQ(buf.data, nullptr); + + EXPECT_EQ(msg_write.type, msg_read->type); + EXPECT_EQ(msg_write.sender, msg_read->sender); + EXPECT_NE(nullptr, msg_read->cmd); + pb_cmd_prop_t * prop = (pb_cmd_prop_t *) msg_read->cmd; + EXPECT_EQ(prop->propid, cmd.propid); + + pb_msg_free(msg_read); +} + +TEST(pb_msg_rw, cmd_req_magic) { + pb_buf_t buf = pb_send_magic_req(); + + ASSERT_NE(buf.data, nullptr); + ASSERT_GE(buf.size, 0); + + pb_msg_t * msg_read = pb_msg_read(&buf); + pb_buf_free(&buf); + + ASSERT_EQ(buf.data, nullptr); + + EXPECT_EQ(msg_read->type, PB_CMD_MAGIC); + EXPECT_EQ(msg_read->sender, PB_MOD_ADDR); + EXPECT_NE(msg_read->cmd, nullptr); + pb_cmd_magic_t * magic = (pb_cmd_magic_t *) msg_read->cmd; + EXPECT_EQ(magic->_magic_size, sizeof(pb_cmd_magic_req)); + EXPECT_EQ(0, memcmp(pb_cmd_magic_req, magic->magic, magic->_magic_size)); + + pb_msg_free(msg_read); +} + diff --git a/test/pbdrv/msg.h b/test/pbdrv/msg.h new file mode 100644 index 0000000..52bee37 --- /dev/null +++ b/test/pbdrv/msg.h @@ -0,0 +1,32 @@ +#pragma once + +#include "pb-types.h" + +static const pb_cmd_req_read_t expected_req_read = { + .propid = 0, +}; + +static const pb_cmd_res_read_t expected_res_read = { + .propid = 0, + .value = (uint8_t[]) { 0x00, }, + ._value_size = 1, +}; + +static const pb_cmd_req_write_t expected_req_write = { + .propid = 0, + .value = (uint8_t[]) { 0x00, }, + ._value_size = 1, +}; + +static const pb_cmd_req_state_t expected_req_state = { + .state = PB_GS_PLAYING, +}; + +static const pb_cmd_res_state_t expected_res_state = { + .state = PB_GS_IDLE, +}; + +static const pb_cmd_req_set_state_t expected_req_set_state ={ + .state = PB_GS_PLAYING, +}; + diff --git a/test/pbdrv/pb-route.c b/test/pbdrv/pb-route.c new file mode 100644 index 0000000..62a4b85 --- /dev/null +++ b/test/pbdrv/pb-route.c @@ -0,0 +1,2 @@ +void pb_mod_blocking_delay_ms(unsigned long ms) { } + |