diff --git a/lib/pbio/include/pbsys/program_load.h b/lib/pbio/include/pbsys/program_load.h index 3f2ceaae4..35d987c60 100644 --- a/lib/pbio/include/pbsys/program_load.h +++ b/lib/pbio/include/pbsys/program_load.h @@ -53,10 +53,24 @@ typedef struct _pbsys_program_load_data_header_t { #define PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE (PBSYS_CONFIG_PROGRAM_LOAD_ROM_SIZE - sizeof(pbsys_program_load_data_header_t)) +pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size); + +pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size); + #else #define PBSYS_PROGRAM_LOAD_MAX_PROGRAM_SIZE (0) +static inline pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size) { + return PBIO_ERROR_NOT_SUPPORTED; +} + +static inline pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size) { + *data = NULL; + return PBIO_ERROR_NOT_SUPPORTED; +} + + #endif // PBSYS_CONFIG_PROGRAM_LOAD #endif // _PBSYS_PROGRAM_LOAD_H_ diff --git a/lib/pbio/sys/program_load.c b/lib/pbio/sys/program_load.c index d673f5f07..1f6e3d868 100644 --- a/lib/pbio/sys/program_load.c +++ b/lib/pbio/sys/program_load.c @@ -46,6 +46,42 @@ static void update_write_size(void) { map->header.write_size = sizeof(pbsys_program_load_data_header_t) + map->header.program_size; } +/** + * Saves user data. This will be saved during power off, like program data. + * + * @param [in] offset Offset from the base address. + * @param [in] data The data to be stored (copied). + * @param [in] size Data size. + * @returns ::PBIO_ERROR_INVALID_ARG if the data won't fit. + * Otherwise, ::PBIO_SUCCESS. + */ +pbio_error_t pbsys_program_load_set_user_data(uint32_t offset, const uint8_t *data, uint32_t size) { + if (offset + size > sizeof(map->header.user_data)) { + return PBIO_ERROR_INVALID_ARG; + } + // Update data and write size to request write on poweroff. + memcpy(map->header.user_data + offset, data, size); + update_write_size(); + return PBIO_SUCCESS; +} + +/** + * Gets pointer to user data. + * + * @param [in] offset Offset from the base address. + * @param [in] data The data reference. + * @param [in] size Data size. + * @returns ::PBIO_ERROR_INVALID_ARG if reading out of range. + * Otherwise, ::PBIO_SUCCESS. + */ +pbio_error_t pbsys_program_load_get_user_data(uint32_t offset, uint8_t **data, uint32_t size) { + if (offset + size > sizeof(map->header.user_data)) { + return PBIO_ERROR_INVALID_ARG; + } + *data = map->header.user_data + offset; + return PBIO_SUCCESS; +} + static bool pbsys_program_load_start_user_program_requested; static bool pbsys_program_load_start_repl_requested; diff --git a/pybricks/common/pb_type_system.c b/pybricks/common/pb_type_system.c index cb8908a50..b90a35ab9 100644 --- a/pybricks/common/pb_type_system.c +++ b/pybricks/common/pb_type_system.c @@ -8,12 +8,16 @@ #include #include +#include #include "py/obj.h" +#include "py/objstr.h" #include "py/runtime.h" #include #include +#include +#include STATIC mp_obj_t pb_type_System_name(void) { const char *hub_name = pbdrv_bluetooth_get_hub_name(); @@ -101,6 +105,34 @@ STATIC mp_obj_t pb_type_System_shutdown(void) { } STATIC MP_DEFINE_CONST_FUN_OBJ_0(pb_type_System_shutdown_obj, pb_type_System_shutdown); +STATIC mp_obj_t pb_type_System_storage(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + PB_PARSE_ARGS_FUNCTION(n_args, pos_args, kw_args, + PB_ARG_REQUIRED(offset), + PB_ARG_DEFAULT_NONE(read), + PB_ARG_DEFAULT_NONE(write)); + + // Get offset and confirm integer type. + mp_int_t offset = mp_obj_get_int(offset_in); + + // Handle read. + if (write_in == mp_const_none) { + byte *data; + mp_uint_t size = mp_obj_get_int(read_in); + pb_assert(pbsys_program_load_get_user_data(offset, &data, size)); + return mp_obj_new_bytes(data, size); + } + + // Handle write. + if (read_in == mp_const_none && !mp_obj_is_str(write_in) && mp_obj_is_str_or_bytes(write_in)) { + mp_obj_str_t *obj = ((mp_obj_str_t *)MP_OBJ_TO_PTR(write_in)); + pbsys_program_load_set_user_data(offset, obj->data, obj->len); + return mp_const_none; + } + + mp_raise_ValueError(MP_ERROR_TEXT("Must set either read (int) or write (bytes).")); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pb_type_System_storage_obj, 0, pb_type_System_storage); + #endif // PBIO_CONFIG_ENABLE_SYS // dir(pybricks.common.System) @@ -113,6 +145,7 @@ STATIC const mp_rom_map_elem_t common_System_locals_dict_table[] = { #if PBIO_CONFIG_ENABLE_SYS { MP_ROM_QSTR(MP_QSTR_set_stop_button), MP_ROM_PTR(&pb_type_System_set_stop_button_obj) }, { MP_ROM_QSTR(MP_QSTR_shutdown), MP_ROM_PTR(&pb_type_System_shutdown_obj) }, + { MP_ROM_QSTR(MP_QSTR_storage), MP_ROM_PTR(&pb_type_System_storage_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(common_System_locals_dict, common_System_locals_dict_table);