diff --git a/LICENSE b/LICENSE index 8c5e4fe4c3..3193eb8cec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2020 Damien P. George +Copyright (c) 2013-2021 Damien P. George Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/library/binascii.rst b/docs/library/binascii.rst index e4878a6faa..6a59e9135d 100644 --- a/docs/library/binascii.rst +++ b/docs/library/binascii.rst @@ -14,13 +14,11 @@ Functions .. function:: hexlify(data, [sep]) - Convert binary data to hexadecimal representation. Returns bytes string. + Convert the bytes in the *data* object to a hexadecimal representation. + Returns a bytes object. - .. admonition:: Difference to CPython - :class: attention - - If additional argument, *sep* is supplied, it is used as a separator - between hexadecimal values. + If the additional argument *sep* is supplied it is used as a separator + between hexadecimal values. .. function:: unhexlify(data) diff --git a/docs/library/btree.rst b/docs/library/btree.rst index 0dafe3f866..66a3c0e34b 100644 --- a/docs/library/btree.rst +++ b/docs/library/btree.rst @@ -120,7 +120,7 @@ Methods .. method:: btree.__getitem__(key) btree.get(key, default=None, /) btree.__setitem__(key, val) - btree.__detitem__(key) + btree.__delitem__(key) btree.__contains__(key) Standard dictionary methods. diff --git a/docs/library/sys.rst b/docs/library/sys.rst index e37f722450..aad01edbb2 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -1,5 +1,5 @@ :mod:`sys` -- system specific functions -======================================= +======================================== .. include:: ../templates/unsupported_in_circuitpython.inc diff --git a/examples/usercmodule/cexample/examplemodule.c b/examples/usercmodule/cexample/examplemodule.c new file mode 100644 index 0000000000..f608823c9e --- /dev/null +++ b/examples/usercmodule/cexample/examplemodule.c @@ -0,0 +1,34 @@ +// Include MicroPython API. +#include "py/runtime.h" + +// This is the function which will be called from Python as cexample.add_ints(a, b). +STATIC mp_obj_t example_add_ints(mp_obj_t a_obj, mp_obj_t b_obj) { + // Extract the ints from the micropython input objects. + int a = mp_obj_get_int(a_obj); + int b = mp_obj_get_int(b_obj); + + // Calculate the addition and convert to MicroPython object. + return mp_obj_new_int(a + b); +} +// Define a Python reference to the function above. +STATIC MP_DEFINE_CONST_FUN_OBJ_2(example_add_ints_obj, example_add_ints); + +// Define all properties of the module. +// Table entries are key/value pairs of the attribute name (a string) +// and the MicroPython object reference. +// All identifiers and strings are written as MP_QSTR_xxx and will be +// optimized to word-sized integers by the build system (interned strings). +STATIC const mp_rom_map_elem_t example_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cexample) }, + { MP_ROM_QSTR(MP_QSTR_add_ints), MP_ROM_PTR(&example_add_ints_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(example_module_globals, example_module_globals_table); + +// Define module object. +const mp_obj_module_t example_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&example_module_globals, +}; + +// Register the module to make it available in Python. +MP_REGISTER_MODULE(MP_QSTR_cexample, example_user_cmodule, MODULE_CEXAMPLE_ENABLED); diff --git a/examples/usercmodule/cexample/micropython.mk b/examples/usercmodule/cexample/micropython.mk new file mode 100644 index 0000000000..dbfe3c5cbd --- /dev/null +++ b/examples/usercmodule/cexample/micropython.mk @@ -0,0 +1,9 @@ +EXAMPLE_MOD_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD += $(EXAMPLE_MOD_DIR)/examplemodule.c + +# We can add our module folder to include paths if needed +# This is not actually needed in this example. +CFLAGS_USERMOD += -I$(EXAMPLE_MOD_DIR) +CEXAMPLE_MOD_DIR := $(USERMOD_DIR) diff --git a/examples/usercmodule/cppexample/example.cpp b/examples/usercmodule/cppexample/example.cpp new file mode 100644 index 0000000000..06809732a4 --- /dev/null +++ b/examples/usercmodule/cppexample/example.cpp @@ -0,0 +1,17 @@ +extern "C" { +#include + +// Here we implement the function using C++ code, but since it's +// declaration has to be compatible with C everything goes in extern "C" scope. +mp_obj_t cppfunc(mp_obj_t a_obj, mp_obj_t b_obj) { + // Prove we have (at least) C++11 features. + const auto a = mp_obj_get_int(a_obj); + const auto b = mp_obj_get_int(b_obj); + const auto sum = [&]() { + return mp_obj_new_int(a + b); + } (); + // Prove we're being scanned for QSTRs. + mp_obj_t tup[] = {sum, MP_ROM_QSTR(MP_QSTR_hellocpp)}; + return mp_obj_new_tuple(2, tup); +} +} diff --git a/examples/usercmodule/cppexample/examplemodule.c b/examples/usercmodule/cppexample/examplemodule.c new file mode 100644 index 0000000000..ceb588bef6 --- /dev/null +++ b/examples/usercmodule/cppexample/examplemodule.c @@ -0,0 +1,25 @@ +#include + +// Define a Python reference to the function we'll make available. +// See example.cpp for the definition. +STATIC MP_DEFINE_CONST_FUN_OBJ_2(cppfunc_obj, cppfunc); + +// Define all properties of the module. +// Table entries are key/value pairs of the attribute name (a string) +// and the MicroPython object reference. +// All identifiers and strings are written as MP_QSTR_xxx and will be +// optimized to word-sized integers by the build system (interned strings). +STATIC const mp_rom_map_elem_t cppexample_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cppexample) }, + { MP_ROM_QSTR(MP_QSTR_cppfunc), MP_ROM_PTR(&cppfunc_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(cppexample_module_globals, cppexample_module_globals_table); + +// Define module object. +const mp_obj_module_t cppexample_user_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&cppexample_module_globals, +}; + +// Register the module to make it available in Python. +MP_REGISTER_MODULE(MP_QSTR_cppexample, cppexample_user_cmodule, MODULE_CPPEXAMPLE_ENABLED); diff --git a/examples/usercmodule/cppexample/examplemodule.h b/examples/usercmodule/cppexample/examplemodule.h new file mode 100644 index 0000000000..d89384a630 --- /dev/null +++ b/examples/usercmodule/cppexample/examplemodule.h @@ -0,0 +1,5 @@ +// Include MicroPython API. +#include "py/runtime.h" + +// Declare the function we'll make available in Python as cppexample.cppfunc(). +extern mp_obj_t cppfunc(mp_obj_t a_obj, mp_obj_t b_obj); diff --git a/examples/usercmodule/cppexample/micropython.mk b/examples/usercmodule/cppexample/micropython.mk new file mode 100644 index 0000000000..e10d965a00 --- /dev/null +++ b/examples/usercmodule/cppexample/micropython.mk @@ -0,0 +1,12 @@ +CPPEXAMPLE_MOD_DIR := $(USERMOD_DIR) + +# Add our source files to the respective variables. +SRC_USERMOD += $(CPPEXAMPLE_MOD_DIR)/examplemodule.c +SRC_USERMOD_CXX += $(CPPEXAMPLE_MOD_DIR)/example.cpp + +# Add our module directory to the include path. +CFLAGS_USERMOD += -I$(CPPEXAMPLE_MOD_DIR) +CXXFLAGS_USERMOD += -I$(CPPEXAMPLE_MOD_DIR) + +# We use C++ features so have to link against the standard library. +LDFLAGS_USERMOD += -lstdc++ diff --git a/extmod/btstack/btstack.mk b/extmod/btstack/btstack.mk deleted file mode 100644 index dd96e63379..0000000000 --- a/extmod/btstack/btstack.mk +++ /dev/null @@ -1,57 +0,0 @@ -# Makefile directives for BlueKitchen BTstack - -ifeq ($(MICROPY_BLUETOOTH_BTSTACK),1) - -MICROPY_BLUETOOTH_BTSTACK_USB ?= 0 - -BTSTACK_EXTMOD_DIR = extmod/btstack - -EXTMOD_SRC_C += extmod/btstack/modbluetooth_btstack.c - -INC += -I$(TOP)/$(BTSTACK_EXTMOD_DIR) - -CFLAGS_MOD += -DMICROPY_BLUETOOTH_BTSTACK=1 - -BTSTACK_DIR = $(TOP)/lib/btstack - -ifneq ($(wildcard $(BTSTACK_DIR)/src),) - -include $(BTSTACK_DIR)/src/Makefile.inc -include $(BTSTACK_DIR)/src/ble/Makefile.inc - -INC += -I$(BTSTACK_DIR)/src -INC += -I$(BTSTACK_DIR)/3rd-party/bluedroid/decoder/include -INC += -I$(BTSTACK_DIR)/3rd-party/bluedroid/encoder/include -INC += -I$(BTSTACK_DIR)/3rd-party/md5 -INC += -I$(BTSTACK_DIR)/3rd-party/yxml - -SRC_BTSTACK = \ - $(addprefix lib/btstack/src/, $(SRC_FILES)) \ - $(addprefix lib/btstack/src/ble/, $(filter-out %_tlv.c, $(SRC_BLE_FILES))) \ - lib/btstack/platform/embedded/btstack_run_loop_embedded.c - -ifeq ($(MICROPY_BLUETOOTH_BTSTACK_USB),1) -SRC_BTSTACK += \ - lib/btstack/platform/libusb/hci_transport_h2_libusb.c - -CFLAGS += $(shell pkg-config libusb-1.0 --cflags) -LDFLAGS += $(shell pkg-config libusb-1.0 --libs) -endif - -ifeq ($(MICROPY_BLUETOOTH_BTSTACK_ENABLE_CLASSIC),1) -include $(BTSTACK_DIR)/src/classic/Makefile.inc -SRC_BTSTACK += \ - $(addprefix lib/btstack/src/classic/, $(SRC_CLASSIC_FILES)) -endif - -LIB_SRC_C += $(SRC_BTSTACK) - -# Suppress some warnings. -BTSTACK_WARNING_CFLAGS = -Wno-old-style-definition -Wno-unused-variable -Wno-unused-parameter -ifneq ($(CC),clang) -BTSTACK_WARNING_CFLAGS += -Wno-format -endif -$(BUILD)/lib/btstack/src/%.o: CFLAGS += $(BTSTACK_WARNING_CFLAGS) - -endif -endif diff --git a/extmod/btstack/btstack_config.h b/extmod/btstack/btstack_config.h deleted file mode 100644 index f420f47a5b..0000000000 --- a/extmod/btstack/btstack_config.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H -#define MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H - -// BTstack features that can be enabled -#define ENABLE_BLE -#define ENABLE_LE_PERIPHERAL -#define ENABLE_LE_CENTRAL -// #define ENABLE_CLASSIC -#define ENABLE_LE_DATA_CHANNELS -// #define ENABLE_LOG_INFO -#define ENABLE_LOG_ERROR - -// BTstack configuration. buffers, sizes, ... -#define HCI_ACL_PAYLOAD_SIZE 1021 -#define MAX_NR_GATT_CLIENTS 1 -#define MAX_NR_HCI_CONNECTIONS 1 -#define MAX_NR_L2CAP_SERVICES 3 -#define MAX_NR_L2CAP_CHANNELS 3 -#define MAX_NR_RFCOMM_MULTIPLEXERS 1 -#define MAX_NR_RFCOMM_SERVICES 1 -#define MAX_NR_RFCOMM_CHANNELS 1 -#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2 -#define MAX_NR_BNEP_SERVICES 1 -#define MAX_NR_BNEP_CHANNELS 1 -#define MAX_NR_HFP_CONNECTIONS 1 -#define MAX_NR_WHITELIST_ENTRIES 1 -#define MAX_NR_SM_LOOKUP_ENTRIES 3 -#define MAX_NR_SERVICE_RECORD_ITEMS 1 -#define MAX_NR_AVDTP_STREAM_ENDPOINTS 1 -#define MAX_NR_AVDTP_CONNECTIONS 1 -#define MAX_NR_AVRCP_CONNECTIONS 1 - -#define MAX_NR_LE_DEVICE_DB_ENTRIES 4 - -// Link Key DB and LE Device DB using TLV on top of Flash Sector interface -// #define NVM_NUM_DEVICE_DB_ENTRIES 16 - -// We don't give btstack a malloc, so use a fixed-size ATT DB. -#define MAX_ATT_DB_SIZE 512 - -// BTstack HAL configuration -#define HAVE_EMBEDDED_TIME_MS - -// Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A). -#define HCI_RESET_RESEND_TIMEOUT_MS 1000 - -#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_BTSTACK_CONFIG_H diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c deleted file mode 100644 index 8f0c82974c..0000000000 --- a/extmod/btstack/modbluetooth_btstack.c +++ /dev/null @@ -1,1047 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2020 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/runtime.h" -#include "py/mperrno.h" -#include "py/mphal.h" - -#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK - -#include "extmod/btstack/modbluetooth_btstack.h" -#include "extmod/modbluetooth.h" - -#include "lib/btstack/src/btstack.h" - -#define DEBUG_EVENT_printf(...) // printf(__VA_ARGS__) - -#ifndef MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME -#define MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME "MPY BTSTACK" -#endif - -// How long to wait for a controller to init/deinit. -// Some controllers can take up to 5-6 seconds in normal operation. -STATIC const uint32_t BTSTACK_INIT_DEINIT_TIMEOUT_MS = 15000; - -// We need to know the attribute handle for the GAP device name (see GAP_DEVICE_NAME_UUID) -// so it can be put into the gatts_db before registering the services, and accessed -// efficiently when requesting an attribute in att_read_callback. Because this is the -// first characteristic of the first service, it always has a handle value of 3. -STATIC const uint16_t BTSTACK_GAP_DEVICE_NAME_HANDLE = 3; - -volatile int mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; - -STATIC int btstack_error_to_errno(int err) { - DEBUG_EVENT_printf(" --> btstack error: %d\n", err); - if (err == ERROR_CODE_SUCCESS) { - return 0; - } else if (err == BTSTACK_ACL_BUFFERS_FULL || err == BTSTACK_MEMORY_ALLOC_FAILED) { - return MP_ENOMEM; - } else if (err == GATT_CLIENT_IN_WRONG_STATE) { - return MP_EALREADY; - } else if (err == GATT_CLIENT_BUSY) { - return MP_EBUSY; - } else if (err == GATT_CLIENT_NOT_CONNECTED) { - return MP_ENOTCONN; - } else { - return MP_EINVAL; - } -} - -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uuid128) { - mp_obj_bluetooth_uuid_t result; - if (uuid16 != 0) { - result.data[0] = uuid16 & 0xff; - result.data[1] = (uuid16 >> 8) & 0xff; - result.type = MP_BLUETOOTH_UUID_TYPE_16; - } else { - reverse_128(uuid128, result.data); - result.type = MP_BLUETOOTH_UUID_TYPE_128; - } - return result; -} -#endif - -// Notes on supporting background ops (e.g. an attempt to gatts_notify while -// an existing notification is in progress): - -// GATTS Notify/Indicate (att_server_notify/indicate) -// * When available, copies buffer immediately. -// * Otherwise fails with BTSTACK_ACL_BUFFERS_FULL -// * Use att_server_request_to_send_notification/indication to get callback -// * Takes btstack_context_callback_registration_t (and takes ownership) and conn_handle. -// * Callback is invoked with just the context member of the btstack_context_callback_registration_t - -// GATTC Write without response (gatt_client_write_value_of_characteristic_without_response) -// * When available, copies buffer immediately. -// * Otherwise, fails with GATT_CLIENT_BUSY. -// * Use gatt_client_request_can_write_without_response_event to get callback -// * Takes btstack_packet_handler_t (function pointer) and conn_handle -// * Callback is invoked, use gatt_event_can_write_without_response_get_handle to get the conn_handle (no other context) -// * There can only be one pending gatt_client_request_can_write_without_response_event (otherwise we fail with EALREADY). - -// GATTC Write with response (gatt_client_write_value_of_characteristic) -// * When peripheral is available, takes ownership of buffer. -// * Otherwise, fails with GATT_CLIENT_IN_WRONG_STATE (we fail the operation). -// * Raises GATT_EVENT_QUERY_COMPLETE to the supplied packet handler. - -// For notify/indicate/write-without-response that proceed immediately, nothing extra required. -// For all other cases, buffer needs to be copied and protected from GC. -// For notify/indicate: -// * btstack_context_callback_registration_t: -// * needs to be malloc'ed -// * needs to be protected from GC -// * context arg needs to point back to the callback registration so it can be freed and un-protected -// For write-without-response -// * only the conn_handle is available in the callback -// * so we need a queue of conn_handle->(value_handle, copied buffer) - -// Pending operation types. -enum { - // Queued for sending when possible. - MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, // Waiting for context callback - MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, // Waiting for context callback - MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, // Waiting for conn handle - // Hold buffer pointer until complete. - MP_BLUETOOTH_BTSTACK_PENDING_WRITE, // Waiting for write done event -}; - -// Pending operation: -// - Holds a GC reference to the copied outgoing buffer. -// - Provides enough information for the callback handler to execute the desired operation. -struct _mp_btstack_pending_op_t { - btstack_linked_item_t *next; // Must be first field to match btstack_linked_item. - - // See enum above. - uint16_t op_type; - - // For all op types. - uint16_t conn_handle; - uint16_t value_handle; - - // For notify/indicate only. - // context_registration.context will point back to this struct. - btstack_context_callback_registration_t context_registration; - - // For notify/indicate/write-without-response, this is the actual buffer to send. - // For write-with-response, just holding onto the buffer for GC ref. - size_t len; - uint8_t buf[]; -}; - -// Must hold MICROPY_PY_BLUETOOTH_ENTER. -STATIC void btstack_remove_pending_operation(mp_btstack_pending_op_t *pending_op, bool del) { - bool removed = btstack_linked_list_remove(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op); - assert(removed); - (void)removed; - if (del) { - m_del_var(mp_btstack_pending_op_t, uint8_t, pending_op->len, pending_op); - } -} - -// Called in response to a gatts_notify/indicate being unable to complete, which then calls -// att_server_request_to_send_notification. -// We now have an opportunity to re-try the operation with an empty ACL buffer. -STATIC void btstack_notify_indicate_ready_handler(void *context) { - MICROPY_PY_BLUETOOTH_ENTER - mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)context; - DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler op_type=%d conn_handle=%d value_handle=%d len=%lu\n", pending_op->op_type, pending_op->conn_handle, pending_op->value_handle, pending_op->len); - if (pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY) { - int err = att_server_notify(pending_op->conn_handle, pending_op->value_handle, pending_op->buf, pending_op->len); - DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler: sending notification err=%d\n", err); - assert(err == ERROR_CODE_SUCCESS); - (void)err; - } else { - assert(pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE); - int err = att_server_indicate(pending_op->conn_handle, pending_op->value_handle, NULL, 0); - DEBUG_EVENT_printf("btstack_notify_indicate_ready_handler: sending indication err=%d\n", err); - assert(err == ERROR_CODE_SUCCESS); - (void)err; - } - // Can't free the pending op as we're in IRQ context. Leave it for the GC. - btstack_remove_pending_operation(pending_op, false /* del */); - MICROPY_PY_BLUETOOTH_EXIT -} - -// Register a pending background operation -- copies the buffer, and makes it known to the GC. -STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, const uint8_t *buf, size_t len) { - DEBUG_EVENT_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%lu\n", op_type, conn_handle, value_handle, len); - mp_btstack_pending_op_t *pending_op = m_new_obj_var(mp_btstack_pending_op_t, uint8_t, len); - pending_op->op_type = op_type; - pending_op->conn_handle = conn_handle; - pending_op->value_handle = value_handle; - pending_op->len = len; - memcpy(pending_op->buf, buf, len); - - if (op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY || op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE) { - pending_op->context_registration.callback = &btstack_notify_indicate_ready_handler; - pending_op->context_registration.context = pending_op; - } - - MICROPY_PY_BLUETOOTH_ENTER - bool added = btstack_linked_list_add(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op); - assert(added); - (void)added; - MICROPY_PY_BLUETOOTH_EXIT - - return pending_op; -} - -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - -// Cleans up a pending op of the specified type for this conn_handle (and if specified, value_handle). -// Used by MP_BLUETOOTH_BTSTACK_PENDING_WRITE and MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE. -// At the moment, both will set value_handle=0xffff as the events do not know their value_handle. -// TODO: Can we make btstack give us the value_handle for regular write (with response) so that we -// know for sure that we're using the correct entry. -STATIC mp_btstack_pending_op_t *btstack_finish_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, bool del) { - MICROPY_PY_BLUETOOTH_ENTER - DEBUG_EVENT_printf("btstack_finish_pending_operation op_type=%d conn_handle=%d value_handle=%d\n", op_type, conn_handle, value_handle); - btstack_linked_list_iterator_t it; - btstack_linked_list_iterator_init(&it, &MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops); - while (btstack_linked_list_iterator_has_next(&it)) { - mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)btstack_linked_list_iterator_next(&it); - - if (pending_op->op_type == op_type && pending_op->conn_handle == conn_handle && (value_handle == 0xffff || pending_op->value_handle == value_handle)) { - DEBUG_EVENT_printf("btstack_finish_pending_operation: found value_handle=%d len=%lu\n", pending_op->value_handle, pending_op->len); - btstack_remove_pending_operation(pending_op, del); - MICROPY_PY_BLUETOOTH_EXIT - return del ? NULL : pending_op; - } - } - DEBUG_EVENT_printf("btstack_finish_pending_operation: not found\n"); - MICROPY_PY_BLUETOOTH_EXIT - return NULL; -} -#endif - -// This needs to be separate to btstack_packet_handler otherwise we get -// dual-delivery of the HCI_EVENT_LE_META event. -STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - DEBUG_EVENT_printf("btstack_packet_handler_att_server(packet_type=%u, packet=%p)\n", packet_type, packet); - if (packet_type != HCI_EVENT_PACKET) { - return; - } - - uint8_t event_type = hci_event_packet_get_type(packet); - - if (event_type == ATT_EVENT_CONNECTED) { - DEBUG_EVENT_printf(" --> att connected\n"); - // The ATT_EVENT_*CONNECTED events are fired for both peripheral and central role, with no way to tell which. - // So we use the HCI_EVENT_LE_META event directly in the main packet handler. - } else if (event_type == ATT_EVENT_DISCONNECTED) { - DEBUG_EVENT_printf(" --> att disconnected\n"); - } else if (event_type == ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE) { - DEBUG_EVENT_printf(" --> att indication complete\n"); - uint16_t conn_handle = att_event_handle_value_indication_complete_get_conn_handle(packet); - uint16_t value_handle = att_event_handle_value_indication_complete_get_attribute_handle(packet); - uint8_t status = att_event_handle_value_indication_complete_get_status(packet); - mp_bluetooth_gatts_on_indicate_complete(conn_handle, value_handle, status); - } else if (event_type == HCI_EVENT_LE_META || event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { - // Ignore, duplicated by att_server.c. - } else { - DEBUG_EVENT_printf(" --> hci att server event type: unknown (0x%02x)\n", event_type); - } -} - -STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq) { - DEBUG_EVENT_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); - if (packet_type != HCI_EVENT_PACKET) { - return; - } - - uint8_t event_type = hci_event_packet_get_type(packet); - - if (event_type == HCI_EVENT_LE_META) { - DEBUG_EVENT_printf(" --> hci le meta\n"); - if (hci_event_le_meta_get_subevent_code(packet) == HCI_SUBEVENT_LE_CONNECTION_COMPLETE) { - uint16_t conn_handle = hci_subevent_le_connection_complete_get_connection_handle(packet); - uint8_t addr_type = hci_subevent_le_connection_complete_get_peer_address_type(packet); - bd_addr_t addr; - hci_subevent_le_connection_complete_get_peer_address(packet, addr); - uint16_t irq_event; - if (hci_subevent_le_connection_complete_get_role(packet) == 0) { - // Master role. - irq_event = MP_BLUETOOTH_IRQ_PERIPHERAL_CONNECT; - } else { - // Slave role. - irq_event = MP_BLUETOOTH_IRQ_CENTRAL_CONNECT; - } - mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, addr_type, addr); - } - } else if (event_type == BTSTACK_EVENT_STATE) { - uint8_t state = btstack_event_state_get_state(packet); - DEBUG_EVENT_printf(" --> btstack event state 0x%02x\n", state); - if (state == HCI_STATE_WORKING) { - // Signal that initialisation has completed. - mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; - } else if (state == HCI_STATE_OFF) { - // Signal that de-initialisation has completed. - mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; - } - } else if (event_type == HCI_EVENT_TRANSPORT_PACKET_SENT) { - DEBUG_EVENT_printf(" --> hci transport packet sent\n"); - } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { - DEBUG_EVENT_printf(" --> hci command complete\n"); - } else if (event_type == HCI_EVENT_COMMAND_STATUS) { - DEBUG_EVENT_printf(" --> hci command status\n"); - } else if (event_type == HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS) { - DEBUG_EVENT_printf(" --> hci number of completed packets\n"); - } else if (event_type == BTSTACK_EVENT_NR_CONNECTIONS_CHANGED) { - DEBUG_EVENT_printf(" --> btstack # conns changed\n"); - } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { - DEBUG_EVENT_printf(" --> hci vendor specific\n"); - } else if (event_type == GATT_EVENT_MTU) { - DEBUG_EVENT_printf(" --> hci MTU\n"); - } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { - DEBUG_EVENT_printf(" --> hci disconnect complete\n"); - uint16_t conn_handle = hci_event_disconnection_complete_get_connection_handle(packet); - const hci_connection_t *conn = hci_connection_for_handle(conn_handle); - uint16_t irq_event; - if (conn == NULL || conn->role == 0) { - // Master role. - irq_event = MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT; - } else { - // Slave role. - irq_event = MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT; - } - uint8_t addr[6] = {0}; - mp_bluetooth_gap_on_connected_disconnected(irq_event, conn_handle, 0xff, addr); - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - } else if (event_type == GAP_EVENT_ADVERTISING_REPORT) { - DEBUG_EVENT_printf(" --> gap advertising report\n"); - bd_addr_t address; - gap_event_advertising_report_get_address(packet, address); - uint8_t adv_event_type = gap_event_advertising_report_get_advertising_event_type(packet); - uint8_t address_type = gap_event_advertising_report_get_address_type(packet); - int8_t rssi = gap_event_advertising_report_get_rssi(packet); - uint8_t length = gap_event_advertising_report_get_data_length(packet); - const uint8_t *data = gap_event_advertising_report_get_data(packet); - mp_bluetooth_gap_on_scan_result(address_type, address, adv_event_type, rssi, data, length); - } else if (event_type == GATT_EVENT_QUERY_COMPLETE) { - uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); - uint16_t status = gatt_event_query_complete_get_att_status(packet); - DEBUG_EVENT_printf(" --> gatt query complete irq=%d conn_handle=%d status=%d\n", irq, conn_handle, status); - if (irq == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || irq == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { - // TODO there is no value_handle available to pass here. - // TODO try and get this implemented in btstack. - mp_bluetooth_gattc_on_read_write_status(irq, conn_handle, 0xffff, status); - // Unref the saved buffer for the write operation on this conn_handle. - if (irq == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { - btstack_finish_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE, conn_handle, 0xffff, false /* del */); - } - } else if (irq == MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE || - irq == MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE || - irq == MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE) { - mp_bluetooth_gattc_on_discover_complete(irq, conn_handle, status); - } - } else if (event_type == GATT_EVENT_SERVICE_QUERY_RESULT) { - DEBUG_EVENT_printf(" --> gatt service query result\n"); - uint16_t conn_handle = gatt_event_service_query_result_get_handle(packet); - gatt_client_service_t service; - gatt_event_service_query_result_get_service(packet, &service); - mp_obj_bluetooth_uuid_t service_uuid = create_mp_uuid(service.uuid16, service.uuid128); - mp_bluetooth_gattc_on_primary_service_result(conn_handle, service.start_group_handle, service.end_group_handle, &service_uuid); - } else if (event_type == GATT_EVENT_CHARACTERISTIC_QUERY_RESULT) { - DEBUG_EVENT_printf(" --> gatt characteristic query result\n"); - uint16_t conn_handle = gatt_event_characteristic_query_result_get_handle(packet); - gatt_client_characteristic_t characteristic; - gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic); - mp_obj_bluetooth_uuid_t characteristic_uuid = create_mp_uuid(characteristic.uuid16, characteristic.uuid128); - mp_bluetooth_gattc_on_characteristic_result(conn_handle, characteristic.start_handle, characteristic.value_handle, characteristic.properties, &characteristic_uuid); - } else if (event_type == GATT_EVENT_CHARACTERISTIC_DESCRIPTOR_QUERY_RESULT) { - DEBUG_EVENT_printf(" --> gatt descriptor query result\n"); - uint16_t conn_handle = gatt_event_all_characteristic_descriptors_query_result_get_handle(packet); - gatt_client_characteristic_descriptor_t descriptor; - gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &descriptor); - mp_obj_bluetooth_uuid_t descriptor_uuid = create_mp_uuid(descriptor.uuid16, descriptor.uuid128); - mp_bluetooth_gattc_on_descriptor_result(conn_handle, descriptor.handle, &descriptor_uuid); - } else if (event_type == GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT) { - DEBUG_EVENT_printf(" --> gatt characteristic value query result\n"); - uint16_t conn_handle = gatt_event_characteristic_value_query_result_get_handle(packet); - uint16_t value_handle = gatt_event_characteristic_value_query_result_get_value_handle(packet); - uint16_t len = gatt_event_characteristic_value_query_result_get_value_length(packet); - const uint8_t *data = gatt_event_characteristic_value_query_result_get_value(packet); - mp_uint_t atomic_state; - len = mp_bluetooth_gattc_on_data_available_start(MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, conn_handle, value_handle, len, &atomic_state); - mp_bluetooth_gattc_on_data_available_chunk(data, len); - mp_bluetooth_gattc_on_data_available_end(atomic_state); - } else if (event_type == GATT_EVENT_NOTIFICATION) { - DEBUG_EVENT_printf(" --> gatt notification\n"); - uint16_t conn_handle = gatt_event_notification_get_handle(packet); - uint16_t value_handle = gatt_event_notification_get_value_handle(packet); - uint16_t len = gatt_event_notification_get_value_length(packet); - const uint8_t *data = gatt_event_notification_get_value(packet); - mp_uint_t atomic_state; - len = mp_bluetooth_gattc_on_data_available_start(MP_BLUETOOTH_IRQ_GATTC_NOTIFY, conn_handle, value_handle, len, &atomic_state); - mp_bluetooth_gattc_on_data_available_chunk(data, len); - mp_bluetooth_gattc_on_data_available_end(atomic_state); - } else if (event_type == GATT_EVENT_INDICATION) { - DEBUG_EVENT_printf(" --> gatt indication\n"); - uint16_t conn_handle = gatt_event_indication_get_handle(packet); - uint16_t value_handle = gatt_event_indication_get_value_handle(packet); - uint16_t len = gatt_event_indication_get_value_length(packet); - const uint8_t *data = gatt_event_indication_get_value(packet); - mp_uint_t atomic_state; - len = mp_bluetooth_gattc_on_data_available_start(MP_BLUETOOTH_IRQ_GATTC_INDICATE, conn_handle, value_handle, len, &atomic_state); - mp_bluetooth_gattc_on_data_available_chunk(data, len); - mp_bluetooth_gattc_on_data_available_end(atomic_state); - } else if (event_type == GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE) { - uint16_t conn_handle = gatt_event_can_write_without_response_get_handle(packet); - DEBUG_EVENT_printf(" --> gatt can write without response %d\n", conn_handle); - mp_btstack_pending_op_t *pending_op = btstack_finish_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, conn_handle, 0xffff, false /* !del */); - if (pending_op) { - DEBUG_EVENT_printf(" --> ready for value_handle=%d len=%lu\n", pending_op->value_handle, pending_op->len); - gatt_client_write_value_of_characteristic_without_response(pending_op->conn_handle, pending_op->value_handle, pending_op->len, (uint8_t *)pending_op->buf); - // Note: Can't "del" the pending_op from IRQ context. Leave it for the GC. - } - - #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - } else { - DEBUG_EVENT_printf(" --> hci event type: unknown (0x%02x)\n", event_type); - } -} - -// Because the packet handler callbacks don't support an argument, we use a specific -// handler when we need to provide additional state to the handler (in the "irq" parameter). -// This is the generic handler for when you don't need extra state. -STATIC void btstack_packet_handler_generic(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - btstack_packet_handler(packet_type, packet, 0); -} - -STATIC btstack_packet_callback_registration_t hci_event_callback_registration = { - .callback = &btstack_packet_handler_generic -}; - -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -// For when the handler is being used for service discovery. -STATIC void btstack_packet_handler_discover_services(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE); -} - -// For when the handler is being used for characteristic discovery. -STATIC void btstack_packet_handler_discover_characteristics(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE); -} - -// For when the handler is being used for descriptor discovery. -STATIC void btstack_packet_handler_discover_descriptors(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE); -} - -// For when the handler is being used for a read query. -STATIC void btstack_packet_handler_read(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_READ_DONE); -} - -// For when the handler is being used for write-with-response. -STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE); -} -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - -STATIC btstack_timer_source_t btstack_init_deinit_timeout; - -STATIC void btstack_init_deinit_timeout_handler(btstack_timer_source_t *ds) { - (void)ds; - - // Stop waiting for initialisation. - // This signals both the loops in mp_bluetooth_init and mp_bluetooth_deinit, - // as well as ports that run a polling loop. - mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT; -} - -int mp_bluetooth_init(void) { - DEBUG_EVENT_printf("mp_bluetooth_init\n"); - - if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { - return 0; - } - - // Clean up if necessary. - mp_bluetooth_deinit(); - - btstack_memory_init(); - - MP_STATE_PORT(bluetooth_btstack_root_pointers) = m_new0(mp_bluetooth_btstack_root_pointers_t, 1); - mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db); - - // Set the default GAP device name. - const char *gap_name = MICROPY_PY_BLUETOOTH_DEFAULT_GAP_NAME; - size_t gap_len = strlen(gap_name); - mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, gap_len); - mp_bluetooth_gap_set_device_name((const uint8_t *)gap_name, gap_len); - - mp_bluetooth_btstack_port_init(); - mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_STARTING; - - l2cap_init(); - le_device_db_init(); - sm_init(); - - // Set blank ER/IR keys to suppress BTstack warning. - // TODO handle this correctly. - sm_key_t dummy_key; - memset(dummy_key, 0, sizeof(dummy_key)); - sm_set_er(dummy_key); - sm_set_ir(dummy_key); - - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - gatt_client_init(); - #endif - - // Register for HCI events. - hci_add_event_handler(&hci_event_callback_registration); - - // Register for ATT server events. - att_server_register_packet_handler(&btstack_packet_handler_att_server); - - // Set a timeout for HCI initialisation. - btstack_run_loop_set_timer(&btstack_init_deinit_timeout, BTSTACK_INIT_DEINIT_TIMEOUT_MS); - btstack_run_loop_set_timer_handler(&btstack_init_deinit_timeout, btstack_init_deinit_timeout_handler); - btstack_run_loop_add_timer(&btstack_init_deinit_timeout); - - // Either the HCI event will set state to ACTIVE, or the timeout will set it to TIMEOUT. - mp_bluetooth_btstack_port_start(); - while (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING) { - MICROPY_EVENT_POLL_HOOK - } - btstack_run_loop_remove_timer(&btstack_init_deinit_timeout); - - // Check for timeout. - if (mp_bluetooth_btstack_state != MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { - // Required to stop the polling loop. - mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; - // Attempt a shutdown (may not do anything). - mp_bluetooth_btstack_port_deinit(); - - // Clean up. - MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL; - return MP_ETIMEDOUT; - } - - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - // Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles. - gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler_generic, GATT_CLIENT_ANY_CONNECTION, NULL); - #endif - - return 0; -} - -void mp_bluetooth_deinit(void) { - DEBUG_EVENT_printf("mp_bluetooth_deinit\n"); - - // Nothing to do if not initialised. - if (!MP_STATE_PORT(bluetooth_btstack_root_pointers)) { - return; - } - - mp_bluetooth_gap_advertise_stop(); - - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - // Remove our registration for notify/indicate. - gatt_client_stop_listening_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification); - #endif - - // Set a timer that will forcibly set the state to TIMEOUT, which will stop the loop below. - btstack_run_loop_set_timer(&btstack_init_deinit_timeout, BTSTACK_INIT_DEINIT_TIMEOUT_MS); - btstack_run_loop_add_timer(&btstack_init_deinit_timeout); - - // This should result in a clean shutdown, which will set the state to OFF. - // On Unix this is blocking (it joins on the poll thread), on other ports the loop below will wait unil - // either timeout or clean shutdown. - mp_bluetooth_btstack_port_deinit(); - while (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { - MICROPY_EVENT_POLL_HOOK - } - btstack_run_loop_remove_timer(&btstack_init_deinit_timeout); - - mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_OFF; - MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL; -} - -bool mp_bluetooth_is_active(void) { - return mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; -} - -void mp_bluetooth_get_device_addr(uint8_t *addr) { - mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr); -} - -size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { - uint8_t *value = NULL; - size_t value_len = 0; - mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, &value, &value_len); - *buf = value; - return value_len; -} - -int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) { - return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, buf, len); -} - -int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len) { - DEBUG_EVENT_printf("mp_bluetooth_gap_advertise_start\n"); - uint16_t adv_int_min = interval_us / 625; - uint16_t adv_int_max = interval_us / 625; - uint8_t adv_type = connectable ? 0 : 2; - bd_addr_t null_addr = {0}; - - uint8_t direct_address_type = 0; - uint8_t channel_map = 0x07; // Use all three broadcast channels. - uint8_t filter_policy = 0x00; // None. - - gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, direct_address_type, null_addr, channel_map, filter_policy); - - // Copy the adv_data and sr_data into a persistent buffer (which is findable via the btstack root pointers). - size_t total_bytes = adv_data_len + sr_data_len; - if (total_bytes > MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc) { - // Resize if necessary. - MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = m_new(uint8_t, total_bytes); - MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = total_bytes; - } - uint8_t *data = MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data; - - if (adv_data) { - memcpy(data, (uint8_t *)adv_data, adv_data_len); - gap_advertisements_set_data(adv_data_len, data); - data += adv_data_len; - } - if (sr_data) { - memcpy(data, (uint8_t *)sr_data, sr_data_len); - gap_scan_response_set_data(sr_data_len, data); - } - - gap_advertisements_enable(true); - return 0; -} - -void mp_bluetooth_gap_advertise_stop(void) { - DEBUG_EVENT_printf("mp_bluetooth_gap_advertise_stop\n"); - gap_advertisements_enable(false); - MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data_alloc = 0; - MP_STATE_PORT(bluetooth_btstack_root_pointers)->adv_data = NULL; -} - -int mp_bluetooth_gatts_register_service_begin(bool append) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service_begin\n"); - if (!append) { - // This will reset the DB. - // Becase the DB is statically allocated, there's no problem with just re-initing it. - // Note this would be a memory leak if we enabled HAVE_MALLOC (there's no API to free the existing db). - att_db_util_init(); - - att_db_util_add_service_uuid16(GAP_SERVICE_UUID); - uint16_t handle = att_db_util_add_characteristic_uuid16(GAP_DEVICE_NAME_UUID, ATT_PROPERTY_READ | ATT_PROPERTY_DYNAMIC, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0); - assert(handle == BTSTACK_GAP_DEVICE_NAME_HANDLE); - (void)handle; - - att_db_util_add_service_uuid16(0x1801); - att_db_util_add_characteristic_uuid16(0x2a05, ATT_PROPERTY_READ, ATT_SECURITY_NONE, ATT_SECURITY_NONE, NULL, 0); - } - - return 0; -} - -STATIC uint16_t att_read_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { - (void)connection_handle; - DEBUG_EVENT_printf("btstack: att_read_callback (handle: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, offset, buffer, buffer_size); - mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, att_handle); - if (!entry) { - DEBUG_EVENT_printf("btstack: att_read_callback handle not found\n"); - return 0; // TODO: Find status code for not-found. - } - - return att_read_callback_handle_blob(entry->data, entry->data_len, offset, buffer, buffer_size); -} - -STATIC int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size) { - (void)offset; - (void)transaction_mode; - DEBUG_EVENT_printf("btstack: att_write_callback (handle: %u, mode: %u, offset: %u, buffer: %p, size: %u)\n", att_handle, transaction_mode, offset, buffer, buffer_size); - mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, att_handle); - if (!entry) { - DEBUG_EVENT_printf("btstack: att_write_callback handle not found\n"); - return 0; // TODO: Find status code for not-found. - } - - // TODO: Use `offset` arg. - size_t append_offset = 0; - if (entry->append) { - append_offset = entry->data_len; - } - entry->data_len = MIN(entry->data_alloc, buffer_size + append_offset); - memcpy(entry->data + append_offset, buffer, entry->data_len - append_offset); - - mp_bluetooth_gatts_on_write(connection_handle, att_handle); - - return 0; -} - -STATIC inline uint16_t get_uuid16(const mp_obj_bluetooth_uuid_t *uuid) { - return (uuid->data[1] << 8) | uuid->data[0]; -} - -int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint8_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint8_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service\n"); - // Note: btstack expects BE UUIDs (which it immediately convertes to LE). - // So we have to convert all our modbluetooth LE UUIDs to BE just for the att_db_util_add_* methods (using get_uuid16 above, and reverse_128 from btstackutil.h). - - // TODO: btstack's att_db_util_add_* methods have no bounds checking or validation. - // Need some way to prevent additional services being added if we're out of space in the static buffer. - - if (service_uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { - att_db_util_add_service_uuid16(get_uuid16(service_uuid)); - } else if (service_uuid->type == MP_BLUETOOTH_UUID_TYPE_128) { - uint8_t buffer[16]; - reverse_128(service_uuid->data, buffer); - att_db_util_add_service_uuid128(buffer); - } else { - return MP_EINVAL; - } - - size_t handle_index = 0; - size_t descriptor_index = 0; - static uint8_t cccb_buf[2] = {0}; - - for (size_t i = 0; i < num_characteristics; ++i) { - uint16_t props = characteristic_flags[i] | ATT_PROPERTY_DYNAMIC; - uint16_t read_permission = ATT_SECURITY_NONE; - uint16_t write_permission = ATT_SECURITY_NONE; - if (characteristic_uuids[i]->type == MP_BLUETOOTH_UUID_TYPE_16) { - handles[handle_index] = att_db_util_add_characteristic_uuid16(get_uuid16(characteristic_uuids[i]), props, read_permission, write_permission, NULL, 0); - } else if (characteristic_uuids[i]->type == MP_BLUETOOTH_UUID_TYPE_128) { - uint8_t buffer[16]; - reverse_128(characteristic_uuids[i]->data, buffer); - handles[handle_index] = att_db_util_add_characteristic_uuid128(buffer, props, read_permission, write_permission, NULL, 0); - } else { - return MP_EINVAL; - } - mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index], MP_BLUETOOTH_DEFAULT_ATTR_LEN); - // If a NOTIFY or INDICATE characteristic is added, then we need to manage a value for the CCCB. - if (props & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)) { - // btstack creates the CCCB as the next handle. - mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index] + 1, MP_BLUETOOTH_CCCB_LEN); - int ret = mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index] + 1, cccb_buf, sizeof(cccb_buf)); - if (ret) { - return ret; - } - } - DEBUG_EVENT_printf("Registered char with handle %u\n", handles[handle_index]); - ++handle_index; - - for (size_t j = 0; j < num_descriptors[i]; ++j) { - props = descriptor_flags[descriptor_index] | ATT_PROPERTY_DYNAMIC; - read_permission = ATT_SECURITY_NONE; - write_permission = ATT_SECURITY_NONE; - - if (descriptor_uuids[descriptor_index]->type == MP_BLUETOOTH_UUID_TYPE_16) { - handles[handle_index] = att_db_util_add_descriptor_uuid16(get_uuid16(descriptor_uuids[descriptor_index]), props, read_permission, write_permission, NULL, 0); - } else if (descriptor_uuids[descriptor_index]->type == MP_BLUETOOTH_UUID_TYPE_128) { - uint8_t buffer[16]; - reverse_128(descriptor_uuids[descriptor_index]->data, buffer); - handles[handle_index] = att_db_util_add_descriptor_uuid128(buffer, props, read_permission, write_permission, NULL, 0); - } else { - return MP_EINVAL; - } - mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, handles[handle_index], MP_BLUETOOTH_DEFAULT_ATTR_LEN); - DEBUG_EVENT_printf("Registered desc with handle %u\n", handles[handle_index]); - ++descriptor_index; - ++handle_index; - } - } - - return 0; -} - -int mp_bluetooth_gatts_register_service_end(void) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_register_service_end\n"); - att_server_init(att_db_util_get_address(), &att_read_callback, &att_write_callback); - return 0; -} - -int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_read\n"); - return mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); -} - -int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_write\n"); - return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); -} - -int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_notify\n"); - // Note: btstack doesn't appear to support sending a notification without a value, so include the stored value. - uint8_t *data = NULL; - size_t len = 0; - mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); - return mp_bluetooth_gatts_notify_send(conn_handle, value_handle, data, len); -} - -int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_notify_send\n"); - - // Attempt to send immediately. If it succeeds, btstack will copy the buffer. - MICROPY_PY_BLUETOOTH_ENTER - int err = att_server_notify(conn_handle, value_handle, value, value_len); - MICROPY_PY_BLUETOOTH_EXIT - - if (err == BTSTACK_ACL_BUFFERS_FULL) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_notify_send: ACL buffer full, scheduling callback\n"); - // Schedule callback, making a copy of the buffer. - mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, conn_handle, value_handle, value, value_len); - - err = att_server_request_to_send_notification(&pending_op->context_registration, conn_handle); - - if (err != ERROR_CODE_SUCCESS) { - // Failure. Unref and free the pending operation. - btstack_remove_pending_operation(pending_op, true /* del */); - } - - return 0; - } else { - return btstack_error_to_errno(err); - } -} - -int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_indicate\n"); - - uint8_t *data = NULL; - size_t len = 0; - mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len); - - // Indicate will raise ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE when - // acknowledged (or timeout/error). - - // Attempt to send immediately, will copy buffer. - MICROPY_PY_BLUETOOTH_ENTER - int err = att_server_indicate(conn_handle, value_handle, data, len); - MICROPY_PY_BLUETOOTH_EXIT - - if (err == BTSTACK_ACL_BUFFERS_FULL) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_indicate: ACL buffer full, scheduling callback\n"); - // Schedule callback, making a copy of the buffer. - mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, conn_handle, value_handle, data, len); - - err = att_server_request_to_send_indication(&pending_op->context_registration, conn_handle); - - if (err != ERROR_CODE_SUCCESS) { - // Failure. Unref and free the pending operation. - btstack_remove_pending_operation(pending_op, true /* del */); - } - - return 0; - } else { - return btstack_error_to_errno(err); - } -} - -int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { - DEBUG_EVENT_printf("mp_bluetooth_gatts_set_buffer\n"); - return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, len, append); -} - -int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gap_disconnect\n"); - gap_disconnect(conn_handle); - return 0; -} - -#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -STATIC btstack_timer_source_t scan_duration_timeout; - -STATIC void scan_duration_timeout_handler(btstack_timer_source_t *ds) { - (void)ds; - mp_bluetooth_gap_scan_stop(); -} - -int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_t window_us, bool active_scan) { - DEBUG_EVENT_printf("mp_bluetooth_gap_scan_start\n"); - - if (duration_ms > 0) { - btstack_run_loop_set_timer(&scan_duration_timeout, duration_ms); - btstack_run_loop_set_timer_handler(&scan_duration_timeout, scan_duration_timeout_handler); - btstack_run_loop_add_timer(&scan_duration_timeout); - } - - gap_set_scan_parameters(active_scan ? 1 : 0, interval_us / 625, window_us / 625); - gap_start_scan(); - - return 0; -} - -int mp_bluetooth_gap_scan_stop(void) { - DEBUG_EVENT_printf("mp_bluetooth_gap_scan_stop\n"); - btstack_run_loop_remove_timer(&scan_duration_timeout); - gap_stop_scan(); - mp_bluetooth_gap_on_scan_complete(); - return 0; -} - -int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) { - DEBUG_EVENT_printf("mp_bluetooth_gap_peripheral_connect\n"); - - uint16_t conn_scan_interval = 60000 / 625; - uint16_t conn_scan_window = 30000 / 625; - uint16_t conn_interval_min = 10000 / 1250; - uint16_t conn_interval_max = 30000 / 1250; - uint16_t conn_latency = 4; - uint16_t supervision_timeout = duration_ms / 10; // default = 720 - uint16_t min_ce_length = 10000 / 625; - uint16_t max_ce_length = 30000 / 625; - - gap_set_connection_parameters(conn_scan_interval, conn_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout, min_ce_length, max_ce_length); - - bd_addr_t btstack_addr; - memcpy(btstack_addr, addr, BD_ADDR_LEN); - return btstack_error_to_errno(gap_connect(btstack_addr, addr_type)); -} - -int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_primary_services\n"); - uint8_t err; - if (uuid) { - if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { - err = gatt_client_discover_primary_services_by_uuid16(&btstack_packet_handler_discover_services, conn_handle, get_uuid16(uuid)); - } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_128) { - uint8_t buffer[16]; - reverse_128(uuid->data, buffer); - err = gatt_client_discover_primary_services_by_uuid128(&btstack_packet_handler_discover_services, conn_handle, buffer); - } else { - DEBUG_EVENT_printf(" --> unknown UUID size\n"); - return MP_EINVAL; - } - } else { - err = gatt_client_discover_primary_services(&btstack_packet_handler_discover_services, conn_handle); - } - return btstack_error_to_errno(err); -} - -int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, const mp_obj_bluetooth_uuid_t *uuid) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_characteristics\n"); - gatt_client_service_t service = { - // Only start/end handles needed for gatt_client_discover_characteristics_for_service. - .start_group_handle = start_handle, - .end_group_handle = end_handle, - .uuid16 = 0, - .uuid128 = {0}, - }; - uint8_t err; - if (uuid) { - if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { - err = gatt_client_discover_characteristics_for_service_by_uuid16(&btstack_packet_handler_discover_characteristics, conn_handle, &service, get_uuid16(uuid)); - } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_128) { - uint8_t buffer[16]; - reverse_128(uuid->data, buffer); - err = gatt_client_discover_characteristics_for_service_by_uuid128(&btstack_packet_handler_discover_characteristics, conn_handle, &service, buffer); - } else { - DEBUG_EVENT_printf(" --> unknown UUID size\n"); - return MP_EINVAL; - } - } else { - err = gatt_client_discover_characteristics_for_service(&btstack_packet_handler_discover_characteristics, conn_handle, &service); - } - return btstack_error_to_errno(err); -} - -int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_discover_descriptors\n"); - gatt_client_characteristic_t characteristic = { - // Only start/end handles needed for gatt_client_discover_characteristic_descriptors. - .start_handle = start_handle, - .value_handle = 0, - .end_handle = end_handle, - .properties = 0, - .uuid16 = 0, - .uuid128 = {0}, - }; - return btstack_error_to_errno(gatt_client_discover_characteristic_descriptors(&btstack_packet_handler_discover_descriptors, conn_handle, &characteristic)); -} - -int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_read\n"); - return btstack_error_to_errno(gatt_client_read_value_of_characteristic_using_value_handle(&btstack_packet_handler_read, conn_handle, value_handle)); -} - -int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t *value_len, unsigned int mode) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_write\n"); - - // We should be distinguishing between gatt_client_write_value_of_characteristic vs - // gatt_client_write_characteristic_descriptor_using_descriptor_handle. - // However both are implemented using send_gatt_write_attribute_value_request under the hood, - // and we get the exact same event to the packet handler. - // Same story for the "without response" version. - - int err; - mp_btstack_pending_op_t *pending_op = NULL; - - if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { - // If possible, this will send immediately, copying the buffer directly to the ACL buffer. - err = gatt_client_write_value_of_characteristic_without_response(conn_handle, value_handle, *value_len, (uint8_t *)value); - if (err == GATT_CLIENT_BUSY) { - DEBUG_EVENT_printf("mp_bluetooth_gattc_write: client busy\n"); - // Can't send right now, need to take a copy of the buffer and add it to the queue. - pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, conn_handle, value_handle, value, *value_len); - // Notify when this conn_handle can write. - err = gatt_client_request_can_write_without_response_event(&btstack_packet_handler_generic, conn_handle); - } else { - DEBUG_EVENT_printf("mp_bluetooth_gattc_write: other failure: %d\n", err); - } - } else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { - // Pending operation copies the value buffer and keeps a GC reference - // until the response comes back (there is always a response). - pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_WRITE, conn_handle, value_handle, value, *value_len); - err = gatt_client_write_value_of_characteristic(&btstack_packet_handler_write_with_response, conn_handle, value_handle, pending_op->len, pending_op->buf); - } else { - return MP_EINVAL; - } - - if (pending_op && err != ERROR_CODE_SUCCESS) { - // Failure. Unref and free the pending operation. - btstack_remove_pending_operation(pending_op, true /* del */); - } - - return btstack_error_to_errno(err); -} -#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - -#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/extmod/btstack/modbluetooth_btstack.h b/extmod/btstack/modbluetooth_btstack.h deleted file mode 100644 index 2fad86f226..0000000000 --- a/extmod/btstack/modbluetooth_btstack.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2020 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H -#define MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H - -#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK - -#include "extmod/modbluetooth.h" - -#include "lib/btstack/src/btstack.h" - -typedef struct _mp_btstack_pending_op_t mp_btstack_pending_op_t; - -typedef struct _mp_bluetooth_btstack_root_pointers_t { - // This stores both the advertising data and the scan response data, concatenated together. - uint8_t *adv_data; - // Total length of both. - size_t adv_data_alloc; - - // Characteristic (and descriptor) value storage. - mp_gatts_db_t gatts_db; - - btstack_linked_list_t pending_ops; - - #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE - // Registration for notify/indicate events. - gatt_client_notification_t notification; - #endif -} mp_bluetooth_btstack_root_pointers_t; - -enum { - MP_BLUETOOTH_BTSTACK_STATE_OFF, - MP_BLUETOOTH_BTSTACK_STATE_STARTING, - MP_BLUETOOTH_BTSTACK_STATE_ACTIVE, - MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT, -}; - -extern volatile int mp_bluetooth_btstack_state; - -void mp_bluetooth_btstack_port_init(void); -void mp_bluetooth_btstack_port_deinit(void); -void mp_bluetooth_btstack_port_start(void); - -#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK - -#endif // MICROPY_INCLUDED_EXTMOD_BTSTACK_MODBLUETOOTH_BTSTACK_H diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 7ad3d7d851..5e12ee75d4 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -41,6 +41,8 @@ SRC_MOD += $(addprefix $(LITTLEFS_DIR)/,\ ) else CFLAGS_MOD += -DMICROPY_VFS_LFS2=0 + +$(BUILD)/$(LITTLEFS_DIR)/lfs2.o: CFLAGS += -Wno-missing-field-initializers endif ################################################################################ diff --git a/extmod/modframebuf.c b/extmod/modframebuf.c index 6ec2c44fde..abaf42b96b 100644 --- a/extmod/modframebuf.c +++ b/extmod/modframebuf.c @@ -26,9 +26,9 @@ typedef struct _mp_obj_framebuf_t { STATIC const mp_obj_type_t mp_type_framebuf; #endif -typedef void (*setpixel_t)(const mp_obj_framebuf_t *, int, int, uint32_t); -typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t *, int, int); -typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, int, int, int, int, uint32_t); +typedef void (*setpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, uint32_t); +typedef uint32_t (*getpixel_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int); +typedef void (*fill_rect_t)(const mp_obj_framebuf_t *, unsigned int, unsigned int, unsigned int, unsigned int, uint32_t); typedef struct _mp_framebuf_p_t { setpixel_t setpixel; @@ -47,25 +47,25 @@ typedef struct _mp_framebuf_p_t { // Functions for MHLSB and MHMSB -STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void mono_horiz_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { size_t index = (x + y * fb->stride) >> 3; - int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); + unsigned int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); ((uint8_t *)fb->buf)[index] = (((uint8_t *)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset); } -STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t mono_horiz_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { size_t index = (x + y * fb->stride) >> 3; - int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); + unsigned int offset = fb->format == FRAMEBUF_MHMSB ? x & 0x07 : 7 - (x & 0x07); return (((uint8_t *)fb->buf)[index] >> (offset)) & 0x01; } -STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { - int reverse = fb->format == FRAMEBUF_MHMSB; - int advance = fb->stride >> 3; +STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + unsigned int reverse = fb->format == FRAMEBUF_MHMSB; + unsigned int advance = fb->stride >> 3; while (w--) { uint8_t *b = &((uint8_t *)fb->buf)[(x >> 3) + y * advance]; - int offset = reverse ? x & 7 : 7 - (x & 7); - for (int hh = h; hh; --hh) { + unsigned int offset = reverse ? x & 7 : 7 - (x & 7); + for (unsigned int hh = h; hh; --hh) { *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset); b += advance; } @@ -75,21 +75,21 @@ STATIC void mono_horiz_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int // Functions for MVLSB format -STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void mvlsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { size_t index = (y >> 3) * fb->stride + x; uint8_t offset = y & 0x07; ((uint8_t *)fb->buf)[index] = (((uint8_t *)fb->buf)[index] & ~(0x01 << offset)) | ((col != 0) << offset); } -STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t mvlsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { return (((uint8_t *)fb->buf)[(y >> 3) * fb->stride + x] >> (y & 0x07)) & 0x01; } -STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { while (h--) { uint8_t *b = &((uint8_t *)fb->buf)[(y >> 3) * fb->stride + x]; uint8_t offset = y & 0x07; - for (int ww = w; ww; --ww) { + for (unsigned int ww = w; ww; --ww) { *b = (*b & ~(0x01 << offset)) | ((col != 0) << offset); ++b; } @@ -99,18 +99,18 @@ STATIC void mvlsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, in // Functions for RGB565 format -STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void rgb565_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { ((uint16_t *)fb->buf)[x + y * fb->stride] = col; } -STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t rgb565_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { return ((uint16_t *)fb->buf)[x + y * fb->stride]; } -STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { uint16_t *b = &((uint16_t *)fb->buf)[x + y * fb->stride]; while (h--) { - for (int ww = w; ww; --ww) { + for (unsigned int ww = w; ww; --ww) { *b++ = col; } b += fb->stride - w; @@ -119,7 +119,7 @@ STATIC void rgb565_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, i // Functions for GS2_HMSB format -STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 2]; uint8_t shift = (x & 0x3) << 1; uint8_t mask = 0x3 << shift; @@ -127,15 +127,15 @@ STATIC void gs2_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_ *pixel = color | (*pixel & (~mask)); } -STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t gs2_hmsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { uint8_t pixel = ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 2]; uint8_t shift = (x & 0x3) << 1; return (pixel >> shift) & 0x3; } -STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { - for (int xx = x; xx < x + w; xx++) { - for (int yy = y; yy < y + h; yy++) { +STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { + for (unsigned int xx = x; xx < x + w; xx++) { + for (unsigned int yy = y; yy < y + h; yy++) { gs2_hmsb_setpixel(fb, xx, yy, col); } } @@ -143,7 +143,7 @@ STATIC void gs2_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, // Functions for GS4_HMSB format -STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1]; if (x % 2) { @@ -153,7 +153,7 @@ STATIC void gs4_hmsb_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_ } } -STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { if (x % 2) { return ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1] & 0x0f; } @@ -161,16 +161,16 @@ STATIC uint32_t gs4_hmsb_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { return ((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1] >> 4; } -STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { col &= 0x0f; uint8_t *pixel_pair = &((uint8_t *)fb->buf)[(x + y * fb->stride) >> 1]; uint8_t col_shifted_left = col << 4; uint8_t col_pixel_pair = col_shifted_left | col; - int pixel_count_till_next_line = (fb->stride - w) >> 1; + unsigned int pixel_count_till_next_line = (fb->stride - w) >> 1; bool odd_x = (x % 2 == 1); while (h--) { - int ww = w; + unsigned int ww = w; if (odd_x && ww > 0) { *pixel_pair = (*pixel_pair & 0xf0) | col; @@ -194,16 +194,16 @@ STATIC void gs4_hmsb_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, // Functions for GS8 format -STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +STATIC void gs8_setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)]; *pixel = col & 0xff; } -STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +STATIC uint32_t gs8_getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { return ((uint8_t *)fb->buf)[(x + y * fb->stride)]; } -STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, int x, int y, int w, int h, uint32_t col) { +STATIC void gs8_fill_rect(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t col) { uint8_t *pixel = &((uint8_t *)fb->buf)[(x + y * fb->stride)]; while (h--) { memset(pixel, col, w); @@ -221,11 +221,11 @@ STATIC const mp_framebuf_p_t formats[] = { [FRAMEBUF_MHMSB] = {mono_horiz_setpixel, mono_horiz_getpixel, mono_horiz_fill_rect}, }; -static inline void setpixel(const mp_obj_framebuf_t *fb, int x, int y, uint32_t col) { +static inline void setpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y, uint32_t col) { formats[fb->format].setpixel(fb, x, y, col); } -static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, int x, int y) { +static inline uint32_t getpixel(const mp_obj_framebuf_t *fb, unsigned int x, unsigned int y) { return formats[fb->format].getpixel(fb, x, y); } diff --git a/extmod/modonewire.c b/extmod/modonewire.c index edb7e23cea..a963c17ebb 100644 --- a/extmod/modonewire.c +++ b/extmod/modonewire.c @@ -23,10 +23,10 @@ #define TIMING_WRITE3 (10) STATIC int onewire_bus_reset(mp_hal_pin_obj_t pin) { - mp_hal_pin_write(pin, 0); + mp_hal_pin_od_low(pin); mp_hal_delay_us(TIMING_RESET1); uint32_t i = mp_hal_quiet_timing_enter(); - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); mp_hal_delay_us_fast(TIMING_RESET2); int status = !mp_hal_pin_read(pin); mp_hal_quiet_timing_exit(i); @@ -35,11 +35,11 @@ STATIC int onewire_bus_reset(mp_hal_pin_obj_t pin) { } STATIC int onewire_bus_readbit(mp_hal_pin_obj_t pin) { - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); uint32_t i = mp_hal_quiet_timing_enter(); - mp_hal_pin_write(pin, 0); + mp_hal_pin_od_low(pin); mp_hal_delay_us_fast(TIMING_READ1); - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); mp_hal_delay_us_fast(TIMING_READ2); int value = mp_hal_pin_read(pin); mp_hal_quiet_timing_exit(i); @@ -49,13 +49,13 @@ STATIC int onewire_bus_readbit(mp_hal_pin_obj_t pin) { STATIC void onewire_bus_writebit(mp_hal_pin_obj_t pin, int value) { uint32_t i = mp_hal_quiet_timing_enter(); - mp_hal_pin_write(pin, 0); + mp_hal_pin_od_low(pin); mp_hal_delay_us_fast(TIMING_WRITE1); if (value) { - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); } mp_hal_delay_us_fast(TIMING_WRITE2); - mp_hal_pin_write(pin, 1); + mp_hal_pin_od_high(pin); mp_hal_delay_us_fast(TIMING_WRITE3); mp_hal_quiet_timing_exit(i); } diff --git a/extmod/moduasyncio.c b/extmod/moduasyncio.c index 2b647d06f2..d4b749f5ba 100644 --- a/extmod/moduasyncio.c +++ b/extmod/moduasyncio.c @@ -146,6 +146,9 @@ STATIC const mp_obj_type_t task_queue_type = { /******************************************************************************/ // Task class +// For efficiency, the task object is stored to the coro entry when the task is done. +#define TASK_IS_DONE(task) ((task)->coro == MP_OBJ_FROM_PTR(task)) + // This is the core uasyncio context with cur_task, _task_queue and CancelledError. STATIC mp_obj_t uasyncio_context = MP_OBJ_NULL; @@ -164,10 +167,16 @@ STATIC mp_obj_t task_make_new(const mp_obj_type_t *type, size_t n_args, const mp return MP_OBJ_FROM_PTR(self); } +STATIC mp_obj_t task_done(mp_obj_t self_in) { + mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(TASK_IS_DONE(self)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_done_obj, task_done); + STATIC mp_obj_t task_cancel(mp_obj_t self_in) { mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); // Check if task is already finished. - if (self->coro == mp_const_none) { + if (TASK_IS_DONE(self)) { return mp_const_false; } // Can't cancel self (not supported yet). @@ -209,6 +218,24 @@ STATIC mp_obj_t task_cancel(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(task_cancel_obj, task_cancel); +STATIC mp_obj_t task_throw(mp_obj_t self_in, mp_obj_t value_in) { + // This task raised an exception which was uncaught; handle that now. + mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); + // Set the data because it was cleared by the main scheduling loop. + self->data = value_in; + if (self->waiting == mp_const_none) { + // Nothing await'ed on the task so call the exception handler. + mp_obj_t _exc_context = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR__exc_context)); + mp_obj_dict_store(_exc_context, MP_OBJ_NEW_QSTR(MP_QSTR_exception), value_in); + mp_obj_dict_store(_exc_context, MP_OBJ_NEW_QSTR(MP_QSTR_future), self_in); + mp_obj_t Loop = mp_obj_dict_get(uasyncio_context, MP_OBJ_NEW_QSTR(MP_QSTR_Loop)); + mp_obj_t call_exception_handler = mp_load_attr(Loop, MP_QSTR_call_exception_handler); + mp_call_function_1(call_exception_handler, _exc_context); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(task_throw_obj, task_throw); + STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); if (dest[0] == MP_OBJ_NULL) { @@ -218,12 +245,18 @@ STATIC void task_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { } else if (attr == MP_QSTR_data) { dest[0] = self->data; } else if (attr == MP_QSTR_waiting) { - if (self->waiting != mp_const_none) { + if (self->waiting != mp_const_none && self->waiting != mp_const_false) { dest[0] = self->waiting; } + } else if (attr == MP_QSTR_done) { + dest[0] = MP_OBJ_FROM_PTR(&task_done_obj); + dest[1] = self_in; } else if (attr == MP_QSTR_cancel) { dest[0] = MP_OBJ_FROM_PTR(&task_cancel_obj); dest[1] = self_in; + } else if (attr == MP_QSTR_throw) { + dest[0] = MP_OBJ_FROM_PTR(&task_throw_obj); + dest[1] = self_in; } else if (attr == MP_QSTR_ph_key) { dest[0] = self->ph_key; } @@ -246,14 +279,21 @@ STATIC mp_obj_t task_getiter(mp_obj_t self_in, mp_obj_iter_buf_t *iter_buf) { (void)iter_buf; mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); if (self->waiting == mp_const_none) { - self->waiting = task_queue_make_new(&task_queue_type, 0, NULL, NULL); + // The is the first access of the "waiting" entry. + if (TASK_IS_DONE(self)) { + // Signal that the completed-task has been await'ed on. + self->waiting = mp_const_false; + } else { + // Lazily allocate the waiting queue. + self->waiting = task_queue_make_new(&task_queue_type, 0, 0, NULL); + } } return self_in; } STATIC mp_obj_t task_iternext(mp_obj_t self_in) { mp_obj_task_t *self = MP_OBJ_TO_PTR(self_in); - if (self->coro == mp_const_none) { + if (TASK_IS_DONE(self)) { // Task finished, raise return value to caller so it can continue. nlr_raise(self->data); } else { diff --git a/extmod/modubinascii.c b/extmod/modubinascii.c index 12d0c32a5d..01a3c62f6c 100644 --- a/extmod/modubinascii.c +++ b/extmod/modubinascii.c @@ -19,8 +19,8 @@ static void check_not_unicode(const mp_obj_t arg) { } STATIC mp_obj_t mod_binascii_hexlify(size_t n_args, const mp_obj_t *args) { - // Second argument is for an extension to allow a separator to be used - // between values. + // First argument is the data to convert. + // Second argument is an optional separator to be used between values. const char *sep = NULL; mp_buffer_info_t bufinfo; check_not_unicode(args[0]); diff --git a/extmod/moductypes.c b/extmod/moductypes.c index 7705dbc16e..f78b892464 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -380,7 +380,7 @@ STATIC void set_aligned(uint val_type, void *p, mp_int_t index, mp_obj_t val) { ((uint64_t *)p)[index] = (uint64_t)v; } else { // TODO: Doesn't offer atomic store semantics, but should at least try - set_unaligned(val_type, p, MP_ENDIANNESS_BIG, val); + set_unaligned(val_type, (void *)&((uint64_t *)p)[index], MP_ENDIANNESS_BIG, val); } return; default: @@ -487,6 +487,7 @@ STATIC mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set return mp_obj_new_bytearray_by_ref(uctypes_struct_agg_size(sub, self->flags, &dummy), self->addr + offset); } // Fall thru to return uctypes struct object + MP_FALLTHROUGH } case PTR: { mp_obj_uctypes_struct_t *o = m_new_obj(mp_obj_uctypes_struct_t); @@ -608,7 +609,7 @@ STATIC mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in) { return mp_obj_new_int((mp_int_t)(uintptr_t)p); } } - /* fallthru */ + MP_FALLTHROUGH default: return MP_OBJ_NULL; // op not supported diff --git a/extmod/modurandom.c b/extmod/modurandom.c index 2b6970c4ed..b33d0eb23f 100644 --- a/extmod/modurandom.c +++ b/extmod/modurandom.c @@ -10,15 +10,29 @@ #if MICROPY_PY_URANDOM +// Work out if the seed will be set on import or not. +#if MICROPY_MODULE_BUILTIN_INIT && defined(MICROPY_PY_URANDOM_SEED_INIT_FUNC) +#define SEED_ON_IMPORT (1) +#else +#define SEED_ON_IMPORT (0) +#endif + // Yasmarang random number generator // by Ilya Levin // http://www.literatecode.com/yasmarang // Public Domain #if !MICROPY_ENABLE_DYNRUNTIME +#if SEED_ON_IMPORT +// If the state is seeded on import then keep these variables in the BSS. +STATIC uint32_t yasmarang_pad, yasmarang_n, yasmarang_d; +STATIC uint8_t yasmarang_dat; +#else +// Without seed-on-import these variables must be initialised via the data section. STATIC uint32_t yasmarang_pad = 0xeda4baba, yasmarang_n = 69, yasmarang_d = 233; STATIC uint8_t yasmarang_dat = 0; #endif +#endif STATIC uint32_t yasmarang(void) { yasmarang_pad += yasmarang_dat + yasmarang_d * yasmarang_n; @@ -62,15 +76,24 @@ STATIC mp_obj_t mod_urandom_getrandbits(mp_obj_t num_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_getrandbits_obj, mod_urandom_getrandbits); -STATIC mp_obj_t mod_urandom_seed(mp_obj_t seed_in) { - mp_uint_t seed = mp_obj_get_int_truncated(seed_in); +STATIC mp_obj_t mod_urandom_seed(size_t n_args, const mp_obj_t *args) { + mp_uint_t seed; + if (n_args == 0 || args[0] == mp_const_none) { + #ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC + seed = MICROPY_PY_URANDOM_SEED_INIT_FUNC; + #else + mp_raise_ValueError(MP_ERROR_TEXT("no default seed")); + #endif + } else { + seed = mp_obj_get_int_truncated(args[0]); + } yasmarang_pad = seed; yasmarang_n = 69; yasmarang_d = 233; yasmarang_dat = 0; return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_urandom_seed_obj, mod_urandom_seed); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_urandom_seed_obj, 0, 1, mod_urandom_seed); #if MICROPY_PY_URANDOM_EXTRA_FUNCS @@ -168,9 +191,15 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_urandom_uniform_obj, mod_urandom_uniform); #endif // MICROPY_PY_URANDOM_EXTRA_FUNCS -#ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC +#if SEED_ON_IMPORT STATIC mp_obj_t mod_urandom___init__() { - mod_urandom_seed(MP_OBJ_NEW_SMALL_INT(MICROPY_PY_URANDOM_SEED_INIT_FUNC)); + // This module may be imported by more than one name so need to ensure + // that it's only ever seeded once. + static bool seeded = false; + if (!seeded) { + seeded = true; + mod_urandom_seed(0, NULL); + } return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom___init___obj, mod_urandom___init__); @@ -179,7 +208,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_urandom___init___obj, mod_urandom___init__) #if !MICROPY_ENABLE_DYNRUNTIME STATIC const mp_rom_map_elem_t mp_module_urandom_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_urandom) }, - #ifdef MICROPY_PY_URANDOM_SEED_INIT_FUNC + #if SEED_ON_IMPORT { MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&mod_urandom___init___obj) }, #endif { MP_ROM_QSTR(MP_QSTR_getrandbits), MP_ROM_PTR(&mod_urandom_getrandbits_obj) }, diff --git a/extmod/modure.c b/extmod/modure.c index 728d99701d..7fd7b09395 100644 --- a/extmod/modure.c +++ b/extmod/modure.c @@ -354,6 +354,9 @@ STATIC mp_obj_t re_sub_helper(size_t n_args, const mp_obj_t *args) { const char *end_match = match->caps[match_no * 2 + 1]; vstr_add_strn(&vstr_return, start_match, end_match - start_match); } + } else if (*repl == '\\') { + // Add the \ character + vstr_add_byte(&vstr_return, *repl++); } } else { // Just add the current byte from the replacement string diff --git a/extmod/re1.5/recursiveloop.c b/extmod/re1.5/recursiveloop.c index 5c1f37a5a1..f8cb926292 100644 --- a/extmod/re1.5/recursiveloop.c +++ b/extmod/re1.5/recursiveloop.c @@ -22,7 +22,7 @@ recursiveloop(char *pc, const char *sp, Subject *input, const char **subp, int n case Char: if(*sp != *pc++) return 0; - /* FALLTHROUGH */ + MP_FALLTHROUGH case Any: sp++; continue; diff --git a/extmod/uasyncio/core.py b/extmod/uasyncio/core.py index 045b4cd139..6a84b0982c 100644 --- a/extmod/uasyncio/core.py +++ b/extmod/uasyncio/core.py @@ -185,8 +185,6 @@ def run_until_complete(main_task=None): if isinstance(er, StopIteration): return er.value raise er - # Save return value of coro to pass up to caller - t.data = er # Schedule any other tasks waiting on the completion of this task waiting = False if hasattr(t, "waiting"): @@ -194,13 +192,15 @@ def run_until_complete(main_task=None): _task_queue.push_head(t.waiting.pop_head()) waiting = True t.waiting = None # Free waiting queue head - # Print out exception for detached tasks if not waiting and not isinstance(er, excs_stop): - _exc_context["exception"] = er - _exc_context["future"] = t - Loop.call_exception_handler(_exc_context) - # Indicate task is done - t.coro = None + # An exception ended this detached task, so queue it for later + # execution to handle the uncaught exception if no other task retrieves + # the exception in the meantime (this is handled by Task.throw). + _task_queue.push_head(t) + # Indicate task is done by setting coro to the task object itself + t.coro = t + # Save return value of coro to pass up to caller + t.data = er # Create a new task from a coroutine and run it until it finishes diff --git a/extmod/uasyncio/funcs.py b/extmod/uasyncio/funcs.py index 6e1305c94f..93f4fd256c 100644 --- a/extmod/uasyncio/funcs.py +++ b/extmod/uasyncio/funcs.py @@ -9,24 +9,44 @@ async def wait_for(aw, timeout, sleep=core.sleep): if timeout is None: return await aw - def cancel(aw, timeout, sleep): - await sleep(timeout) - aw.cancel() + def runner(waiter, aw): + nonlocal status, result + try: + result = await aw + s = True + except BaseException as er: + s = er + if status is None: + # The waiter is still waiting, set status for it and cancel it. + status = s + waiter.cancel() + + # Run aw in a separate runner task that manages its exceptions. + status = None + result = None + runner_task = core.create_task(runner(core.cur_task, aw)) - cancel_task = core.create_task(cancel(aw, timeout, sleep)) try: - ret = await aw - except core.CancelledError: - # Ignore CancelledError from aw, it's probably due to timeout - pass - finally: - # Cancel the "cancel" task if it's still active (optimisation instead of cancel_task.cancel()) - if cancel_task.coro is not None: - core._task_queue.remove(cancel_task) - if cancel_task.coro is None: - # Cancel task ran to completion, ie there was a timeout - raise core.TimeoutError - return ret + # Wait for the timeout to elapse. + await sleep(timeout) + except core.CancelledError as er: + if status is True: + # aw completed successfully and cancelled the sleep, so return aw's result. + return result + elif status is None: + # This wait_for was cancelled externally, so cancel aw and re-raise. + status = True + runner_task.cancel() + raise er + else: + # aw raised an exception, propagate it out to the caller. + raise status + + # The sleep finished before aw, so cancel aw and raise TimeoutError. + status = True + runner_task.cancel() + await runner_task + raise core.TimeoutError def wait_for_ms(aw, timeout): diff --git a/extmod/uasyncio/task.py b/extmod/uasyncio/task.py index 1788cf0ed0..68ddf496f0 100644 --- a/extmod/uasyncio/task.py +++ b/extmod/uasyncio/task.py @@ -130,13 +130,16 @@ class Task: self.ph_rightmost_parent = None # Paring heap def __iter__(self): - if not hasattr(self, "waiting"): + if self.coro is self: + # Signal that the completed-task has been await'ed on. + self.waiting = None + elif not hasattr(self, "waiting"): # Lazily allocated head of linked list of Tasks waiting on completion of this task. self.waiting = TaskQueue() return self def __next__(self): - if not self.coro: + if self.coro is self: # Task finished, raise return value to caller so it can continue. raise self.data else: @@ -145,9 +148,12 @@ class Task: # Set calling task's data to this task that it waits on, to double-link it. core.cur_task.data = self + def done(self): + return self.coro is self + def cancel(self): # Check if task is already finished. - if self.coro is None: + if self.coro is self: return False # Can't cancel self (not supported yet). if self is core.cur_task: @@ -166,3 +172,13 @@ class Task: core._task_queue.push_head(self) self.data = core.CancelledError return True + + def throw(self, value): + # This task raised an exception which was uncaught; handle that now. + # Set the data because it was cleared by the main scheduling loop. + self.data = value + if not hasattr(self, "waiting"): + # Nothing await'ed on the task so call the exception handler. + core._exc_context["exception"] = value + core._exc_context["future"] = self + core.Loop.call_exception_handler(core._exc_context) diff --git a/extmod/utime_mphal.c b/extmod/utime_mphal.c index a21f2f7fc5..0501724a67 100644 --- a/extmod/utime_mphal.c +++ b/extmod/utime_mphal.c @@ -78,4 +78,10 @@ STATIC mp_obj_t time_ticks_add(mp_obj_t ticks_in, mp_obj_t delta_in) { } MP_DEFINE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj, time_ticks_add); +// Returns the number of nanoseconds since the Epoch, as an integer. +STATIC mp_obj_t time_time_ns(void) { + return mp_obj_new_int_from_ull(mp_hal_time_ns()); +} +MP_DEFINE_CONST_FUN_OBJ_0(mp_utime_time_ns_obj, time_time_ns); + #endif // MICROPY_PY_UTIME_MP_HAL diff --git a/extmod/utime_mphal.h b/extmod/utime_mphal.h index 20b4093d31..61a851cec0 100644 --- a/extmod/utime_mphal.h +++ b/extmod/utime_mphal.h @@ -17,5 +17,6 @@ MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_us_obj); MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_ticks_cpu_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_diff_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_utime_ticks_add_obj); +MP_DECLARE_CONST_FUN_OBJ_0(mp_utime_time_ns_obj); #endif // MICROPY_INCLUDED_EXTMOD_UTIME_MPHAL_H diff --git a/extmod/vfs.c b/extmod/vfs.c index b02cefdb69..b3eeb4e4cf 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -62,12 +62,8 @@ mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) { } } - // if we get here then there's nothing mounted on / - - if (is_abs) { - // path began with / and was not found - return MP_VFS_NONE; - } + // if we get here then there's nothing mounted on /, so the path doesn't exist + return MP_VFS_NONE; } // a relative path within a mounted device @@ -144,27 +140,30 @@ STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { #if MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2 nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_obj_t vfs = MP_OBJ_NULL; + // The superblock for littlefs is in both block 0 and 1, but block 0 may be erased + // or partially written, so search both blocks 0 and 1 for the littlefs signature. mp_vfs_blockdev_t blockdev; mp_vfs_blockdev_init(&blockdev, bdev_obj); uint8_t buf[44]; - mp_vfs_blockdev_read_ext(&blockdev, 0, 8, sizeof(buf), buf); - #if MICROPY_VFS_LFS1 - if (memcmp(&buf[32], "littlefs", 8) == 0) { - // LFS1 - vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, &bdev_obj, NULL); - nlr_pop(); - return vfs; + for (size_t block_num = 0; block_num <= 1; ++block_num) { + mp_vfs_blockdev_read_ext(&blockdev, block_num, 8, sizeof(buf), buf); + #if MICROPY_VFS_LFS1 + if (memcmp(&buf[32], "littlefs", 8) == 0) { + // LFS1 + mp_obj_t vfs = mp_type_vfs_lfs1.make_new(&mp_type_vfs_lfs1, 1, &bdev_obj, NULL); + nlr_pop(); + return vfs; + } + #endif + #if MICROPY_VFS_LFS2 + if (memcmp(&buf[0], "littlefs", 8) == 0) { + // LFS2 + mp_obj_t vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, &bdev_obj, NULL); + nlr_pop(); + return vfs; + } + #endif } - #endif - #if MICROPY_VFS_LFS2 - if (memcmp(&buf[0], "littlefs", 8) == 0) { - // LFS2 - vfs = mp_type_vfs_lfs2.make_new(&mp_type_vfs_lfs2, 1, &bdev_obj, NULL); - nlr_pop(); - return vfs; - } - #endif nlr_pop(); } else { // Ignore exception (eg block device doesn't support extended readblocks) @@ -175,7 +174,8 @@ STATIC mp_obj_t mp_vfs_autodetect(mp_obj_t bdev_obj) { return mp_fat_vfs_type.make_new(&mp_fat_vfs_type, 1, &bdev_obj, NULL); #endif - return bdev_obj; + // no filesystem found + mp_raise_OSError(MP_ENODEV); } mp_obj_t mp_vfs_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/extmod/vfs_lfs.c b/extmod/vfs_lfs.c index a53f66f2d6..dd78269a46 100644 --- a/extmod/vfs_lfs.c +++ b/extmod/vfs_lfs.c @@ -26,6 +26,7 @@ #include "py/runtime.h" #include "py/mphal.h" +#include "lib/timeutils/timeutils.h" #include "extmod/vfs.h" #include "extmod/vfs_lfs.h" @@ -34,7 +35,7 @@ enum { LFS_MAKE_ARG_bdev, LFS_MAKE_ARG_readsize, LFS_MAKE_ARG_progsize, LFS_MAKE_ARG_lookahead, LFS_MAKE_ARG_mtime }; static const mp_arg_t lfs_make_allowed_args[] = { - { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_readsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, { MP_QSTR_progsize, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, { MP_QSTR_lookahead, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 32} }, @@ -126,7 +127,8 @@ const char *mp_vfs_lfs2_make_path(mp_obj_vfs_lfs2_t *self, mp_obj_t path_in); mp_obj_t mp_vfs_lfs2_file_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in); STATIC void lfs_get_mtime(uint8_t buf[8]) { - uint64_t ns = mp_hal_time_ns(); + // On-disk storage of timestamps uses 1970 as the Epoch, so convert from host's Epoch. + uint64_t ns = timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(mp_hal_time_ns()); // Store "ns" to "buf" in little-endian format (essentially htole64). for (size_t i = 0; i < 8; ++i) { buf[i] = ns; diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index d4e4e9d40e..3a2a6f1fc2 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -365,10 +365,8 @@ STATIC mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { for (size_t i = sizeof(mtime_buf); i > 0; --i) { ns = ns << 8 | mtime_buf[i - 1]; } - mtime = timeutils_seconds_since_2000_from_nanoseconds_since_1970(ns); - #if MICROPY_EPOCH_IS_1970 - mtime += TIMEUTILS_SECONDS_1970_TO_2000; - #endif + // On-disk storage of timestamps uses 1970 as the Epoch, so convert to host's Epoch. + mtime = timeutils_seconds_since_epoch_from_nanoseconds_since_1970(ns); } #endif @@ -425,10 +423,16 @@ STATIC mp_obj_t MP_VFS_LFSx(statvfs)(mp_obj_t self_in, mp_obj_t path_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_2(MP_VFS_LFSx(statvfs_obj), MP_VFS_LFSx(statvfs)); STATIC mp_obj_t MP_VFS_LFSx(mount)(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) { - (void)self_in; - (void)readonly; + MP_OBJ_VFS_LFSx *self = MP_OBJ_TO_PTR(self_in); (void)mkfs; - // already called LFSx_API(mount) in MP_VFS_LFSx(make_new) + + // Make block device read-only if requested. + if (mp_obj_is_true(readonly)) { + self->blockdev.writeblocks[0] = MP_OBJ_NULL; + } + + // Already called LFSx_API(mount) in MP_VFS_LFSx(make_new) so the filesystem is ready. + return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_3(MP_VFS_LFSx(mount_obj), MP_VFS_LFSx(mount)); diff --git a/lib/littlefs/README.md b/lib/littlefs/README.md index 1f0aacbf98..ca21a05f42 100644 --- a/lib/littlefs/README.md +++ b/lib/littlefs/README.md @@ -2,7 +2,7 @@ littlefs library ================ The upstream source for the files in this directory is -https://github.com/ARMmbed/littlefs +https://github.com/littlefs-project/littlefs To generate the separate files with lfs1 and lfs2 prefixes run the following commands in the top-level directory of the littlefs repository (replace the @@ -13,7 +13,7 @@ version tags with the latest/desired ones, and set `$MPY_DIR`): cp lfs1*.[ch] $MPY_DIR/lib/littlefs git reset --hard HEAD - git checkout v2.1.3 + git checkout v2.3.0 python2 ./scripts/prefix.py lfs2 cp lfs2*.[ch] $MPY_DIR/lib/littlefs git reset --hard HEAD diff --git a/lib/littlefs/lfs2.c b/lib/littlefs/lfs2.c index c8c1ca1f77..39bd2f0d1c 100644 --- a/lib/littlefs/lfs2.c +++ b/lib/littlefs/lfs2.c @@ -118,24 +118,29 @@ static int lfs2_bd_cmp(lfs2_t *lfs2, lfs2_block_t block, lfs2_off_t off, const void *buffer, lfs2_size_t size) { const uint8_t *data = buffer; + lfs2_size_t diff = 0; - for (lfs2_off_t i = 0; i < size; i++) { - uint8_t dat; - int err = lfs2_bd_read(lfs2, + for (lfs2_off_t i = 0; i < size; i += diff) { + uint8_t dat[8]; + + diff = lfs2_min(size-i, sizeof(dat)); + int res = lfs2_bd_read(lfs2, pcache, rcache, hint-i, - block, off+i, &dat, 1); - if (err) { - return err; + block, off+i, &dat, diff); + if (res) { + return res; } - if (dat != data[i]) { - return (dat < data[i]) ? LFS2_CMP_LT : LFS2_CMP_GT; + res = memcmp(dat, data + i, diff); + if (res) { + return res < 0 ? LFS2_CMP_LT : LFS2_CMP_GT; } } return LFS2_CMP_EQ; } +#ifndef LFS2_READONLY static int lfs2_bd_flush(lfs2_t *lfs2, lfs2_cache_t *pcache, lfs2_cache_t *rcache, bool validate) { if (pcache->block != LFS2_BLOCK_NULL && pcache->block != LFS2_BLOCK_INLINE) { @@ -168,7 +173,9 @@ static int lfs2_bd_flush(lfs2_t *lfs2, return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_bd_sync(lfs2_t *lfs2, lfs2_cache_t *pcache, lfs2_cache_t *rcache, bool validate) { lfs2_cache_drop(lfs2, rcache); @@ -182,7 +189,9 @@ static int lfs2_bd_sync(lfs2_t *lfs2, LFS2_ASSERT(err <= 0); return err; } +#endif +#ifndef LFS2_READONLY static int lfs2_bd_prog(lfs2_t *lfs2, lfs2_cache_t *pcache, lfs2_cache_t *rcache, bool validate, lfs2_block_t block, lfs2_off_t off, @@ -228,13 +237,16 @@ static int lfs2_bd_prog(lfs2_t *lfs2, return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_bd_erase(lfs2_t *lfs2, lfs2_block_t block) { LFS2_ASSERT(block < lfs2->cfg->block_count); int err = lfs2->cfg->erase(lfs2->cfg, block); LFS2_ASSERT(err <= 0); return err; } +#endif /// Small type-level utilities /// @@ -388,10 +400,12 @@ static void lfs2_ctz_fromle32(struct lfs2_ctz *ctz) { ctz->size = lfs2_fromle32(ctz->size); } +#ifndef LFS2_READONLY static void lfs2_ctz_tole32(struct lfs2_ctz *ctz) { ctz->head = lfs2_tole32(ctz->head); ctz->size = lfs2_tole32(ctz->size); } +#endif static inline void lfs2_superblock_fromle32(lfs2_superblock_t *superblock) { superblock->version = lfs2_fromle32(superblock->version); @@ -411,15 +425,48 @@ static inline void lfs2_superblock_tole32(lfs2_superblock_t *superblock) { superblock->attr_max = lfs2_tole32(superblock->attr_max); } +#ifndef LFS2_NO_ASSERT +static inline bool lfs2_mlist_isopen(struct lfs2_mlist *head, + struct lfs2_mlist *node) { + for (struct lfs2_mlist **p = &head; *p; p = &(*p)->next) { + if (*p == (struct lfs2_mlist*)node) { + return true; + } + } + + return false; +} +#endif + +static inline void lfs2_mlist_remove(lfs2_t *lfs2, struct lfs2_mlist *mlist) { + for (struct lfs2_mlist **p = &lfs2->mlist; *p; p = &(*p)->next) { + if (*p == mlist) { + *p = (*p)->next; + break; + } + } +} + +static inline void lfs2_mlist_append(lfs2_t *lfs2, struct lfs2_mlist *mlist) { + mlist->next = lfs2->mlist; + lfs2->mlist = mlist; +} + /// Internal operations predeclared here /// +#ifndef LFS2_READONLY static int lfs2_dir_commit(lfs2_t *lfs2, lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount); static int lfs2_dir_compact(lfs2_t *lfs2, lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount, lfs2_mdir_t *source, uint16_t begin, uint16_t end); + +static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file, + const void *buffer, lfs2_size_t size); +static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file); static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file); static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file); + static void lfs2_fs_preporphans(lfs2_t *lfs2, int8_t orphans); static void lfs2_fs_prepmove(lfs2_t *lfs2, uint16_t id, const lfs2_block_t pair[2]); @@ -429,17 +476,32 @@ static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t dir[2], lfs2_mdir_t *parent); static int lfs2_fs_relocate(lfs2_t *lfs2, const lfs2_block_t oldpair[2], lfs2_block_t newpair[2]); -int lfs2_fs_traverseraw(lfs2_t *lfs2, - int (*cb)(void *data, lfs2_block_t block), void *data, - bool includeorphans); static int lfs2_fs_forceconsistency(lfs2_t *lfs2); -static int lfs2_deinit(lfs2_t *lfs2); +#endif + #ifdef LFS2_MIGRATE static int lfs21_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); #endif +static int lfs2_dir_rawrewind(lfs2_t *lfs2, lfs2_dir_t *dir); + +static lfs2_ssize_t lfs2_file_rawread(lfs2_t *lfs2, lfs2_file_t *file, + void *buffer, lfs2_size_t size); +static int lfs2_file_rawclose(lfs2_t *lfs2, lfs2_file_t *file); +static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file); + +static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2); +static int lfs2_fs_rawtraverse(lfs2_t *lfs2, + int (*cb)(void *data, lfs2_block_t block), void *data, + bool includeorphans); + +static int lfs2_deinit(lfs2_t *lfs2); +static int lfs2_rawunmount(lfs2_t *lfs2); + + /// Block allocator /// +#ifndef LFS2_READONLY static int lfs2_alloc_lookahead(void *p, lfs2_block_t block) { lfs2_t *lfs2 = (lfs2_t*)p; lfs2_block_t off = ((block - lfs2->free.off) @@ -451,20 +513,24 @@ static int lfs2_alloc_lookahead(void *p, lfs2_block_t block) { return 0; } +#endif +// indicate allocated blocks have been committed into the filesystem, this +// is to prevent blocks from being garbage collected in the middle of a +// commit operation static void lfs2_alloc_ack(lfs2_t *lfs2) { lfs2->free.ack = lfs2->cfg->block_count; } -// Invalidate the lookahead buffer. This is done during mounting and -// failed traversals -static void lfs2_alloc_reset(lfs2_t *lfs2) { - lfs2->free.off = lfs2->seed % lfs2->cfg->block_size; +// drop the lookahead buffer, this is done during mounting and failed +// traversals in order to avoid invalid lookahead state +static void lfs2_alloc_drop(lfs2_t *lfs2) { lfs2->free.size = 0; lfs2->free.i = 0; lfs2_alloc_ack(lfs2); } +#ifndef LFS2_READONLY static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) { while (true) { while (lfs2->free.i != lfs2->free.size) { @@ -503,13 +569,14 @@ static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) { // find mask of free blocks from tree memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); - int err = lfs2_fs_traverseraw(lfs2, lfs2_alloc_lookahead, lfs2, true); + int err = lfs2_fs_rawtraverse(lfs2, lfs2_alloc_lookahead, lfs2, true); if (err) { - lfs2_alloc_reset(lfs2); + lfs2_alloc_drop(lfs2); return err; } } } +#endif /// Metadata pair and directory operations /// static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir, @@ -642,6 +709,7 @@ static int lfs2_dir_getread(lfs2_t *lfs2, const lfs2_mdir_t *dir, return 0; } +#ifndef LFS2_READONLY static int lfs2_dir_traverse_filter(void *p, lfs2_tag_t tag, const void *buffer) { lfs2_tag_t *filtertag = p; @@ -669,7 +737,9 @@ static int lfs2_dir_traverse_filter(void *p, return false; } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_traverse(lfs2_t *lfs2, const lfs2_mdir_t *dir, lfs2_off_t off, lfs2_tag_t ptag, const struct lfs2_mattr *attrs, int attrcount, @@ -763,6 +833,7 @@ static int lfs2_dir_traverse(lfs2_t *lfs2, } } } +#endif static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, lfs2_mdir_t *dir, const lfs2_block_t pair[2], @@ -870,8 +941,10 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, ptag ^= (lfs2_tag_t)(lfs2_tag_chunk(tag) & 1U) << 31; // toss our crc into the filesystem seed for - // pseudorandom numbers - lfs2->seed ^= crc; + // pseudorandom numbers, note we use another crc here + // as a collection function because it is sufficiently + // random and convenient + lfs2->seed = lfs2_crc(lfs2->seed, &crc, sizeof(crc)); // update with what's found so far besttag = tempbesttag; @@ -1200,6 +1273,7 @@ struct lfs2_commit { lfs2_off_t end; }; +#ifndef LFS2_READONLY static int lfs2_dir_commitprog(lfs2_t *lfs2, struct lfs2_commit *commit, const void *buffer, lfs2_size_t size) { int err = lfs2_bd_prog(lfs2, @@ -1214,7 +1288,9 @@ static int lfs2_dir_commitprog(lfs2_t *lfs2, struct lfs2_commit *commit, commit->off += size; return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_commitattr(lfs2_t *lfs2, struct lfs2_commit *commit, lfs2_tag_t tag, const void *buffer) { // check if we fit @@ -1259,14 +1335,17 @@ static int lfs2_dir_commitattr(lfs2_t *lfs2, struct lfs2_commit *commit, commit->ptag = tag & 0x7fffffff; return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_commitcrc(lfs2_t *lfs2, struct lfs2_commit *commit) { - const lfs2_off_t off1 = commit->off; - const uint32_t crc1 = commit->crc; // align to program units - const lfs2_off_t end = lfs2_alignup(off1 + 2*sizeof(uint32_t), + const lfs2_off_t end = lfs2_alignup(commit->off + 2*sizeof(uint32_t), lfs2->cfg->prog_size); + lfs2_off_t off1 = 0; + uint32_t crc1 = 0; + // create crc tags to fill up remainder of commit, note that // padding is not crced, which lets fetches skip padding but // makes committing a bit more complicated @@ -1302,6 +1381,12 @@ static int lfs2_dir_commitcrc(lfs2_t *lfs2, struct lfs2_commit *commit) { return err; } + // keep track of non-padding checksum to verify + if (off1 == 0) { + off1 = commit->off + sizeof(uint32_t); + crc1 = commit->crc; + } + commit->off += sizeof(tag)+lfs2_tag_size(tag); commit->ptag = tag ^ ((lfs2_tag_t)reset << 31); commit->crc = 0xffffffff; // reset crc for next "commit" @@ -1315,7 +1400,7 @@ static int lfs2_dir_commitcrc(lfs2_t *lfs2, struct lfs2_commit *commit) { // successful commit, check checksums to make sure lfs2_off_t off = commit->begin; - lfs2_off_t noff = off1 + sizeof(uint32_t); + lfs2_off_t noff = off1; while (off < end) { uint32_t crc = 0xffffffff; for (lfs2_off_t i = off; i < noff+sizeof(uint32_t); i++) { @@ -1352,7 +1437,9 @@ static int lfs2_dir_commitcrc(lfs2_t *lfs2, struct lfs2_commit *commit) { return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_alloc(lfs2_t *lfs2, lfs2_mdir_t *dir) { // allocate pair of dir blocks (backwards, so we write block 1 first) for (int i = 0; i < 2; i++) { @@ -1375,8 +1462,12 @@ static int lfs2_dir_alloc(lfs2_t *lfs2, lfs2_mdir_t *dir) { return err; } - // make sure we don't immediately evict - dir->rev += dir->rev & 1; + // to make sure we don't immediately evict, align the new revision count + // to our block_cycles modulus, see lfs2_dir_compact for why our modulus + // is tweaked this way + if (lfs2->cfg->block_cycles > 0) { + dir->rev = lfs2_alignup(dir->rev, ((lfs2->cfg->block_cycles+1)|1)); + } // set defaults dir->off = sizeof(dir->rev); @@ -1390,7 +1481,9 @@ static int lfs2_dir_alloc(lfs2_t *lfs2, lfs2_mdir_t *dir) { // don't write out yet, let caller take care of that return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_drop(lfs2_t *lfs2, lfs2_mdir_t *dir, lfs2_mdir_t *tail) { // steal state int err = lfs2_dir_getgstate(lfs2, tail, &lfs2->gdelta); @@ -1409,7 +1502,9 @@ static int lfs2_dir_drop(lfs2_t *lfs2, lfs2_mdir_t *dir, lfs2_mdir_t *tail) { return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_split(lfs2_t *lfs2, lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount, lfs2_mdir_t *source, uint16_t split, uint16_t end) { @@ -1442,7 +1537,9 @@ static int lfs2_dir_split(lfs2_t *lfs2, return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_commit_size(void *p, lfs2_tag_t tag, const void *buffer) { lfs2_size_t *size = p; (void)buffer; @@ -1450,17 +1547,23 @@ static int lfs2_dir_commit_size(void *p, lfs2_tag_t tag, const void *buffer) { *size += lfs2_tag_dsize(tag); return 0; } +#endif +#ifndef LFS2_READONLY struct lfs2_dir_commit_commit { lfs2_t *lfs2; struct lfs2_commit *commit; }; +#endif +#ifndef LFS2_READONLY static int lfs2_dir_commit_commit(void *p, lfs2_tag_t tag, const void *buffer) { struct lfs2_dir_commit_commit *commit = p; return lfs2_dir_commitattr(commit->lfs2, commit->commit, tag, buffer); } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_compact(lfs2_t *lfs2, lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount, lfs2_mdir_t *source, uint16_t begin, uint16_t end) { @@ -1525,7 +1628,7 @@ static int lfs2_dir_compact(lfs2_t *lfs2, if (lfs2_pair_cmp(dir->pair, (const lfs2_block_t[2]){0, 1}) == 0) { // oh no! we're writing too much to the superblock, // should we expand? - lfs2_ssize_t res = lfs2_fs_size(lfs2); + lfs2_ssize_t res = lfs2_fs_rawsize(lfs2); if (res < 0) { return res; } @@ -1715,7 +1818,9 @@ relocate: return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_dir_commit(lfs2_t *lfs2, lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount) { // check for any inline files that aren't RAM backed and @@ -1903,15 +2008,15 @@ compact: return 0; } +#endif /// Top level directory operations /// -int lfs2_mkdir(lfs2_t *lfs2, const char *path) { - LFS2_TRACE("lfs2_mkdir(%p, \"%s\")", (void*)lfs2, path); +#ifndef LFS2_READONLY +static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { - LFS2_TRACE("lfs2_mkdir -> %d", err); return err; } @@ -1920,14 +2025,12 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) { uint16_t id; err = lfs2_dir_find(lfs2, &cwd.m, &path, &id); if (!(err == LFS2_ERR_NOENT && id != 0x3ff)) { - LFS2_TRACE("lfs2_mkdir -> %d", (err < 0) ? err : LFS2_ERR_EXIST); return (err < 0) ? err : LFS2_ERR_EXIST; } // check that name fits lfs2_size_t nlen = strlen(path); if (nlen > lfs2->name_max) { - LFS2_TRACE("lfs2_mkdir -> %d", LFS2_ERR_NAMETOOLONG); return LFS2_ERR_NAMETOOLONG; } @@ -1936,7 +2039,6 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) { lfs2_mdir_t dir; err = lfs2_dir_alloc(lfs2, &dir); if (err) { - LFS2_TRACE("lfs2_mkdir -> %d", err); return err; } @@ -1945,7 +2047,6 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) { while (pred.split) { err = lfs2_dir_fetch(lfs2, &pred, pred.tail); if (err) { - LFS2_TRACE("lfs2_mkdir -> %d", err); return err; } } @@ -1956,7 +2057,6 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) { {LFS2_MKTAG(LFS2_TYPE_SOFTTAIL, 0x3ff, 8), pred.tail})); lfs2_pair_fromle32(pred.tail); if (err) { - LFS2_TRACE("lfs2_mkdir -> %d", err); return err; } @@ -1979,7 +2079,6 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) { lfs2_pair_fromle32(dir.pair); if (err) { lfs2->mlist = cwd.next; - LFS2_TRACE("lfs2_mkdir -> %d", err); return err; } @@ -1997,24 +2096,20 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) { LFS2_TYPE_SOFTTAIL, 0x3ff, 8), dir.pair})); lfs2_pair_fromle32(dir.pair); if (err) { - LFS2_TRACE("lfs2_mkdir -> %d", err); return err; } - LFS2_TRACE("lfs2_mkdir -> %d", 0); return 0; } +#endif -int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { - LFS2_TRACE("lfs2_dir_open(%p, %p, \"%s\")", (void*)lfs2, (void*)dir, path); +static int lfs2_dir_rawopen(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { lfs2_stag_t tag = lfs2_dir_find(lfs2, &dir->m, &path, NULL); if (tag < 0) { - LFS2_TRACE("lfs2_dir_open -> %"PRId32, tag); return tag; } if (lfs2_tag_type3(tag) != LFS2_TYPE_DIR) { - LFS2_TRACE("lfs2_dir_open -> %d", LFS2_ERR_NOTDIR); return LFS2_ERR_NOTDIR; } @@ -2028,7 +2123,6 @@ int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { lfs2_stag_t res = lfs2_dir_get(lfs2, &dir->m, LFS2_MKTAG(0x700, 0x3ff, 0), LFS2_MKTAG(LFS2_TYPE_STRUCT, lfs2_tag_id(tag), 8), pair); if (res < 0) { - LFS2_TRACE("lfs2_dir_open -> %"PRId32, res); return res; } lfs2_pair_fromle32(pair); @@ -2037,7 +2131,6 @@ int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { // fetch first pair int err = lfs2_dir_fetch(lfs2, &dir->m, pair); if (err) { - LFS2_TRACE("lfs2_dir_open -> %d", err); return err; } @@ -2049,30 +2142,19 @@ int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { // add to list of mdirs dir->type = LFS2_TYPE_DIR; - dir->next = (lfs2_dir_t*)lfs2->mlist; - lfs2->mlist = (struct lfs2_mlist*)dir; + lfs2_mlist_append(lfs2, (struct lfs2_mlist *)dir); - LFS2_TRACE("lfs2_dir_open -> %d", 0); return 0; } -int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir) { - LFS2_TRACE("lfs2_dir_close(%p, %p)", (void*)lfs2, (void*)dir); +static int lfs2_dir_rawclose(lfs2_t *lfs2, lfs2_dir_t *dir) { // remove from list of mdirs - for (struct lfs2_mlist **p = &lfs2->mlist; *p; p = &(*p)->next) { - if (*p == (struct lfs2_mlist*)dir) { - *p = (*p)->next; - break; - } - } + lfs2_mlist_remove(lfs2, (struct lfs2_mlist *)dir); - LFS2_TRACE("lfs2_dir_close -> %d", 0); return 0; } -int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { - LFS2_TRACE("lfs2_dir_read(%p, %p, %p)", - (void*)lfs2, (void*)dir, (void*)info); +static int lfs2_dir_rawread(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { memset(info, 0, sizeof(*info)); // special offset for '.' and '..' @@ -2080,26 +2162,22 @@ int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { info->type = LFS2_TYPE_DIR; strcpy(info->name, "."); dir->pos += 1; - LFS2_TRACE("lfs2_dir_read -> %d", true); return true; } else if (dir->pos == 1) { info->type = LFS2_TYPE_DIR; strcpy(info->name, ".."); dir->pos += 1; - LFS2_TRACE("lfs2_dir_read -> %d", true); return true; } while (true) { if (dir->id == dir->m.count) { if (!dir->m.split) { - LFS2_TRACE("lfs2_dir_read -> %d", false); return false; } int err = lfs2_dir_fetch(lfs2, &dir->m, dir->m.tail); if (err) { - LFS2_TRACE("lfs2_dir_read -> %d", err); return err; } @@ -2108,7 +2186,6 @@ int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { int err = lfs2_dir_getinfo(lfs2, &dir->m, dir->id, info); if (err && err != LFS2_ERR_NOENT) { - LFS2_TRACE("lfs2_dir_read -> %d", err); return err; } @@ -2119,17 +2196,13 @@ int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { } dir->pos += 1; - LFS2_TRACE("lfs2_dir_read -> %d", true); return true; } -int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { - LFS2_TRACE("lfs2_dir_seek(%p, %p, %"PRIu32")", - (void*)lfs2, (void*)dir, off); +static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { // simply walk from head dir - int err = lfs2_dir_rewind(lfs2, dir); + int err = lfs2_dir_rawrewind(lfs2, dir); if (err) { - LFS2_TRACE("lfs2_dir_seek -> %d", err); return err; } @@ -2148,13 +2221,11 @@ int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { if (dir->id == dir->m.count) { if (!dir->m.split) { - LFS2_TRACE("lfs2_dir_seek -> %d", LFS2_ERR_INVAL); return LFS2_ERR_INVAL; } err = lfs2_dir_fetch(lfs2, &dir->m, dir->m.tail); if (err) { - LFS2_TRACE("lfs2_dir_seek -> %d", err); return err; } @@ -2162,29 +2233,23 @@ int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { } } - LFS2_TRACE("lfs2_dir_seek -> %d", 0); return 0; } -lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir) { - LFS2_TRACE("lfs2_dir_tell(%p, %p)", (void*)lfs2, (void*)dir); +static lfs2_soff_t lfs2_dir_rawtell(lfs2_t *lfs2, lfs2_dir_t *dir) { (void)lfs2; - LFS2_TRACE("lfs2_dir_tell -> %"PRId32, dir->pos); return dir->pos; } -int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir) { - LFS2_TRACE("lfs2_dir_rewind(%p, %p)", (void*)lfs2, (void*)dir); +static int lfs2_dir_rawrewind(lfs2_t *lfs2, lfs2_dir_t *dir) { // reload the head dir int err = lfs2_dir_fetch(lfs2, &dir->m, dir->head); if (err) { - LFS2_TRACE("lfs2_dir_rewind -> %d", err); return err; } dir->id = 0; dir->pos = 0; - LFS2_TRACE("lfs2_dir_rewind -> %d", 0); return 0; } @@ -2237,6 +2302,7 @@ static int lfs2_ctz_find(lfs2_t *lfs2, return 0; } +#ifndef LFS2_READONLY static int lfs2_ctz_extend(lfs2_t *lfs2, lfs2_cache_t *pcache, lfs2_cache_t *rcache, lfs2_block_t head, lfs2_size_t size, @@ -2334,6 +2400,7 @@ relocate: lfs2_cache_drop(lfs2, pcache); } } +#endif static int lfs2_ctz_traverse(lfs2_t *lfs2, const lfs2_cache_t *pcache, lfs2_cache_t *rcache, @@ -2380,27 +2447,25 @@ static int lfs2_ctz_traverse(lfs2_t *lfs2, /// Top level file operations /// -int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, +static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags, const struct lfs2_file_config *cfg) { - LFS2_TRACE("lfs2_file_opencfg(%p, %p, \"%s\", %x, %p {" - ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", - (void*)lfs2, (void*)file, path, flags, - (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); - +#ifndef LFS2_READONLY // deorphan if we haven't yet, needed at most once after poweron - if ((flags & 3) != LFS2_O_RDONLY) { + if ((flags & LFS2_O_WRONLY) == LFS2_O_WRONLY) { int err = lfs2_fs_forceconsistency(lfs2); if (err) { - LFS2_TRACE("lfs2_file_opencfg -> %d", err); return err; } } +#else + LFS2_ASSERT((flags & LFS2_O_RDONLY) == LFS2_O_RDONLY); +#endif // setup simple file details int err; file->cfg = cfg; - file->flags = flags | LFS2_F_OPENED; + file->flags = flags; file->pos = 0; file->off = 0; file->cache.buffer = NULL; @@ -2414,9 +2479,13 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, // get id, add to list of mdirs to catch update changes file->type = LFS2_TYPE_REG; - file->next = (lfs2_file_t*)lfs2->mlist; - lfs2->mlist = (struct lfs2_mlist*)file; + lfs2_mlist_append(lfs2, (struct lfs2_mlist *)file); +#ifdef LFS2_READONLY + if (tag == LFS2_ERR_NOENT) { + err = LFS2_ERR_NOENT; + goto cleanup; +#else if (tag == LFS2_ERR_NOENT) { if (!(flags & LFS2_O_CREAT)) { err = LFS2_ERR_NOENT; @@ -2432,9 +2501,9 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, // get next slot and create entry to remember name err = lfs2_dir_commit(lfs2, &file->m, LFS2_MKATTRS( - {LFS2_MKTAG(LFS2_TYPE_CREATE, file->id, 0)}, + {LFS2_MKTAG(LFS2_TYPE_CREATE, file->id, 0), NULL}, {LFS2_MKTAG(LFS2_TYPE_REG, file->id, nlen), path}, - {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, file->id, 0)})); + {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, file->id, 0), NULL})); if (err) { err = LFS2_ERR_NAMETOOLONG; goto cleanup; @@ -2444,13 +2513,16 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, } else if (flags & LFS2_O_EXCL) { err = LFS2_ERR_EXIST; goto cleanup; +#endif } else if (lfs2_tag_type3(tag) != LFS2_TYPE_REG) { err = LFS2_ERR_ISDIR; goto cleanup; +#ifndef LFS2_READONLY } else if (flags & LFS2_O_TRUNC) { // truncate if requested tag = LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, file->id, 0); file->flags |= LFS2_F_DIRTY; +#endif } else { // try to load what's on disk, if it's inlined we'll fix it later tag = lfs2_dir_get(lfs2, &file->m, LFS2_MKTAG(0x700, 0x3ff, 0), @@ -2464,7 +2536,8 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, // fetch attrs for (unsigned i = 0; i < file->cfg->attr_count; i++) { - if ((file->flags & 3) != LFS2_O_WRONLY) { + // if opened for read / read-write operations + if ((file->flags & LFS2_O_RDONLY) == LFS2_O_RDONLY) { lfs2_stag_t res = lfs2_dir_get(lfs2, &file->m, LFS2_MKTAG(0x7ff, 0x3ff, 0), LFS2_MKTAG(LFS2_TYPE_USERATTR + file->cfg->attrs[i].type, @@ -2476,7 +2549,9 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, } } - if ((file->flags & 3) != LFS2_O_RDONLY) { +#ifndef LFS2_READONLY + // if opened for write / read-write operations + if ((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY) { if (file->cfg->attrs[i].size > lfs2->attr_max) { err = LFS2_ERR_NOSPC; goto cleanup; @@ -2484,6 +2559,7 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, file->flags |= LFS2_F_DIRTY; } +#endif } // allocate buffer if needed @@ -2523,54 +2599,45 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, } } - LFS2_TRACE("lfs2_file_opencfg -> %d", 0); return 0; cleanup: // clean up lingering resources +#ifndef LFS2_READONLY file->flags |= LFS2_F_ERRED; - lfs2_file_close(lfs2, file); - LFS2_TRACE("lfs2_file_opencfg -> %d", err); +#endif + lfs2_file_rawclose(lfs2, file); return err; } -int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, +static int lfs2_file_rawopen(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags) { - LFS2_TRACE("lfs2_file_open(%p, %p, \"%s\", %x)", - (void*)lfs2, (void*)file, path, flags); static const struct lfs2_file_config defaults = {0}; - int err = lfs2_file_opencfg(lfs2, file, path, flags, &defaults); - LFS2_TRACE("lfs2_file_open -> %d", err); + int err = lfs2_file_rawopencfg(lfs2, file, path, flags, &defaults); return err; } -int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file) { - LFS2_TRACE("lfs2_file_close(%p, %p)", (void*)lfs2, (void*)file); - LFS2_ASSERT(file->flags & LFS2_F_OPENED); - - int err = lfs2_file_sync(lfs2, file); +static int lfs2_file_rawclose(lfs2_t *lfs2, lfs2_file_t *file) { +#ifndef LFS2_READONLY + int err = lfs2_file_rawsync(lfs2, file); +#else + int err = 0; +#endif // remove from list of mdirs - for (struct lfs2_mlist **p = &lfs2->mlist; *p; p = &(*p)->next) { - if (*p == (struct lfs2_mlist*)file) { - *p = (*p)->next; - break; - } - } + lfs2_mlist_remove(lfs2, (struct lfs2_mlist*)file); // clean up memory if (!file->cfg->buffer) { lfs2_free(file->cache.buffer); } - file->flags &= ~LFS2_F_OPENED; - LFS2_TRACE("lfs2_file_close -> %d", err); return err; } -static int lfs2_file_relocate(lfs2_t *lfs2, lfs2_file_t *file) { - LFS2_ASSERT(file->flags & LFS2_F_OPENED); +#ifndef LFS2_READONLY +static int lfs2_file_relocate(lfs2_t *lfs2, lfs2_file_t *file) { while (true) { // just relocate what exists into new block lfs2_block_t nblock; @@ -2638,7 +2705,9 @@ relocate: lfs2_cache_drop(lfs2, &lfs2->pcache); } } +#endif +#ifndef LFS2_READONLY static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file) { file->off = file->pos; lfs2_alloc_ack(lfs2); @@ -2650,10 +2719,10 @@ static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file) { file->flags &= ~LFS2_F_INLINE; return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { - LFS2_ASSERT(file->flags & LFS2_F_OPENED); - if (file->flags & LFS2_F_READING) { if (!(file->flags & LFS2_F_INLINE)) { lfs2_cache_drop(lfs2, &file->cache); @@ -2669,7 +2738,7 @@ static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { lfs2_file_t orig = { .ctz.head = file->ctz.head, .ctz.size = file->ctz.size, - .flags = LFS2_O_RDONLY | LFS2_F_OPENED, + .flags = LFS2_O_RDONLY, .pos = file->pos, .cache = lfs2->rcache, }; @@ -2679,12 +2748,12 @@ static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { // copy over a byte at a time, leave it up to caching // to make this efficient uint8_t data; - lfs2_ssize_t res = lfs2_file_read(lfs2, &orig, &data, 1); + lfs2_ssize_t res = lfs2_file_rawread(lfs2, &orig, &data, 1); if (res < 0) { return res; } - res = lfs2_file_write(lfs2, file, &data, 1); + res = lfs2_file_rawwrite(lfs2, file, &data, 1); if (res < 0) { return res; } @@ -2730,24 +2799,22 @@ relocate: return 0; } +#endif -int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file) { - LFS2_TRACE("lfs2_file_sync(%p, %p)", (void*)lfs2, (void*)file); - LFS2_ASSERT(file->flags & LFS2_F_OPENED); - +#ifndef LFS2_READONLY +static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) { if (file->flags & LFS2_F_ERRED) { // it's not safe to do anything if our file errored - LFS2_TRACE("lfs2_file_sync -> %d", 0); return 0; } int err = lfs2_file_flush(lfs2, file); if (err) { file->flags |= LFS2_F_ERRED; - LFS2_TRACE("lfs2_file_sync -> %d", err); return err; } + if ((file->flags & LFS2_F_DIRTY) && !lfs2_pair_isnull(file->m.pair)) { // update dir entry @@ -2777,39 +2844,35 @@ int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file) { file->cfg->attr_count), file->cfg->attrs})); if (err) { file->flags |= LFS2_F_ERRED; - LFS2_TRACE("lfs2_file_sync -> %d", err); return err; } file->flags &= ~LFS2_F_DIRTY; } - LFS2_TRACE("lfs2_file_sync -> %d", 0); return 0; } +#endif -lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_rawread(lfs2_t *lfs2, lfs2_file_t *file, void *buffer, lfs2_size_t size) { - LFS2_TRACE("lfs2_file_read(%p, %p, %p, %"PRIu32")", - (void*)lfs2, (void*)file, buffer, size); - LFS2_ASSERT(file->flags & LFS2_F_OPENED); - LFS2_ASSERT((file->flags & 3) != LFS2_O_WRONLY); + LFS2_ASSERT((file->flags & LFS2_O_RDONLY) == LFS2_O_RDONLY); uint8_t *data = buffer; lfs2_size_t nsize = size; +#ifndef LFS2_READONLY if (file->flags & LFS2_F_WRITING) { // flush out any writes int err = lfs2_file_flush(lfs2, file); if (err) { - LFS2_TRACE("lfs2_file_read -> %d", err); return err; } } +#endif if (file->pos >= file->ctz.size) { // eof if past end - LFS2_TRACE("lfs2_file_read -> %d", 0); return 0; } @@ -2825,7 +2888,6 @@ lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, file->ctz.head, file->ctz.size, file->pos, &file->block, &file->off); if (err) { - LFS2_TRACE("lfs2_file_read -> %d", err); return err; } } else { @@ -2845,7 +2907,6 @@ lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, file->id, 0), file->off, data, diff); if (err) { - LFS2_TRACE("lfs2_file_read -> %d", err); return err; } } else { @@ -2853,7 +2914,6 @@ lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, NULL, &file->cache, lfs2->cfg->block_size, file->block, file->off, data, diff); if (err) { - LFS2_TRACE("lfs2_file_read -> %d", err); return err; } } @@ -2864,16 +2924,13 @@ lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, nsize -= diff; } - LFS2_TRACE("lfs2_file_read -> %"PRId32, size); return size; } -lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, +#ifndef LFS2_READONLY +static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file, const void *buffer, lfs2_size_t size) { - LFS2_TRACE("lfs2_file_write(%p, %p, %p, %"PRIu32")", - (void*)lfs2, (void*)file, buffer, size); - LFS2_ASSERT(file->flags & LFS2_F_OPENED); - LFS2_ASSERT((file->flags & 3) != LFS2_O_RDONLY); + LFS2_ASSERT((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY); const uint8_t *data = buffer; lfs2_size_t nsize = size; @@ -2882,7 +2939,6 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, // drop any reads int err = lfs2_file_flush(lfs2, file); if (err) { - LFS2_TRACE("lfs2_file_write -> %d", err); return err; } } @@ -2893,7 +2949,6 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, if (file->pos + size > lfs2->file_max) { // Larger than file limit? - LFS2_TRACE("lfs2_file_write -> %d", LFS2_ERR_FBIG); return LFS2_ERR_FBIG; } @@ -2903,9 +2958,8 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, file->pos = file->ctz.size; while (file->pos < pos) { - lfs2_ssize_t res = lfs2_file_write(lfs2, file, &(uint8_t){0}, 1); + lfs2_ssize_t res = lfs2_file_rawwrite(lfs2, file, &(uint8_t){0}, 1); if (res < 0) { - LFS2_TRACE("lfs2_file_write -> %"PRId32, res); return res; } } @@ -2919,7 +2973,6 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, int err = lfs2_file_outline(lfs2, file); if (err) { file->flags |= LFS2_F_ERRED; - LFS2_TRACE("lfs2_file_write -> %d", err); return err; } } @@ -2936,7 +2989,6 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, file->pos-1, &file->block, &file->off); if (err) { file->flags |= LFS2_F_ERRED; - LFS2_TRACE("lfs2_file_write -> %d", err); return err; } @@ -2951,7 +3003,6 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, &file->block, &file->off); if (err) { file->flags |= LFS2_F_ERRED; - LFS2_TRACE("lfs2_file_write -> %d", err); return err; } } else { @@ -2972,7 +3023,6 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, goto relocate; } file->flags |= LFS2_F_ERRED; - LFS2_TRACE("lfs2_file_write -> %d", err); return err; } @@ -2981,7 +3031,6 @@ relocate: err = lfs2_file_relocate(lfs2, file); if (err) { file->flags |= LFS2_F_ERRED; - LFS2_TRACE("lfs2_file_write -> %d", err); return err; } } @@ -2995,22 +3044,19 @@ relocate: } file->flags &= ~LFS2_F_ERRED; - LFS2_TRACE("lfs2_file_write -> %"PRId32, size); return size; } +#endif -lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file, lfs2_soff_t off, int whence) { - LFS2_TRACE("lfs2_file_seek(%p, %p, %"PRId32", %d)", - (void*)lfs2, (void*)file, off, whence); - LFS2_ASSERT(file->flags & LFS2_F_OPENED); - +#ifndef LFS2_READONLY // write out everything beforehand, may be noop if rdonly int err = lfs2_file_flush(lfs2, file); if (err) { - LFS2_TRACE("lfs2_file_seek -> %d", err); return err; } +#endif // find new pos lfs2_off_t npos = file->pos; @@ -3024,34 +3070,28 @@ lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, if (npos > lfs2->file_max) { // file position out of range - LFS2_TRACE("lfs2_file_seek -> %d", LFS2_ERR_INVAL); return LFS2_ERR_INVAL; } // update pos file->pos = npos; - LFS2_TRACE("lfs2_file_seek -> %"PRId32, npos); return npos; } -int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { - LFS2_TRACE("lfs2_file_truncate(%p, %p, %"PRIu32")", - (void*)lfs2, (void*)file, size); - LFS2_ASSERT(file->flags & LFS2_F_OPENED); - LFS2_ASSERT((file->flags & 3) != LFS2_O_RDONLY); +#ifndef LFS2_READONLY +static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { + LFS2_ASSERT((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY); if (size > LFS2_FILE_MAX) { - LFS2_TRACE("lfs2_file_truncate -> %d", LFS2_ERR_INVAL); return LFS2_ERR_INVAL; } lfs2_off_t pos = file->pos; - lfs2_off_t oldsize = lfs2_file_size(lfs2, file); + lfs2_off_t oldsize = lfs2_file_rawsize(lfs2, file); if (size < oldsize) { // need to flush since directly changing metadata int err = lfs2_file_flush(lfs2, file); if (err) { - LFS2_TRACE("lfs2_file_truncate -> %d", err); return err; } @@ -3060,7 +3100,6 @@ int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { file->ctz.head, file->ctz.size, size, &file->block, &file->off); if (err) { - LFS2_TRACE("lfs2_file_truncate -> %d", err); return err; } @@ -3070,97 +3109,80 @@ int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { } else if (size > oldsize) { // flush+seek if not already at end if (file->pos != oldsize) { - lfs2_soff_t res = lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_END); + lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_END); if (res < 0) { - LFS2_TRACE("lfs2_file_truncate -> %"PRId32, res); return (int)res; } } // fill with zeros while (file->pos < size) { - lfs2_ssize_t res = lfs2_file_write(lfs2, file, &(uint8_t){0}, 1); + lfs2_ssize_t res = lfs2_file_rawwrite(lfs2, file, &(uint8_t){0}, 1); if (res < 0) { - LFS2_TRACE("lfs2_file_truncate -> %"PRId32, res); return (int)res; } } } // restore pos - lfs2_soff_t res = lfs2_file_seek(lfs2, file, pos, LFS2_SEEK_SET); + lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, pos, LFS2_SEEK_SET); if (res < 0) { - LFS2_TRACE("lfs2_file_truncate -> %"PRId32, res); return (int)res; } - LFS2_TRACE("lfs2_file_truncate -> %d", 0); return 0; } +#endif -lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file) { - LFS2_TRACE("lfs2_file_tell(%p, %p)", (void*)lfs2, (void*)file); - LFS2_ASSERT(file->flags & LFS2_F_OPENED); +static lfs2_soff_t lfs2_file_rawtell(lfs2_t *lfs2, lfs2_file_t *file) { (void)lfs2; - LFS2_TRACE("lfs2_file_tell -> %"PRId32, file->pos); return file->pos; } -int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file) { - LFS2_TRACE("lfs2_file_rewind(%p, %p)", (void*)lfs2, (void*)file); - lfs2_soff_t res = lfs2_file_seek(lfs2, file, 0, LFS2_SEEK_SET); +static int lfs2_file_rawrewind(lfs2_t *lfs2, lfs2_file_t *file) { + lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_SET); if (res < 0) { - LFS2_TRACE("lfs2_file_rewind -> %"PRId32, res); return (int)res; } - LFS2_TRACE("lfs2_file_rewind -> %d", 0); return 0; } -lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file) { - LFS2_TRACE("lfs2_file_size(%p, %p)", (void*)lfs2, (void*)file); - LFS2_ASSERT(file->flags & LFS2_F_OPENED); +static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file) { (void)lfs2; + +#ifndef LFS2_READONLY if (file->flags & LFS2_F_WRITING) { - LFS2_TRACE("lfs2_file_size -> %"PRId32, - lfs2_max(file->pos, file->ctz.size)); return lfs2_max(file->pos, file->ctz.size); - } else { - LFS2_TRACE("lfs2_file_size -> %"PRId32, file->ctz.size); - return file->ctz.size; } +#endif + + return file->ctz.size; } /// General fs operations /// -int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { - LFS2_TRACE("lfs2_stat(%p, \"%s\", %p)", (void*)lfs2, path, (void*)info); +static int lfs2_rawstat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { lfs2_mdir_t cwd; lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); if (tag < 0) { - LFS2_TRACE("lfs2_stat -> %"PRId32, tag); return (int)tag; } - int err = lfs2_dir_getinfo(lfs2, &cwd, lfs2_tag_id(tag), info); - LFS2_TRACE("lfs2_stat -> %d", err); - return err; + return lfs2_dir_getinfo(lfs2, &cwd, lfs2_tag_id(tag), info); } -int lfs2_remove(lfs2_t *lfs2, const char *path) { - LFS2_TRACE("lfs2_remove(%p, \"%s\")", (void*)lfs2, path); +#ifndef LFS2_READONLY +static int lfs2_rawremove(lfs2_t *lfs2, const char *path) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { - LFS2_TRACE("lfs2_remove -> %d", err); return err; } lfs2_mdir_t cwd; lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); if (tag < 0 || lfs2_tag_id(tag) == 0x3ff) { - LFS2_TRACE("lfs2_remove -> %"PRId32, (tag < 0) ? tag : LFS2_ERR_INVAL); return (tag < 0) ? (int)tag : LFS2_ERR_INVAL; } @@ -3172,19 +3194,16 @@ int lfs2_remove(lfs2_t *lfs2, const char *path) { lfs2_stag_t res = lfs2_dir_get(lfs2, &cwd, LFS2_MKTAG(0x700, 0x3ff, 0), LFS2_MKTAG(LFS2_TYPE_STRUCT, lfs2_tag_id(tag), 8), pair); if (res < 0) { - LFS2_TRACE("lfs2_remove -> %"PRId32, res); return (int)res; } lfs2_pair_fromle32(pair); err = lfs2_dir_fetch(lfs2, &dir.m, pair); if (err) { - LFS2_TRACE("lfs2_remove -> %d", err); return err; } if (dir.m.count > 0 || dir.m.split) { - LFS2_TRACE("lfs2_remove -> %d", LFS2_ERR_NOTEMPTY); return LFS2_ERR_NOTEMPTY; } @@ -3200,10 +3219,9 @@ int lfs2_remove(lfs2_t *lfs2, const char *path) { // delete the entry err = lfs2_dir_commit(lfs2, &cwd, LFS2_MKATTRS( - {LFS2_MKTAG(LFS2_TYPE_DELETE, lfs2_tag_id(tag), 0)})); + {LFS2_MKTAG(LFS2_TYPE_DELETE, lfs2_tag_id(tag), 0), NULL})); if (err) { lfs2->mlist = dir.next; - LFS2_TRACE("lfs2_remove -> %d", err); return err; } @@ -3214,28 +3232,24 @@ int lfs2_remove(lfs2_t *lfs2, const char *path) { err = lfs2_fs_pred(lfs2, dir.m.pair, &cwd); if (err) { - LFS2_TRACE("lfs2_remove -> %d", err); return err; } err = lfs2_dir_drop(lfs2, &cwd, &dir.m); if (err) { - LFS2_TRACE("lfs2_remove -> %d", err); return err; } } - LFS2_TRACE("lfs2_remove -> %d", 0); return 0; } +#endif -int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { - LFS2_TRACE("lfs2_rename(%p, \"%s\", \"%s\")", (void*)lfs2, oldpath, newpath); - +#ifndef LFS2_READONLY +static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { - LFS2_TRACE("lfs2_rename -> %d", err); return err; } @@ -3243,8 +3257,6 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { lfs2_mdir_t oldcwd; lfs2_stag_t oldtag = lfs2_dir_find(lfs2, &oldcwd, &oldpath, NULL); if (oldtag < 0 || lfs2_tag_id(oldtag) == 0x3ff) { - LFS2_TRACE("lfs2_rename -> %"PRId32, - (oldtag < 0) ? oldtag : LFS2_ERR_INVAL); return (oldtag < 0) ? (int)oldtag : LFS2_ERR_INVAL; } @@ -3254,8 +3266,6 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { lfs2_stag_t prevtag = lfs2_dir_find(lfs2, &newcwd, &newpath, &newid); if ((prevtag < 0 || lfs2_tag_id(prevtag) == 0x3ff) && !(prevtag == LFS2_ERR_NOENT && newid != 0x3ff)) { - LFS2_TRACE("lfs2_rename -> %"PRId32, - (prevtag < 0) ? prevtag : LFS2_ERR_INVAL); return (prevtag < 0) ? (int)prevtag : LFS2_ERR_INVAL; } @@ -3269,7 +3279,6 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { // check that name fits lfs2_size_t nlen = strlen(newpath); if (nlen > lfs2->name_max) { - LFS2_TRACE("lfs2_rename -> %d", LFS2_ERR_NAMETOOLONG); return LFS2_ERR_NAMETOOLONG; } @@ -3280,11 +3289,9 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { newoldid += 1; } } else if (lfs2_tag_type3(prevtag) != lfs2_tag_type3(oldtag)) { - LFS2_TRACE("lfs2_rename -> %d", LFS2_ERR_ISDIR); return LFS2_ERR_ISDIR; } else if (samepair && newid == newoldid) { // we're renaming to ourselves?? - LFS2_TRACE("lfs2_rename -> %d", 0); return 0; } else if (lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR) { // must be empty before removal @@ -3292,7 +3299,6 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { lfs2_stag_t res = lfs2_dir_get(lfs2, &newcwd, LFS2_MKTAG(0x700, 0x3ff, 0), LFS2_MKTAG(LFS2_TYPE_STRUCT, newid, 8), prevpair); if (res < 0) { - LFS2_TRACE("lfs2_rename -> %"PRId32, res); return (int)res; } lfs2_pair_fromle32(prevpair); @@ -3300,12 +3306,10 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { // must be empty before removal err = lfs2_dir_fetch(lfs2, &prevdir.m, prevpair); if (err) { - LFS2_TRACE("lfs2_rename -> %d", err); return err; } if (prevdir.m.count > 0 || prevdir.m.split) { - LFS2_TRACE("lfs2_rename -> %d", LFS2_ERR_NOTEMPTY); return LFS2_ERR_NOTEMPTY; } @@ -3326,15 +3330,14 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { // move over all attributes err = lfs2_dir_commit(lfs2, &newcwd, LFS2_MKATTRS( {LFS2_MKTAG_IF(prevtag != LFS2_ERR_NOENT, - LFS2_TYPE_DELETE, newid, 0)}, - {LFS2_MKTAG(LFS2_TYPE_CREATE, newid, 0)}, + LFS2_TYPE_DELETE, newid, 0), NULL}, + {LFS2_MKTAG(LFS2_TYPE_CREATE, newid, 0), NULL}, {LFS2_MKTAG(lfs2_tag_type3(oldtag), newid, strlen(newpath)), newpath}, {LFS2_MKTAG(LFS2_FROM_MOVE, newid, lfs2_tag_id(oldtag)), &oldcwd}, {LFS2_MKTAG_IF(samepair, - LFS2_TYPE_DELETE, newoldid, 0)})); + LFS2_TYPE_DELETE, newoldid, 0), NULL})); if (err) { lfs2->mlist = prevdir.next; - LFS2_TRACE("lfs2_rename -> %d", err); return err; } @@ -3344,10 +3347,9 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { // prep gstate and delete move id lfs2_fs_prepmove(lfs2, 0x3ff, NULL); err = lfs2_dir_commit(lfs2, &oldcwd, LFS2_MKATTRS( - {LFS2_MKTAG(LFS2_TYPE_DELETE, lfs2_tag_id(oldtag), 0)})); + {LFS2_MKTAG(LFS2_TYPE_DELETE, lfs2_tag_id(oldtag), 0), NULL})); if (err) { lfs2->mlist = prevdir.next; - LFS2_TRACE("lfs2_rename -> %d", err); return err; } } @@ -3359,29 +3361,24 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { err = lfs2_fs_pred(lfs2, prevdir.m.pair, &newcwd); if (err) { - LFS2_TRACE("lfs2_rename -> %d", err); return err; } err = lfs2_dir_drop(lfs2, &newcwd, &prevdir.m); if (err) { - LFS2_TRACE("lfs2_rename -> %d", err); return err; } } - LFS2_TRACE("lfs2_rename -> %d", 0); return 0; } +#endif -lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, +static lfs2_ssize_t lfs2_rawgetattr(lfs2_t *lfs2, const char *path, uint8_t type, void *buffer, lfs2_size_t size) { - LFS2_TRACE("lfs2_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", - (void*)lfs2, path, type, buffer, size); lfs2_mdir_t cwd; lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); if (tag < 0) { - LFS2_TRACE("lfs2_getattr -> %"PRId32, tag); return tag; } @@ -3391,7 +3388,6 @@ lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, id = 0; int err = lfs2_dir_fetch(lfs2, &cwd, lfs2->root); if (err) { - LFS2_TRACE("lfs2_getattr -> %d", err); return err; } } @@ -3402,19 +3398,16 @@ lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, buffer); if (tag < 0) { if (tag == LFS2_ERR_NOENT) { - LFS2_TRACE("lfs2_getattr -> %d", LFS2_ERR_NOATTR); return LFS2_ERR_NOATTR; } - LFS2_TRACE("lfs2_getattr -> %"PRId32, tag); return tag; } - size = lfs2_tag_size(tag); - LFS2_TRACE("lfs2_getattr -> %"PRId32, size); - return size; + return lfs2_tag_size(tag); } +#ifndef LFS2_READONLY static int lfs2_commitattr(lfs2_t *lfs2, const char *path, uint8_t type, const void *buffer, lfs2_size_t size) { lfs2_mdir_t cwd; @@ -3436,27 +3429,24 @@ static int lfs2_commitattr(lfs2_t *lfs2, const char *path, return lfs2_dir_commit(lfs2, &cwd, LFS2_MKATTRS( {LFS2_MKTAG(LFS2_TYPE_USERATTR + type, id, size), buffer})); } +#endif -int lfs2_setattr(lfs2_t *lfs2, const char *path, +#ifndef LFS2_READONLY +static int lfs2_rawsetattr(lfs2_t *lfs2, const char *path, uint8_t type, const void *buffer, lfs2_size_t size) { - LFS2_TRACE("lfs2_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", - (void*)lfs2, path, type, buffer, size); if (size > lfs2->attr_max) { - LFS2_TRACE("lfs2_setattr -> %d", LFS2_ERR_NOSPC); return LFS2_ERR_NOSPC; } - int err = lfs2_commitattr(lfs2, path, type, buffer, size); - LFS2_TRACE("lfs2_setattr -> %d", err); - return err; + return lfs2_commitattr(lfs2, path, type, buffer, size); } +#endif -int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type) { - LFS2_TRACE("lfs2_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs2, path, type); - int err = lfs2_commitattr(lfs2, path, type, NULL, 0x3ff); - LFS2_TRACE("lfs2_removeattr -> %d", err); - return err; +#ifndef LFS2_READONLY +static int lfs2_rawremoveattr(lfs2_t *lfs2, const char *path, uint8_t type) { + return lfs2_commitattr(lfs2, path, type, NULL, 0x3ff); } +#endif /// Filesystem operations /// @@ -3584,28 +3574,12 @@ static int lfs2_deinit(lfs2_t *lfs2) { return 0; } -int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { - LFS2_TRACE("lfs2_format(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs2, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); +#ifndef LFS2_READONLY +static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { int err = 0; { err = lfs2_init(lfs2, cfg); if (err) { - LFS2_TRACE("lfs2_format -> %d", err); return err; } @@ -3636,7 +3610,7 @@ int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { lfs2_superblock_tole32(&superblock); err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS( - {LFS2_MKTAG(LFS2_TYPE_CREATE, 0, 0)}, + {LFS2_MKTAG(LFS2_TYPE_CREATE, 0, 0), NULL}, {LFS2_MKTAG(LFS2_TYPE_SUPERBLOCK, 0, 8), "littlefs"}, {LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), &superblock})); @@ -3661,30 +3635,14 @@ int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { cleanup: lfs2_deinit(lfs2); - LFS2_TRACE("lfs2_format -> %d", err); return err; -} -int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) { - LFS2_TRACE("lfs2_mount(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs2, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); +} +#endif + +static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { int err = lfs2_init(lfs2, cfg); if (err) { - LFS2_TRACE("lfs2_mount -> %d", err); return err; } @@ -3797,28 +3755,25 @@ int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) { lfs2->gstate.tag += !lfs2_tag_isvalid(lfs2->gstate.tag); lfs2->gdisk = lfs2->gstate; - // setup free lookahead - lfs2_alloc_reset(lfs2); + // setup free lookahead, to distribute allocations uniformly across + // boots, we start the allocator at a random location + lfs2->free.off = lfs2->seed % lfs2->cfg->block_count; + lfs2_alloc_drop(lfs2); - LFS2_TRACE("lfs2_mount -> %d", 0); return 0; cleanup: - lfs2_unmount(lfs2); - LFS2_TRACE("lfs2_mount -> %d", err); + lfs2_rawunmount(lfs2); return err; } -int lfs2_unmount(lfs2_t *lfs2) { - LFS2_TRACE("lfs2_unmount(%p)", (void*)lfs2); - int err = lfs2_deinit(lfs2); - LFS2_TRACE("lfs2_unmount -> %d", err); - return err; +static int lfs2_rawunmount(lfs2_t *lfs2) { + return lfs2_deinit(lfs2); } /// Filesystem filesystem operations /// -int lfs2_fs_traverseraw(lfs2_t *lfs2, +int lfs2_fs_rawtraverse(lfs2_t *lfs2, int (*cb)(void *data, lfs2_block_t block), void *data, bool includeorphans) { // iterate over metadata pairs @@ -3888,6 +3843,7 @@ int lfs2_fs_traverseraw(lfs2_t *lfs2, } } +#ifndef LFS2_READONLY // iterate over any open files for (lfs2_file_t *f = (lfs2_file_t*)lfs2->mlist; f; f = f->next) { if (f->type != LFS2_TYPE_REG) { @@ -3910,19 +3866,12 @@ int lfs2_fs_traverseraw(lfs2_t *lfs2, } } } +#endif return 0; } -int lfs2_fs_traverse(lfs2_t *lfs2, - int (*cb)(void *data, lfs2_block_t block), void *data) { - LFS2_TRACE("lfs2_fs_traverse(%p, %p, %p)", - (void*)lfs2, (void*)(uintptr_t)cb, data); - int err = lfs2_fs_traverseraw(lfs2, cb, data, true); - LFS2_TRACE("lfs2_fs_traverse -> %d", 0); - return err; -} - +#ifndef LFS2_READONLY static int lfs2_fs_pred(lfs2_t *lfs2, const lfs2_block_t pair[2], lfs2_mdir_t *pdir) { // iterate over all directory directory entries @@ -3948,12 +3897,16 @@ static int lfs2_fs_pred(lfs2_t *lfs2, return LFS2_ERR_NOENT; } +#endif +#ifndef LFS2_READONLY struct lfs2_fs_parent_match { lfs2_t *lfs2; const lfs2_block_t pair[2]; }; +#endif +#ifndef LFS2_READONLY static int lfs2_fs_parent_match(void *data, lfs2_tag_t tag, const void *buffer) { struct lfs2_fs_parent_match *find = data; @@ -3972,7 +3925,9 @@ static int lfs2_fs_parent_match(void *data, lfs2_pair_fromle32(child); return (lfs2_pair_cmp(child, find->pair) == 0) ? LFS2_CMP_EQ : LFS2_CMP_LT; } +#endif +#ifndef LFS2_READONLY static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t pair[2], lfs2_mdir_t *parent) { // use fetchmatch with callback to find pairs @@ -3999,7 +3954,9 @@ static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t pair[2], return LFS2_ERR_NOENT; } +#endif +#ifndef LFS2_READONLY static int lfs2_fs_relocate(lfs2_t *lfs2, const lfs2_block_t oldpair[2], lfs2_block_t newpair[2]) { // update internal root @@ -4050,7 +4007,7 @@ static int lfs2_fs_relocate(lfs2_t *lfs2, lfs2_pair_tole32(newpair); int err = lfs2_dir_commit(lfs2, &parent, LFS2_MKATTRS( {LFS2_MKTAG_IF(moveid != 0x3ff, - LFS2_TYPE_DELETE, moveid, 0)}, + LFS2_TYPE_DELETE, moveid, 0), NULL}, {tag, newpair})); lfs2_pair_fromle32(newpair); if (err) { @@ -4084,7 +4041,7 @@ static int lfs2_fs_relocate(lfs2_t *lfs2, lfs2_pair_tole32(newpair); err = lfs2_dir_commit(lfs2, &parent, LFS2_MKATTRS( {LFS2_MKTAG_IF(moveid != 0x3ff, - LFS2_TYPE_DELETE, moveid, 0)}, + LFS2_TYPE_DELETE, moveid, 0), NULL}, {LFS2_MKTAG(LFS2_TYPE_TAIL + parent.split, 0x3ff, 8), newpair})); lfs2_pair_fromle32(newpair); if (err) { @@ -4094,14 +4051,18 @@ static int lfs2_fs_relocate(lfs2_t *lfs2, return 0; } +#endif +#ifndef LFS2_READONLY static void lfs2_fs_preporphans(lfs2_t *lfs2, int8_t orphans) { LFS2_ASSERT(lfs2_tag_size(lfs2->gstate.tag) > 0 || orphans >= 0); lfs2->gstate.tag += orphans; lfs2->gstate.tag = ((lfs2->gstate.tag & ~LFS2_MKTAG(0x800, 0, 0)) | ((uint32_t)lfs2_gstate_hasorphans(&lfs2->gstate) << 31)); } +#endif +#ifndef LFS2_READONLY static void lfs2_fs_prepmove(lfs2_t *lfs2, uint16_t id, const lfs2_block_t pair[2]) { lfs2->gstate.tag = ((lfs2->gstate.tag & ~LFS2_MKTAG(0x7ff, 0x3ff, 0)) | @@ -4109,7 +4070,9 @@ static void lfs2_fs_prepmove(lfs2_t *lfs2, lfs2->gstate.pair[0] = (id != 0x3ff) ? pair[0] : 0; lfs2->gstate.pair[1] = (id != 0x3ff) ? pair[1] : 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_fs_demove(lfs2_t *lfs2) { if (!lfs2_gstate_hasmove(&lfs2->gdisk)) { return 0; @@ -4132,14 +4095,16 @@ static int lfs2_fs_demove(lfs2_t *lfs2) { uint16_t moveid = lfs2_tag_id(lfs2->gdisk.tag); lfs2_fs_prepmove(lfs2, 0x3ff, NULL); err = lfs2_dir_commit(lfs2, &movedir, LFS2_MKATTRS( - {LFS2_MKTAG(LFS2_TYPE_DELETE, moveid, 0)})); + {LFS2_MKTAG(LFS2_TYPE_DELETE, moveid, 0), NULL})); if (err) { return err; } return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_fs_deorphan(lfs2_t *lfs2) { if (!lfs2_gstate_hasorphans(&lfs2->gstate)) { return 0; @@ -4213,7 +4178,9 @@ static int lfs2_fs_deorphan(lfs2_t *lfs2) { lfs2_fs_preporphans(lfs2, -lfs2_gstate_getorphans(&lfs2->gstate)); return 0; } +#endif +#ifndef LFS2_READONLY static int lfs2_fs_forceconsistency(lfs2_t *lfs2) { int err = lfs2_fs_demove(lfs2); if (err) { @@ -4227,6 +4194,7 @@ static int lfs2_fs_forceconsistency(lfs2_t *lfs2) { return 0; } +#endif static int lfs2_fs_size_count(void *p, lfs2_block_t block) { (void)block; @@ -4235,16 +4203,13 @@ static int lfs2_fs_size_count(void *p, lfs2_block_t block) { return 0; } -lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2) { - LFS2_TRACE("lfs2_fs_size(%p)", (void*)lfs2); +static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2) { lfs2_size_t size = 0; - int err = lfs2_fs_traverseraw(lfs2, lfs2_fs_size_count, &size, false); + int err = lfs2_fs_rawtraverse(lfs2, lfs2_fs_size_count, &size, false); if (err) { - LFS2_TRACE("lfs2_fs_size -> %d", err); return err; } - LFS2_TRACE("lfs2_fs_size -> %d", err); return size; } @@ -4669,27 +4634,10 @@ static int lfs21_unmount(lfs2_t *lfs2) { } /// v1 migration /// -int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { - LFS2_TRACE("lfs2_migrate(%p, %p {.context=%p, " - ".read=%p, .prog=%p, .erase=%p, .sync=%p, " - ".read_size=%"PRIu32", .prog_size=%"PRIu32", " - ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " - ".lookahead_size=%"PRIu32", .read_buffer=%p, " - ".prog_buffer=%p, .lookahead_buffer=%p, " - ".name_max=%"PRIu32", .file_max=%"PRIu32", " - ".attr_max=%"PRIu32"})", - (void*)lfs2, (void*)cfg, cfg->context, - (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, - (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, - cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, - cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, - cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, - cfg->name_max, cfg->file_max, cfg->attr_max); +static int lfs2_rawmigrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { struct lfs21 lfs21; int err = lfs21_mount(lfs2, &lfs21, cfg); if (err) { - LFS2_TRACE("lfs2_migrate -> %d", err); return err; } @@ -4906,8 +4854,538 @@ int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { cleanup: lfs21_unmount(lfs2); - LFS2_TRACE("lfs2_migrate -> %d", err); return err; } #endif + + +/// Public API wrappers /// + +// Here we can add tracing/thread safety easily + +// Thread-safe wrappers if enabled +#ifdef LFS2_THREADSAFE +#define LFS2_LOCK(cfg) cfg->lock(cfg) +#define LFS2_UNLOCK(cfg) cfg->unlock(cfg) +#else +#define LFS2_LOCK(cfg) ((void)cfg, 0) +#define LFS2_UNLOCK(cfg) ((void)cfg) +#endif + +// Public API +#ifndef LFS2_READONLY +int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { + int err = LFS2_LOCK(cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_format(%p, %p {.context=%p, " + ".read=%p, .prog=%p, .erase=%p, .sync=%p, " + ".read_size=%"PRIu32", .prog_size=%"PRIu32", " + ".block_size=%"PRIu32", .block_count=%"PRIu32", " + ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".lookahead_size=%"PRIu32", .read_buffer=%p, " + ".prog_buffer=%p, .lookahead_buffer=%p, " + ".name_max=%"PRIu32", .file_max=%"PRIu32", " + ".attr_max=%"PRIu32"})", + (void*)lfs2, (void*)cfg, cfg->context, + (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, + (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, + cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, + cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, + cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, + cfg->name_max, cfg->file_max, cfg->attr_max); + + err = lfs2_rawformat(lfs2, cfg); + + LFS2_TRACE("lfs2_format -> %d", err); + LFS2_UNLOCK(cfg); + return err; +} +#endif + +int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) { + int err = LFS2_LOCK(cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_mount(%p, %p {.context=%p, " + ".read=%p, .prog=%p, .erase=%p, .sync=%p, " + ".read_size=%"PRIu32", .prog_size=%"PRIu32", " + ".block_size=%"PRIu32", .block_count=%"PRIu32", " + ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".lookahead_size=%"PRIu32", .read_buffer=%p, " + ".prog_buffer=%p, .lookahead_buffer=%p, " + ".name_max=%"PRIu32", .file_max=%"PRIu32", " + ".attr_max=%"PRIu32"})", + (void*)lfs2, (void*)cfg, cfg->context, + (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, + (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, + cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, + cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, + cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, + cfg->name_max, cfg->file_max, cfg->attr_max); + + err = lfs2_rawmount(lfs2, cfg); + + LFS2_TRACE("lfs2_mount -> %d", err); + LFS2_UNLOCK(cfg); + return err; +} + +int lfs2_unmount(lfs2_t *lfs2) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_unmount(%p)", (void*)lfs2); + + err = lfs2_rawunmount(lfs2); + + LFS2_TRACE("lfs2_unmount -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +#ifndef LFS2_READONLY +int lfs2_remove(lfs2_t *lfs2, const char *path) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_remove(%p, \"%s\")", (void*)lfs2, path); + + err = lfs2_rawremove(lfs2, path); + + LFS2_TRACE("lfs2_remove -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +#ifndef LFS2_READONLY +int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_rename(%p, \"%s\", \"%s\")", (void*)lfs2, oldpath, newpath); + + err = lfs2_rawrename(lfs2, oldpath, newpath); + + LFS2_TRACE("lfs2_rename -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_stat(%p, \"%s\", %p)", (void*)lfs2, path, (void*)info); + + err = lfs2_rawstat(lfs2, path, info); + + LFS2_TRACE("lfs2_stat -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, + uint8_t type, void *buffer, lfs2_size_t size) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", + (void*)lfs2, path, type, buffer, size); + + lfs2_ssize_t res = lfs2_rawgetattr(lfs2, path, type, buffer, size); + + LFS2_TRACE("lfs2_getattr -> %"PRId32, res); + LFS2_UNLOCK(lfs2->cfg); + return res; +} + +#ifndef LFS2_READONLY +int lfs2_setattr(lfs2_t *lfs2, const char *path, + uint8_t type, const void *buffer, lfs2_size_t size) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", + (void*)lfs2, path, type, buffer, size); + + err = lfs2_rawsetattr(lfs2, path, type, buffer, size); + + LFS2_TRACE("lfs2_setattr -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +#ifndef LFS2_READONLY +int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs2, path, type); + + err = lfs2_rawremoveattr(lfs2, path, type); + + LFS2_TRACE("lfs2_removeattr -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_open(%p, %p, \"%s\", %x)", + (void*)lfs2, (void*)file, path, flags); + LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + err = lfs2_file_rawopen(lfs2, file, path, flags); + + LFS2_TRACE("lfs2_file_open -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, + const char *path, int flags, + const struct lfs2_file_config *cfg) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_opencfg(%p, %p, \"%s\", %x, %p {" + ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", + (void*)lfs2, (void*)file, path, flags, + (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); + LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + err = lfs2_file_rawopencfg(lfs2, file, path, flags, cfg); + + LFS2_TRACE("lfs2_file_opencfg -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_close(%p, %p)", (void*)lfs2, (void*)file); + LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + err = lfs2_file_rawclose(lfs2, file); + + LFS2_TRACE("lfs2_file_close -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +#ifndef LFS2_READONLY +int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_sync(%p, %p)", (void*)lfs2, (void*)file); + LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + err = lfs2_file_rawsync(lfs2, file); + + LFS2_TRACE("lfs2_file_sync -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, + void *buffer, lfs2_size_t size) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_read(%p, %p, %p, %"PRIu32")", + (void*)lfs2, (void*)file, buffer, size); + LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + lfs2_ssize_t res = lfs2_file_rawread(lfs2, file, buffer, size); + + LFS2_TRACE("lfs2_file_read -> %"PRId32, res); + LFS2_UNLOCK(lfs2->cfg); + return res; +} + +#ifndef LFS2_READONLY +lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, + const void *buffer, lfs2_size_t size) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_write(%p, %p, %p, %"PRIu32")", + (void*)lfs2, (void*)file, buffer, size); + LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + lfs2_ssize_t res = lfs2_file_rawwrite(lfs2, file, buffer, size); + + LFS2_TRACE("lfs2_file_write -> %"PRId32, res); + LFS2_UNLOCK(lfs2->cfg); + return res; +} +#endif + +lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, + lfs2_soff_t off, int whence) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_seek(%p, %p, %"PRId32", %d)", + (void*)lfs2, (void*)file, off, whence); + LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, off, whence); + + LFS2_TRACE("lfs2_file_seek -> %"PRId32, res); + LFS2_UNLOCK(lfs2->cfg); + return res; +} + +#ifndef LFS2_READONLY +int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_truncate(%p, %p, %"PRIu32")", + (void*)lfs2, (void*)file, size); + LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + err = lfs2_file_rawtruncate(lfs2, file, size); + + LFS2_TRACE("lfs2_file_truncate -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_tell(%p, %p)", (void*)lfs2, (void*)file); + LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + lfs2_soff_t res = lfs2_file_rawtell(lfs2, file); + + LFS2_TRACE("lfs2_file_tell -> %"PRId32, res); + LFS2_UNLOCK(lfs2->cfg); + return res; +} + +int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_rewind(%p, %p)", (void*)lfs2, (void*)file); + + err = lfs2_file_rawrewind(lfs2, file); + + LFS2_TRACE("lfs2_file_rewind -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_file_size(%p, %p)", (void*)lfs2, (void*)file); + LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); + + lfs2_soff_t res = lfs2_file_rawsize(lfs2, file); + + LFS2_TRACE("lfs2_file_size -> %"PRId32, res); + LFS2_UNLOCK(lfs2->cfg); + return res; +} + +#ifndef LFS2_READONLY +int lfs2_mkdir(lfs2_t *lfs2, const char *path) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_mkdir(%p, \"%s\")", (void*)lfs2, path); + + err = lfs2_rawmkdir(lfs2, path); + + LFS2_TRACE("lfs2_mkdir -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} +#endif + +int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_dir_open(%p, %p, \"%s\")", (void*)lfs2, (void*)dir, path); + LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)dir)); + + err = lfs2_dir_rawopen(lfs2, dir, path); + + LFS2_TRACE("lfs2_dir_open -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_dir_close(%p, %p)", (void*)lfs2, (void*)dir); + + err = lfs2_dir_rawclose(lfs2, dir); + + LFS2_TRACE("lfs2_dir_close -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_dir_read(%p, %p, %p)", + (void*)lfs2, (void*)dir, (void*)info); + + err = lfs2_dir_rawread(lfs2, dir, info); + + LFS2_TRACE("lfs2_dir_read -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_dir_seek(%p, %p, %"PRIu32")", + (void*)lfs2, (void*)dir, off); + + err = lfs2_dir_rawseek(lfs2, dir, off); + + LFS2_TRACE("lfs2_dir_seek -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_dir_tell(%p, %p)", (void*)lfs2, (void*)dir); + + lfs2_soff_t res = lfs2_dir_rawtell(lfs2, dir); + + LFS2_TRACE("lfs2_dir_tell -> %"PRId32, res); + LFS2_UNLOCK(lfs2->cfg); + return res; +} + +int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_dir_rewind(%p, %p)", (void*)lfs2, (void*)dir); + + err = lfs2_dir_rawrewind(lfs2, dir); + + LFS2_TRACE("lfs2_dir_rewind -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_fs_size(%p)", (void*)lfs2); + + lfs2_ssize_t res = lfs2_fs_rawsize(lfs2); + + LFS2_TRACE("lfs2_fs_size -> %"PRId32, res); + LFS2_UNLOCK(lfs2->cfg); + return res; +} + +int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void *, lfs2_block_t), void *data) { + int err = LFS2_LOCK(lfs2->cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_fs_traverse(%p, %p, %p)", + (void*)lfs2, (void*)(uintptr_t)cb, data); + + err = lfs2_fs_rawtraverse(lfs2, cb, data, true); + + LFS2_TRACE("lfs2_fs_traverse -> %d", err); + LFS2_UNLOCK(lfs2->cfg); + return err; +} + +#ifdef LFS2_MIGRATE +int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { + int err = LFS2_LOCK(cfg); + if (err) { + return err; + } + LFS2_TRACE("lfs2_migrate(%p, %p {.context=%p, " + ".read=%p, .prog=%p, .erase=%p, .sync=%p, " + ".read_size=%"PRIu32", .prog_size=%"PRIu32", " + ".block_size=%"PRIu32", .block_count=%"PRIu32", " + ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".lookahead_size=%"PRIu32", .read_buffer=%p, " + ".prog_buffer=%p, .lookahead_buffer=%p, " + ".name_max=%"PRIu32", .file_max=%"PRIu32", " + ".attr_max=%"PRIu32"})", + (void*)lfs2, (void*)cfg, cfg->context, + (void*)(uintptr_t)cfg->read, (void*)(uintptr_t)cfg->prog, + (void*)(uintptr_t)cfg->erase, (void*)(uintptr_t)cfg->sync, + cfg->read_size, cfg->prog_size, cfg->block_size, cfg->block_count, + cfg->block_cycles, cfg->cache_size, cfg->lookahead_size, + cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, + cfg->name_max, cfg->file_max, cfg->attr_max); + + err = lfs2_rawmigrate(lfs2, cfg); + + LFS2_TRACE("lfs2_migrate -> %d", err); + LFS2_UNLOCK(cfg); + return err; +} +#endif diff --git a/lib/littlefs/lfs2.h b/lib/littlefs/lfs2.h index 5f4d7bd9d1..f3b66d76ff 100644 --- a/lib/littlefs/lfs2.h +++ b/lib/littlefs/lfs2.h @@ -9,6 +9,7 @@ #include #include +#include "lfs2_util.h" #ifdef __cplusplus extern "C" @@ -21,7 +22,7 @@ extern "C" // Software library version // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS2_VERSION 0x00020002 +#define LFS2_VERSION 0x00020003 #define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16)) #define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0)) @@ -123,20 +124,25 @@ enum lfs2_type { enum lfs2_open_flags { // open flags LFS2_O_RDONLY = 1, // Open a file as read only +#ifndef LFS2_READONLY LFS2_O_WRONLY = 2, // Open a file as write only LFS2_O_RDWR = 3, // Open a file as read and write LFS2_O_CREAT = 0x0100, // Create a file if it does not exist LFS2_O_EXCL = 0x0200, // Fail if a file already exists LFS2_O_TRUNC = 0x0400, // Truncate the existing file to zero size LFS2_O_APPEND = 0x0800, // Move to end of file on every write +#endif // internally used flags +#ifndef LFS2_READONLY LFS2_F_DIRTY = 0x010000, // File does not match storage LFS2_F_WRITING = 0x020000, // File has been written since last flush +#endif LFS2_F_READING = 0x040000, // File has been read since last flush - LFS2_F_ERRED = 0x080000, // An error occured during write +#ifndef LFS2_READONLY + LFS2_F_ERRED = 0x080000, // An error occurred during write +#endif LFS2_F_INLINE = 0x100000, // Currently inlined in directory entry - LFS2_F_OPENED = 0x200000, // File has been opened }; // File seek flags @@ -174,6 +180,16 @@ struct lfs2_config { // are propogated to the user. int (*sync)(const struct lfs2_config *c); +#ifdef LFS2_THREADSAFE + // Lock the underlying block device. Negative error codes + // are propogated to the user. + int (*lock)(const struct lfs2_config *c); + + // Unlock the underlying block device. Negative error codes + // are propogated to the user. + int (*unlock)(const struct lfs2_config *c); +#endif + // Minimum size of a block read. All read operations will be a // multiple of this value. lfs2_size_t read_size; @@ -399,6 +415,7 @@ typedef struct lfs2 { /// Filesystem functions /// +#ifndef LFS2_READONLY // Format a block device with the littlefs // // Requires a littlefs object and config struct. This clobbers the littlefs @@ -407,6 +424,7 @@ typedef struct lfs2 { // // Returns a negative error code on failure. int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *config); +#endif // Mounts a littlefs // @@ -426,12 +444,15 @@ int lfs2_unmount(lfs2_t *lfs2); /// General operations /// +#ifndef LFS2_READONLY // Removes a file or directory // // If removing a directory, the directory must be empty. // Returns a negative error code on failure. int lfs2_remove(lfs2_t *lfs2, const char *path); +#endif +#ifndef LFS2_READONLY // Rename or move a file or directory // // If the destination exists, it must match the source in type. @@ -439,6 +460,7 @@ int lfs2_remove(lfs2_t *lfs2, const char *path); // // Returns a negative error code on failure. int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath); +#endif // Find info about a file or directory // @@ -461,6 +483,7 @@ int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info); lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, uint8_t type, void *buffer, lfs2_size_t size); +#ifndef LFS2_READONLY // Set custom attributes // // Custom attributes are uniquely identified by an 8-bit type and limited @@ -470,13 +493,16 @@ lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, // Returns a negative error code on failure. int lfs2_setattr(lfs2_t *lfs2, const char *path, uint8_t type, const void *buffer, lfs2_size_t size); +#endif +#ifndef LFS2_READONLY // Removes a custom attribute // // If an attribute is not found, nothing happens. // // Returns a negative error code on failure. int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type); +#endif /// File operations /// @@ -525,6 +551,7 @@ int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file); lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, void *buffer, lfs2_size_t size); +#ifndef LFS2_READONLY // Write data to file // // Takes a buffer and size indicating the data to write. The file will not @@ -533,6 +560,7 @@ lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, // Returns the number of bytes written, or a negative error code on failure. lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, const void *buffer, lfs2_size_t size); +#endif // Change the position of the file // @@ -541,10 +569,12 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, lfs2_soff_t off, int whence); +#ifndef LFS2_READONLY // Truncates the size of the file to the specified size // // Returns a negative error code on failure. int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size); +#endif // Return the position of the file // @@ -567,10 +597,12 @@ lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file); /// Directory operations /// +#ifndef LFS2_READONLY // Create a directory // // Returns a negative error code on failure. int lfs2_mkdir(lfs2_t *lfs2, const char *path); +#endif // Open a directory // @@ -632,6 +664,7 @@ lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2); // Returns a negative error code on failure. int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); +#ifndef LFS2_READONLY #ifdef LFS2_MIGRATE // Attempts to migrate a previous version of littlefs // @@ -646,6 +679,7 @@ int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); // Returns a negative error code on failure. int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg); #endif +#endif #ifdef __cplusplus diff --git a/lib/mp-readline/readline.c b/lib/mp-readline/readline.c index 8cef7d4c10..b47204abac 100644 --- a/lib/mp-readline/readline.c +++ b/lib/mp-readline/readline.c @@ -261,7 +261,7 @@ int readline_process_char(int c) { #endif } else if (32 <= c) { // printable character - char lcp = rl.line->buf[rl.cursor_pos]; + uint8_t lcp = rl.line->buf[rl.cursor_pos]; uint8_t cont_need = 0; if (!UTF8_IS_CONT(c)) { // ASCII or Lead code point diff --git a/lib/timeutils/timeutils.c b/lib/timeutils/timeutils.c index 5991df1dc1..e77daeb707 100644 --- a/lib/timeutils/timeutils.c +++ b/lib/timeutils/timeutils.c @@ -4,7 +4,7 @@ * The MIT License (MIT) * * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George - * Copyright (c) 2015 Daniel Campora + * SPDX-FileCopyrightText: Copyright (c) 2015 Daniel Campora * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -158,18 +158,7 @@ mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, + (year - 2000) * 31536000; } -void timeutils_seconds_since_epoch_to_struct_time(mp_uint_t t, timeutils_struct_time_t *tm) { - t -= TIMEUTILS_SECONDS_1970_TO_2000; - timeutils_seconds_since_2000_to_struct_time(t, tm); -} - -mp_uint_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, mp_uint_t date, - mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { - mp_uint_t t = timeutils_seconds_since_2000(year, month, date, hour, minute, second); - return t + TIMEUTILS_SECONDS_1970_TO_2000; -} - -mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, +mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { // Normalize the tuple. This allows things like: @@ -222,5 +211,5 @@ mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, year++; } } - return timeutils_seconds_since_epoch(year, month, mday, hours, minutes, seconds); + return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds); } diff --git a/lib/timeutils/timeutils.h b/lib/timeutils/timeutils.h index 3100b95cb9..ac93e108a3 100644 --- a/lib/timeutils/timeutils.h +++ b/lib/timeutils/timeutils.h @@ -4,7 +4,7 @@ * The MIT License (MIT) * * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George - * Copyright (c) 2015 Daniel Campora + * SPDX-FileCopyrightText: Copyright (c) 2015 Daniel Campora * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -42,14 +42,6 @@ typedef struct _timeutils_struct_time_t { uint16_t tm_yday; // 1..366 } timeutils_struct_time_t; -static inline uint64_t timeutils_seconds_since_2000_to_nanoseconds_since_1970(mp_uint_t s) { - return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL; -} - -static inline mp_uint_t timeutils_seconds_since_2000_from_nanoseconds_since_1970(uint64_t ns) { - return ns / 1000000000ULL - TIMEUTILS_SECONDS_1970_TO_2000; -} - bool timeutils_is_leap_year(mp_uint_t year); mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month); mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date); @@ -60,18 +52,52 @@ void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second); -void timeutils_seconds_since_epoch_to_struct_time(mp_uint_t t, timeutils_struct_time_t *tm); - -mp_uint_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, mp_uint_t date, - mp_uint_t hour, mp_uint_t minute, mp_uint_t second); - -mp_uint_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, +mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds); -static inline uint64_t timeutils_nanoseconds_since_1970(mp_uint_t year, mp_uint_t month, - mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { - return timeutils_seconds_since_2000_to_nanoseconds_since_1970( - timeutils_seconds_since_2000(year, month, date, hour, minute, second)); +// Select the Epoch used by the port. +#if MICROPY_EPOCH_IS_1970 + +static inline void timeutils_seconds_since_epoch_to_struct_time(uint64_t t, timeutils_struct_time_t *tm) { + // TODO this will give incorrect results for dates before 2000/1/1 + return timeutils_seconds_since_2000_to_struct_time(t - TIMEUTILS_SECONDS_1970_TO_2000, tm); } +static inline uint64_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + return timeutils_mktime_2000(year, month, mday, hours, minutes, seconds) + TIMEUTILS_SECONDS_1970_TO_2000; +} + +static inline uint64_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, + mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return timeutils_seconds_since_2000(year, month, date, hour, minute, second) + TIMEUTILS_SECONDS_1970_TO_2000; +} + +static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { + return ns / 1000000000ULL; +} + +static inline uint64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(uint64_t ns) { + return ns; +} + +#else // Epoch is 2000 + +#define timeutils_seconds_since_epoch_to_struct_time timeutils_seconds_since_2000_to_struct_time +#define timeutils_seconds_since_epoch timeutils_seconds_since_2000 +#define timeutils_mktime timeutils_mktime_2000 + +static inline uint64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_uint_t s) { + return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL; +} + +static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { + return ns / 1000000000ULL - TIMEUTILS_SECONDS_1970_TO_2000; +} + +static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(int64_t ns) { + return ns + TIMEUTILS_SECONDS_1970_TO_2000 * 1000000000ULL; +} + +#endif + #endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H diff --git a/lib/utils/mpirq.c b/lib/utils/mpirq.c index 663be18224..02139f24dc 100644 --- a/lib/utils/mpirq.c +++ b/lib/utils/mpirq.c @@ -53,12 +53,16 @@ const mp_arg_t mp_irq_init_args[] = { mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent) { mp_irq_obj_t *self = m_new0(mp_irq_obj_t, 1); + mp_irq_init(self, methods, parent); + return self; +} + +void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent) { self->base.type = &mp_irq_type; self->methods = (mp_irq_methods_t *)methods; self->parent = parent; self->handler = mp_const_none; self->ishard = false; - return self; } void mp_irq_handler(mp_irq_obj_t *self) { diff --git a/lib/utils/mpirq.h b/lib/utils/mpirq.h index 548185b531..dd423c0101 100644 --- a/lib/utils/mpirq.h +++ b/lib/utils/mpirq.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H #define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H +#include "py/runtime.h" + /****************************************************************************** DEFINE CONSTANTS ******************************************************************************/ @@ -41,20 +43,17 @@ enum { DEFINE TYPES ******************************************************************************/ -typedef mp_obj_t (*mp_irq_init_t)(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -typedef mp_uint_t (*mp_irq_uint_method_one_uint_para_t)(mp_obj_t self, mp_uint_t trigger); -typedef mp_uint_t (*mp_irq_int_method_one_para_t)(mp_obj_t self, mp_uint_t info_type); +typedef mp_uint_t (*mp_irq_trigger_fun_t)(mp_obj_t self, mp_uint_t trigger); +typedef mp_uint_t (*mp_irq_info_fun_t)(mp_obj_t self, mp_uint_t info_type); enum { MP_IRQ_INFO_FLAGS, MP_IRQ_INFO_TRIGGERS, - MP_IRQ_INFO_CNT }; typedef struct _mp_irq_methods_t { - mp_irq_init_t init; - mp_irq_uint_method_one_uint_para_t trigger; - mp_irq_int_method_one_para_t info; + mp_irq_trigger_fun_t trigger; + mp_irq_info_fun_t info; } mp_irq_methods_t; typedef struct _mp_irq_obj_t { @@ -77,6 +76,7 @@ extern const mp_obj_type_t mp_irq_type; ******************************************************************************/ mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent); +void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent); void mp_irq_handler(mp_irq_obj_t *self); #endif // MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H diff --git a/lib/utils/pyexec.c b/lib/utils/pyexec.c index eff68dd766..8bd027cf2c 100644 --- a/lib/utils/pyexec.c +++ b/lib/utils/pyexec.c @@ -57,6 +57,7 @@ STATIC bool repl_display_debugging_info = 0; #define EXEC_FLAG_SOURCE_IS_RAW_CODE (8) #define EXEC_FLAG_SOURCE_IS_VSTR (16) #define EXEC_FLAG_SOURCE_IS_FILENAME (32) +#define EXEC_FLAG_SOURCE_IS_READER (64) // parses, compiles and executes the code in the lexer // frees the lexer before returning @@ -69,10 +70,15 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input uint32_t start = 0; #endif + #ifdef MICROPY_BOARD_BEFORE_PYTHON_EXEC + MICROPY_BOARD_BEFORE_PYTHON_EXEC(input_kind, exec_flags); + #endif + // by default a SystemExit exception returns 0 pyexec_system_exit = 0; nlr_buf_t nlr; + nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { mp_obj_t module_fun; #if MICROPY_MODULE_FROZEN_MPY @@ -87,6 +93,8 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { const vstr_t *vstr = source; lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); + } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { + lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source); } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { lex = mp_lexer_new_from_file(source); } else { @@ -130,6 +138,12 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input // uncaught exception mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(false); // clear any pending exceptions (and run any callbacks) + + if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { + const mp_reader_t *reader = source; + reader->close(reader->data); + } + // print EOF after normal output if (exec_flags & EXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); @@ -192,10 +206,107 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_hal_stdout_tx_strn("\x04", 1); } + #ifdef MICROPY_BOARD_AFTER_PYTHON_EXEC + MICROPY_BOARD_AFTER_PYTHON_EXEC(input_kind, exec_flags, nlr.ret_val, &ret); + #endif + return ret; } #if MICROPY_ENABLE_COMPILER + +// This can be configured by a port (and even configured to a function to be +// computed dynamically) to indicate the maximum number of bytes that can be +// held in the stdin buffer. +#ifndef MICROPY_REPL_STDIN_BUFFER_MAX +#define MICROPY_REPL_STDIN_BUFFER_MAX (256) +#endif + +typedef struct _mp_reader_stdin_t { + bool eof; + uint16_t window_max; + uint16_t window_remain; +} mp_reader_stdin_t; + +STATIC mp_uint_t mp_reader_stdin_readbyte(void *data) { + mp_reader_stdin_t *reader = (mp_reader_stdin_t *)data; + + if (reader->eof) { + return MP_READER_EOF; + } + + int c = mp_hal_stdin_rx_chr(); + + if (c == CHAR_CTRL_C || c == CHAR_CTRL_D) { + reader->eof = true; + mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host + if (c == CHAR_CTRL_C) { + #if MICROPY_KBD_EXCEPTION + MP_STATE_VM(mp_kbd_exception).traceback_data = NULL; + nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception))); + #else + mp_raise_type(&mp_type_KeyboardInterrupt); + #endif + } else { + return MP_READER_EOF; + } + } + + if (--reader->window_remain == 0) { + mp_hal_stdout_tx_strn("\x01", 1); // indicate window available to host + reader->window_remain = reader->window_max; + } + + return c; +} + +STATIC void mp_reader_stdin_close(void *data) { + mp_reader_stdin_t *reader = (mp_reader_stdin_t *)data; + if (!reader->eof) { + reader->eof = true; + mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host + for (;;) { + int c = mp_hal_stdin_rx_chr(); + if (c == CHAR_CTRL_C || c == CHAR_CTRL_D) { + break; + } + } + } +} + +STATIC void mp_reader_new_stdin(mp_reader_t *reader, mp_reader_stdin_t *reader_stdin, uint16_t buf_max) { + // Make flow-control window half the buffer size, and indicate to the host that 2x windows are + // free (sending the window size implicitly indicates that a window is free, and then the 0x01 + // indicates that another window is free). + size_t window = buf_max / 2; + char reply[3] = { window & 0xff, window >> 8, 0x01 }; + mp_hal_stdout_tx_strn(reply, sizeof(reply)); + + reader_stdin->eof = false; + reader_stdin->window_max = window; + reader_stdin->window_remain = window; + reader->data = reader_stdin; + reader->readbyte = mp_reader_stdin_readbyte; + reader->close = mp_reader_stdin_close; +} + +STATIC int do_reader_stdin(int c) { + if (c != 'A') { + // Unsupported command. + mp_hal_stdout_tx_strn("R\x00", 2); + return 0; + } + + // Indicate reception of command. + mp_hal_stdout_tx_strn("R\x01", 2); + + mp_reader_t reader; + mp_reader_stdin_t reader_stdin; + mp_reader_new_stdin(&reader, &reader_stdin, MICROPY_REPL_STDIN_BUFFER_MAX); + int exec_flags = EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_READER; + return parse_compile_execute(&reader, MP_PARSE_FILE_INPUT, exec_flags, NULL); +} + #if MICROPY_REPL_EVENT_DRIVEN typedef struct _repl_t { @@ -229,6 +340,13 @@ void pyexec_event_repl_init(void) { STATIC int pyexec_raw_repl_process_char(int c) { if (c == CHAR_CTRL_A) { // reset raw REPL + if (vstr_len(MP_STATE_VM(repl_line)) == 2 && vstr_str(MP_STATE_VM(repl_line))[0] == CHAR_CTRL_E) { + int ret = do_reader_stdin(vstr_str(MP_STATE_VM(repl_line))[1]); + if (ret & PYEXEC_FORCED_EXIT) { + return ret; + } + goto reset; + } mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n"); goto reset; } else if (c == CHAR_CTRL_B) { @@ -413,6 +531,15 @@ raw_repl_reset: int c = mp_hal_stdin_rx_chr(); if (c == CHAR_CTRL_A) { // reset raw REPL + if (vstr_len(&line) == 2 && vstr_str(&line)[0] == CHAR_CTRL_E) { + int ret = do_reader_stdin(vstr_str(&line)[1]); + if (ret & PYEXEC_FORCED_EXIT) { + return ret; + } + vstr_reset(&line); + mp_hal_stdout_tx_str(">"); + continue; + } goto raw_repl_reset; } else if (c == CHAR_CTRL_B) { // change to friendly REPL @@ -453,12 +580,6 @@ int pyexec_friendly_repl(void) { vstr_t line; vstr_init(&line, 32); - #if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD - // in host mode, we enable the LCD for the repl - mp_obj_t lcd_o = mp_call_function_0(mp_load_name(qstr_from_str("LCD"))); - mp_call_function_1(mp_load_attr(lcd_o, qstr_from_str("light")), mp_const_true); - #endif - friendly_repl_reset: mp_hal_stdout_tx_str("\r\n"); mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index dd88cbe153..c04405b99f 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -3477,6 +3477,10 @@ msgstr "" msgid "name not defined" msgstr "" +#: py/asmthumb.c +msgid "native method too big" +msgstr "" + #: py/emitnative.c msgid "native yield" msgstr "" @@ -3518,6 +3522,10 @@ msgstr "" msgid "no default packer" msgstr "" +#: extmod/modurandom.c +msgid "no default seed" +msgstr "" + #: py/builtinimport.c msgid "no module named '%q'" msgstr "" @@ -4086,6 +4094,10 @@ msgstr "" msgid "too many indices" msgstr "" +#: py/asmthumb.c +msgid "too many locals for native method" +msgstr "" + #: py/runtime.c #, c-format msgid "too many values to unpack (expected %d)" diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index d5f77494e4..aacfeabdda 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -9,6 +9,14 @@ #define MICROPY_PERSISTENT_CODE_LOAD (0) #define MICROPY_PERSISTENT_CODE_SAVE (1) +#ifndef MICROPY_PERSISTENT_CODE_SAVE_FILE +#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) || defined(__unix__) || defined(__APPLE__) +#define MICROPY_PERSISTENT_CODE_SAVE_FILE (1) +#else +#define MICROPY_PERSISTENT_CODE_SAVE_FILE (0) +#endif +#endif + #define MICROPY_EMIT_X64 (1) #define MICROPY_EMIT_X86 (1) #define MICROPY_EMIT_THUMB (1) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 8f6a5eb63c..70aefad412 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -34,7 +34,7 @@ INC += -I$(BUILD) # compiler settings CWARN = -Wall -Werror -CWARN += -Wpointer-arith -Wuninitialized -Wdouble-promotion -Wsign-compare -Wfloat-conversion +CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(CFLAGS_MOD) $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) # Debugging/Optimization @@ -42,10 +42,12 @@ ifdef DEBUG COPT ?= -O0 else COPT ?= -Os -COPT += -fdata-sections -ffunction-sections COPT += -DNDEBUG endif +# Remove unused sections. +COPT += -fdata-sections -ffunction-sections + # Always enable symbols -- They're occasionally useful, and don't make it into the # final .bin/.hex/.dfu so the extra size doesn't matter. CFLAGS += -g @@ -168,7 +170,6 @@ SRC_C += \ modtime.c \ moduselect.c \ alloc.c \ - coverage.c \ fatfs_port.c \ supervisor/stub/filesystem.c \ supervisor/stub/safe_mode.c \ @@ -184,13 +185,17 @@ LIB_SRC_C += $(addprefix lib/,\ utils/gchelper_generic.c \ ) +SRC_CXX += \ + $(SRC_MOD_CXX) + OBJ = $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o)) OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) # List of sources for qstr extraction -SRC_QSTR += $(SRC_C) $(LIB_SRC_C) $(EXTMOD_SRC_C) +SRC_QSTR += $(SRC_C) $(SRC_CXX) $(LIB_SRC_C) $(EXTMOD_SRC_C) # Append any auto-generated sources that are needed by sources listed in # SRC_QSTR SRC_QSTR_AUTO_DEPS += @@ -203,6 +208,14 @@ CFLAGS += -DMPZ_DIG_SIZE=16 # force 16 bits to work on both 32 and 64 bit archs MPY_CROSS_FLAGS += -mcache-lookup-bc endif +HASCPP17 = $(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 7) +ifeq ($(HASCPP17), 1) + CXXFLAGS += -std=c++17 +else + CXXFLAGS += -std=c++11 +endif +CXXFLAGS += $(filter-out -Wmissing-prototypes -Wold-style-definition -std=gnu99,$(CFLAGS) $(CXXFLAGS_MOD)) + ifeq ($(MICROPY_FORCE_32BIT),1) RUN_TESTS_MPY_CROSS_FLAGS = --mpy-cross-flags='-mcache-lookup-bc -march=x86' else diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 372943b894..8df8fd5f1e 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -188,7 +188,7 @@ STATIC mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%ld\n", 123); // long mp_printf(&mp_plat_print, "%lx\n", 0x123); // long hex mp_printf(&mp_plat_print, "%X\n", 0x1abcdef); // capital hex - mp_printf(&mp_plat_print, "%.2s %.3s\n", "abc", "abc"); // fixed string precision + mp_printf(&mp_plat_print, "%.2s %.3s '%4.4s' '%5.5q' '%.3q'\n", "abc", "abc", "abc", MP_QSTR_True, MP_QSTR_True); // fixed string precision mp_printf(&mp_plat_print, "%.*s\n", -1, "abc"); // negative string precision mp_printf(&mp_plat_print, "%b %b\n", 0, 1); // bools #ifndef NDEBUG diff --git a/ports/unix/coveragecpp.cpp b/ports/unix/coveragecpp.cpp new file mode 100644 index 0000000000..ea7418e1dd --- /dev/null +++ b/ports/unix/coveragecpp.cpp @@ -0,0 +1,23 @@ +extern "C" { +#include "py/obj.h" +} + +#if defined(MICROPY_UNIX_COVERAGE) + +// Just to test building of C++ code. +STATIC mp_obj_t extra_cpp_coverage_impl() { + return mp_const_none; +} + +extern "C" { +mp_obj_t extra_cpp_coverage(void); +mp_obj_t extra_cpp_coverage(void) { + return extra_cpp_coverage_impl(); +} + +// This is extern to avoid name mangling. +extern const mp_obj_fun_builtin_fixed_t extra_cpp_coverage_obj = {{&mp_type_fun_builtin_0}, {extra_cpp_coverage}}; + +} + +#endif diff --git a/ports/unix/main.c b/ports/unix/main.c index 08c50d5641..34aae65cbb 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -132,7 +132,7 @@ STATIC int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu // allow to print the parse tree in the coverage build if (mp_verbose_flag >= 3) { printf("----------------\n"); - mp_parse_node_print(parse_tree.root, 0); + mp_parse_node_print(&mp_plat_print, parse_tree.root, 0); printf("----------------\n"); } #endif @@ -534,7 +534,9 @@ MP_NOINLINE int main_(int argc, char **argv) { #if defined(MICROPY_UNIX_COVERAGE) { MP_DECLARE_CONST_FUN_OBJ_0(extra_coverage_obj); - mp_store_global(QSTR_FROM_STR_STATIC("extra_coverage"), MP_OBJ_FROM_PTR(&extra_coverage_obj)); + MP_DECLARE_CONST_FUN_OBJ_0(extra_cpp_coverage_obj); + mp_store_global(MP_QSTR_extra_coverage, MP_OBJ_FROM_PTR(&extra_coverage_obj)); + mp_store_global(MP_QSTR_extra_cpp_coverage, MP_OBJ_FROM_PTR(&extra_cpp_coverage_obj)); } #endif @@ -547,9 +549,9 @@ MP_NOINLINE int main_(int argc, char **argv) { // test_obj.attr = 42 // // mp_obj_t test_class_type, test_class_instance; - // test_class_type = mp_obj_new_type(QSTR_FROM_STR_STATIC("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); - // mp_store_name(QSTR_FROM_STR_STATIC("test_obj"), test_class_instance = mp_call_function_0(test_class_type)); - // mp_store_attr(test_class_instance, QSTR_FROM_STR_STATIC("attr"), mp_obj_new_int(42)); + // test_class_type = mp_obj_new_type(qstr_from_str("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); + // mp_store_name(qstr_from_str("test_obj"), test_class_instance = mp_call_function_0(test_class_type)); + // mp_store_attr(test_class_instance, qstr_from_str("attr"), mp_obj_new_int(42)); /* printf("bytes:\n"); diff --git a/ports/unix/modmachine.c b/ports/unix/modmachine.c index e139605b29..04ff325eef 100644 --- a/ports/unix/modmachine.c +++ b/ports/unix/modmachine.c @@ -49,7 +49,7 @@ #if MICROPY_PY_MACHINE uintptr_t mod_machine_mem_get_addr(mp_obj_t addr_o, uint align) { - uintptr_t addr = mp_obj_int_get_truncated(addr_o); + uintptr_t addr = mp_obj_get_int_truncated(addr_o); if ((addr & (align - 1)) != 0) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("address %08x is not aligned to %d bytes"), addr, align); } diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index 188f24db83..e72c611d1b 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -67,7 +67,7 @@ static inline int msec_sleep_tv(struct timeval *tv) { #endif STATIC mp_obj_t mod_time_time(void) { - #if MICROPY_PY_BUILTINS_FLOAT + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE struct timeval tv; gettimeofday(&tv, NULL); mp_float_t val = tv.tv_sec + (mp_float_t)tv.tv_usec / 1000000; @@ -132,19 +132,19 @@ STATIC mp_obj_t mod_time_sleep(mp_obj_t arg) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_time_sleep_obj, mod_time_sleep); -STATIC mp_obj_t mod_time_localtime(size_t n_args, const mp_obj_t *args) { +STATIC mp_obj_t mod_time_gm_local_time(size_t n_args, const mp_obj_t *args, struct tm *(*time_func)(const time_t *timep)) { time_t t; if (n_args == 0) { t = time(NULL); } else { - #if MICROPY_PY_BUILTINS_FLOAT + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE mp_float_t val = mp_obj_get_float(args[0]); t = (time_t)MICROPY_FLOAT_C_FUN(trunc)(val); #else t = mp_obj_get_int(args[0]); #endif } - struct tm *tm = localtime(&t); + struct tm *tm = time_func(&t); mp_obj_t ret = mp_obj_new_tuple(9, NULL); @@ -165,6 +165,15 @@ STATIC mp_obj_t mod_time_localtime(size_t n_args, const mp_obj_t *args) { return ret; } + +STATIC mp_obj_t mod_time_gmtime(size_t n_args, const mp_obj_t *args) { + return mod_time_gm_local_time(n_args, args, gmtime); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_time_gmtime_obj, 0, 1, mod_time_gmtime); + +STATIC mp_obj_t mod_time_localtime(size_t n_args, const mp_obj_t *args) { + return mod_time_gm_local_time(n_args, args, localtime); +} STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_time_localtime_obj, 0, 1, mod_time_localtime); STATIC mp_obj_t mod_time_mktime(mp_obj_t tuple) { @@ -210,6 +219,8 @@ STATIC const mp_rom_map_elem_t mp_module_time_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, + { MP_ROM_QSTR(MP_QSTR_time_ns), MP_ROM_PTR(&mp_utime_time_ns_obj) }, + { MP_ROM_QSTR(MP_QSTR_gmtime), MP_ROM_PTR(&mod_time_gmtime_obj) }, { MP_ROM_QSTR(MP_QSTR_localtime), MP_ROM_PTR(&mod_time_localtime_obj) }, { MP_ROM_QSTR(MP_QSTR_mktime), MP_ROM_PTR(&mod_time_mktime_obj) }, }; diff --git a/ports/unix/mpbthciport.c b/ports/unix/mpbthciport.c new file mode 100644 index 0000000000..14afbebcd7 --- /dev/null +++ b/ports/unix/mpbthciport.c @@ -0,0 +1,285 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && (MICROPY_BLUETOOTH_NIMBLE || (MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4)) + +#if !MICROPY_PY_THREAD +#error Unix HCI UART requires MICROPY_PY_THREAD +#endif + +#include "extmod/modbluetooth.h" +#include "extmod/mpbthci.h" + +#include +#include + +#include +#include +#include +#include + +#define DEBUG_printf(...) // printf(__VA_ARGS__) +#define DEBUG_HCI_DUMP (0) + +uint8_t mp_bluetooth_hci_cmd_buf[4 + 256]; + +STATIC int uart_fd = -1; + +// Must be provided by the stack bindings (e.g. mpnimbleport.c or mpbtstackport.c). +extern bool mp_bluetooth_hci_poll(void); + +#if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS + +// For synchronous mode, we run all BLE stack code inside a scheduled task. +// This task is scheduled periodically (every 1ms) by a background thread. + +// Allows the stack to tell us that we should stop trying to schedule. +extern bool mp_bluetooth_hci_active(void); + +// Prevent double-enqueuing of the scheduled task. +STATIC volatile bool events_task_is_scheduled = false; + +STATIC mp_obj_t run_events_scheduled_task(mp_obj_t none_in) { + (void)none_in; + MICROPY_PY_BLUETOOTH_ENTER + events_task_is_scheduled = false; + MICROPY_PY_BLUETOOTH_EXIT + mp_bluetooth_hci_poll(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(run_events_scheduled_task_obj, run_events_scheduled_task); + +#endif // MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS + +STATIC const useconds_t UART_POLL_INTERVAL_US = 1000; +STATIC pthread_t hci_poll_thread_id; + +STATIC void *hci_poll_thread(void *arg) { + (void)arg; + + DEBUG_printf("hci_poll_thread: starting\n"); + + #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS + + events_task_is_scheduled = false; + + while (mp_bluetooth_hci_active()) { + MICROPY_PY_BLUETOOTH_ENTER + if (!events_task_is_scheduled) { + events_task_is_scheduled = mp_sched_schedule(MP_OBJ_FROM_PTR(&run_events_scheduled_task_obj), mp_const_none); + } + MICROPY_PY_BLUETOOTH_EXIT + usleep(UART_POLL_INTERVAL_US); + } + + #else + + // In asynchronous (i.e. ringbuffer) mode, we run the BLE stack directly from the thread. + // This will return false when the stack is shutdown. + while (mp_bluetooth_hci_poll()) { + usleep(UART_POLL_INTERVAL_US); + } + + #endif + + DEBUG_printf("hci_poll_thread: stopped\n"); + + return NULL; +} + +STATIC int configure_uart(void) { + struct termios toptions; + + // Get existing config. + if (tcgetattr(uart_fd, &toptions) < 0) { + DEBUG_printf("Couldn't get term attributes"); + return -1; + } + + // Raw mode (disable all processing). + cfmakeraw(&toptions); + + // 8N1, no parity. + toptions.c_cflag &= ~CSTOPB; + toptions.c_cflag |= CS8; + toptions.c_cflag &= ~PARENB; + + // Enable receiver, ignore modem control lines + toptions.c_cflag |= CREAD | CLOCAL; + + // Blocking, single-byte reads. + toptions.c_cc[VMIN] = 1; + toptions.c_cc[VTIME] = 0; + + // Enable HW RTS/CTS flow control. + toptions.c_iflag &= ~(IXON | IXOFF | IXANY); + toptions.c_cflag |= CRTSCTS; + + // 1Mbit (TODO: make this configurable). + speed_t brate = B1000000; + cfsetospeed(&toptions, brate); + cfsetispeed(&toptions, brate); + + // Apply immediately. + if (tcsetattr(uart_fd, TCSANOW, &toptions) < 0) { + DEBUG_printf("Couldn't set term attributes"); + + close(uart_fd); + uart_fd = -1; + + return -1; + } + + return 0; +} + +// HCI UART bindings. +int mp_bluetooth_hci_uart_init(uint32_t port, uint32_t baudrate) { + (void)port; + (void)baudrate; + + DEBUG_printf("mp_bluetooth_hci_uart_init (unix)\n"); + + if (uart_fd != -1) { + DEBUG_printf("mp_bluetooth_hci_uart_init: already active\n"); + return 0; + } + + char uart_device_name[256] = "/dev/ttyUSB0"; + + char *path = getenv("MICROPYBTUART"); + if (path != NULL) { + strcpy(uart_device_name, path); + } + DEBUG_printf("mp_bluetooth_hci_uart_init: Using HCI UART: %s\n", uart_device_name); + + int flags = O_RDWR | O_NOCTTY | O_NONBLOCK; + uart_fd = open(uart_device_name, flags); + if (uart_fd == -1) { + printf("mp_bluetooth_hci_uart_init: Unable to open port %s\n", uart_device_name); + return -1; + } + + if (configure_uart()) { + return -1; + } + + // Create a thread to run the polling loop. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&hci_poll_thread_id, &attr, &hci_poll_thread, NULL); + + return 0; +} + +int mp_bluetooth_hci_uart_deinit(void) { + DEBUG_printf("mp_bluetooth_hci_uart_deinit\n"); + + if (uart_fd == -1) { + return 0; + } + + // Wait for the poll loop to terminate when the state is set to OFF. + pthread_join(hci_poll_thread_id, NULL); + + // Close the UART. + close(uart_fd); + uart_fd = -1; + + return 0; +} + +int mp_bluetooth_hci_uart_set_baudrate(uint32_t baudrate) { + (void)baudrate; + DEBUG_printf("mp_bluetooth_hci_uart_set_baudrate\n"); + return 0; +} + +int mp_bluetooth_hci_uart_readchar(void) { + // DEBUG_printf("mp_bluetooth_hci_uart_readchar\n"); + + if (uart_fd == -1) { + return -1; + } + + uint8_t c; + ssize_t bytes_read = read(uart_fd, &c, 1); + + if (bytes_read == 1) { + #if DEBUG_HCI_DUMP + printf("[% 8ld] RX: %02x\n", mp_hal_ticks_ms(), c); + #endif + return c; + } else { + return -1; + } +} + +int mp_bluetooth_hci_uart_write(const uint8_t *buf, size_t len) { + // DEBUG_printf("mp_bluetooth_hci_uart_write\n"); + + if (uart_fd == -1) { + return 0; + } + + #if DEBUG_HCI_DUMP + printf("[% 8ld] TX: %02x", mp_hal_ticks_ms(), buf[0]); + for (size_t i = 1; i < len; ++i) { + printf(":%02x", buf[i]); + } + printf("\n"); + #endif + + return write(uart_fd, buf, len); +} + +// No-op implementations of HCI controller interface. +int mp_bluetooth_hci_controller_init(void) { + return 0; +} + +int mp_bluetooth_hci_controller_deinit(void) { + return 0; +} + +int mp_bluetooth_hci_controller_sleep_maybe(void) { + return 0; +} + +bool mp_bluetooth_hci_controller_woken(void) { + return true; +} + +int mp_bluetooth_hci_controller_wakeup(void) { + return 0; +} + +#endif // MICROPY_PY_BLUETOOTH && (MICROPY_BLUETOOTH_NIMBLE || (MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4)) diff --git a/ports/unix/mpbtstackport.h b/ports/unix/mpbtstackport.h new file mode 100644 index 0000000000..c82e8bd812 --- /dev/null +++ b/ports/unix/mpbtstackport.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_UNIX_BTSTACK_PORT_H +#define MICROPY_INCLUDED_UNIX_BTSTACK_PORT_H + +#define MICROPY_HW_BLE_UART_ID (0) +#define MICROPY_HW_BLE_UART_BAUDRATE (1000000) + +bool mp_bluetooth_hci_poll(void); + +#if MICROPY_BLUETOOTH_BTSTACK_H4 +void mp_bluetooth_hci_poll_h4(void); +void mp_bluetooth_btstack_port_init_h4(void); +#endif + +#if MICROPY_BLUETOOTH_BTSTACK_USB +void mp_bluetooth_btstack_port_init_usb(void); +#endif + +#endif // MICROPY_INCLUDED_UNIX_BTSTACK_PORT_H diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c new file mode 100644 index 0000000000..621e661f9e --- /dev/null +++ b/ports/unix/mpbtstackport_common.c @@ -0,0 +1,96 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK + +#include "lib/btstack/src/btstack.h" + +#include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" +#include "lib/btstack/platform/embedded/hal_cpu.h" +#include "lib/btstack/platform/embedded/hal_time_ms.h" + +#include "extmod/btstack/modbluetooth_btstack.h" + +#include "mpbtstackport.h" + +// Called by the UART polling thread in mpbthciport.c, or by the USB polling thread in mpbtstackport_usb.c. +bool mp_bluetooth_hci_poll(void) { + if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_HALTING) { + // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). + mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + #if MICROPY_BLUETOOTH_BTSTACK_H4 + mp_bluetooth_hci_poll_h4(); + #endif + btstack_run_loop_embedded_execute_once(); + MICROPY_END_ATOMIC_SECTION(atomic_state); + + return true; + } + + return false; +} + +// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the +// following three functions are empty. + +void hal_cpu_disable_irqs(void) { +} + +void hal_cpu_enable_irqs(void) { +} + +void hal_cpu_enable_irqs_and_sleep(void) { +} + +uint32_t hal_time_ms(void) { + return mp_hal_ticks_ms(); +} + +void mp_bluetooth_btstack_port_init(void) { + static bool run_loop_init = false; + if (!run_loop_init) { + run_loop_init = true; + btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); + } else { + btstack_run_loop_embedded_get_instance()->init(); + } + + // hci_dump_open(NULL, HCI_DUMP_STDOUT); + + #if MICROPY_BLUETOOTH_BTSTACK_H4 + mp_bluetooth_btstack_port_init_h4(); + #endif + + #if MICROPY_BLUETOOTH_BTSTACK_USB + mp_bluetooth_btstack_port_init_usb(); + #endif +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/ports/unix/mpbtstackport_h4.c b/ports/unix/mpbtstackport_h4.c new file mode 100644 index 0000000000..4fdc20c22b --- /dev/null +++ b/ports/unix/mpbtstackport_h4.c @@ -0,0 +1,80 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4 + +#include "lib/btstack/chipset/zephyr/btstack_chipset_zephyr.h" + +#include "extmod/btstack/btstack_hci_uart.h" +#include "extmod/btstack/modbluetooth_btstack.h" + +#include "mpbtstackport.h" + +#define DEBUG_printf(...) // printf(__VA_ARGS__) + +STATIC hci_transport_config_uart_t hci_transport_config_uart = { + HCI_TRANSPORT_CONFIG_UART, + 1000000, // initial baudrate + 0, // main baudrate + 1, // flow control + NULL, // device name +}; + +void mp_bluetooth_hci_poll_h4(void) { + if (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { + mp_bluetooth_btstack_hci_uart_process(); + } +} + +void mp_bluetooth_btstack_port_init_h4(void) { + DEBUG_printf("mp_bluetooth_btstack_port_init_h4\n"); + + const hci_transport_t *transport = hci_transport_h4_instance(&mp_bluetooth_btstack_hci_uart_block); + hci_init(transport, &hci_transport_config_uart); + + hci_set_chipset(btstack_chipset_zephyr_instance()); +} + +void mp_bluetooth_btstack_port_deinit(void) { + DEBUG_printf("mp_bluetooth_btstack_port_deinit\n"); + + hci_power_control(HCI_POWER_OFF); + hci_close(); +} + +void mp_bluetooth_btstack_port_start(void) { + DEBUG_printf("mp_bluetooth_btstack_port_start\n"); + + hci_power_control(HCI_POWER_ON); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_H4 diff --git a/ports/unix/btstack_usb.c b/ports/unix/mpbtstackport_usb.c similarity index 54% rename from ports/unix/btstack_usb.c rename to ports/unix/mpbtstackport_usb.c index ab6a49f39a..28d2c8c543 100644 --- a/ports/unix/btstack_usb.c +++ b/ports/unix/mpbtstackport_usb.c @@ -31,7 +31,7 @@ #include "py/mperrno.h" #include "py/mphal.h" -#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_USB #include "lib/btstack/src/btstack.h" #include "lib/btstack/platform/embedded/btstack_run_loop_embedded.h" @@ -40,76 +40,15 @@ #include "extmod/btstack/modbluetooth_btstack.h" +#include "mpbtstackport.h" + #if !MICROPY_PY_THREAD #error Unix btstack requires MICROPY_PY_THREAD #endif STATIC const useconds_t USB_POLL_INTERVAL_US = 1000; -STATIC const uint8_t read_static_address_command_complete_prefix[] = { 0x0e, 0x1b, 0x01, 0x09, 0xfc }; - -STATIC uint8_t local_addr[6] = {0}; -STATIC uint8_t static_address[6] = {0}; -STATIC volatile bool have_addr = false; -STATIC bool using_static_address = false; - -STATIC btstack_packet_callback_registration_t hci_event_callback_registration; - -STATIC void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { - (void)channel; - (void)size; - if (packet_type != HCI_EVENT_PACKET) { - return; - } - switch (hci_event_packet_get_type(packet)) { - case BTSTACK_EVENT_STATE: - if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) { - return; - } - gap_local_bd_addr(local_addr); - if (using_static_address) { - memcpy(local_addr, static_address, sizeof(local_addr)); - } - have_addr = true; - break; - case HCI_EVENT_COMMAND_COMPLETE: - if (memcmp(packet, read_static_address_command_complete_prefix, sizeof(read_static_address_command_complete_prefix)) == 0) { - reverse_48(&packet[7], static_address); - gap_random_address_set(static_address); - using_static_address = true; - have_addr = true; - } - break; - default: - break; - } -} - -// The IRQ functionality in btstack_run_loop_embedded.c is not used, so the -// following three functions are empty. - -void hal_cpu_disable_irqs(void) { -} - -void hal_cpu_enable_irqs(void) { -} - -void hal_cpu_enable_irqs_and_sleep(void) { -} - -uint32_t hal_time_ms(void) { - return mp_hal_ticks_ms(); -} - -void mp_bluetooth_btstack_port_init(void) { - static bool run_loop_init = false; - if (!run_loop_init) { - run_loop_init = true; - btstack_run_loop_init(btstack_run_loop_embedded_get_instance()); - } else { - btstack_run_loop_embedded_get_instance()->init(); - } - +void mp_bluetooth_btstack_port_init_usb(void) { // MICROPYBTUSB can be a ':'' or '-' separated port list. char *path = getenv("MICROPYBTUSB"); if (path != NULL) { @@ -128,11 +67,7 @@ void mp_bluetooth_btstack_port_init(void) { hci_transport_usb_set_path(usb_path_len, usb_path); } - // hci_dump_open(NULL, HCI_DUMP_STDOUT); hci_init(hci_transport_usb_instance(), NULL); - - hci_event_callback_registration.callback = &packet_handler; - hci_add_event_handler(&hci_event_callback_registration); } STATIC pthread_t bstack_thread_id; @@ -142,9 +77,12 @@ void mp_bluetooth_btstack_port_deinit(void) { // Wait for the poll loop to terminate when the state is set to OFF. pthread_join(bstack_thread_id, NULL); - have_addr = false; } + +// Provided by mpbstackport_common.c. +extern bool mp_bluetooth_hci_poll(void); + STATIC void *btstack_thread(void *arg) { (void)arg; hci_power_control(HCI_POWER_ON); @@ -155,19 +93,15 @@ STATIC void *btstack_thread(void *arg) { // in modbluetooth_btstack.c setting the state back to OFF. // Or, if a timeout results in it being set to TIMEOUT. - while (mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_STARTING || mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE) { - // Pretend like we're running in IRQ context (i.e. other things can't be running at the same time). - mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); - btstack_run_loop_embedded_execute_once(); - MICROPY_END_ATOMIC_SECTION(atomic_state); + while (true) { + if (!mp_bluetooth_hci_poll()) { + break; + } // The USB transport schedules events to the run loop at 1ms intervals, // and the implementation currently polls rather than selects. usleep(USB_POLL_INTERVAL_US); } - - hci_close(); - return NULL; } @@ -179,13 +113,4 @@ void mp_bluetooth_btstack_port_start(void) { pthread_create(&bstack_thread_id, &attr, &btstack_thread, NULL); } -void mp_hal_get_mac(int idx, uint8_t buf[6]) { - if (idx == MP_HAL_MAC_BDADDR) { - if (!have_addr) { - mp_raise_OSError(MP_ENODEV); - } - memcpy(buf, local_addr, sizeof(local_addr)); - } -} - -#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK && MICROPY_BLUETOOTH_BTSTACK_USB diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 7aa755ef6c..99da47f31b 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -87,6 +87,7 @@ #define MICROPY_VFS_POSIX_FILE (1) #define MICROPY_PY_FUNCTION_ATTRS (1) #define MICROPY_PY_DESCRIPTORS (1) +#define MICROPY_PY_DELATTR_SETATTR (1) #define MICROPY_PY_BUILTINS_STR_UNICODE (1) #define MICROPY_PY_BUILTINS_STR_CENTER (1) #define MICROPY_PY_BUILTINS_STR_PARTITION (1) @@ -347,11 +348,16 @@ void mp_unix_mark_exec(void); #endif #if MICROPY_PY_THREAD -#define MICROPY_BEGIN_ATOMIC_SECTION() (mp_thread_unix_begin_atomic_section(), 0) +#define MICROPY_BEGIN_ATOMIC_SECTION() (mp_thread_unix_begin_atomic_section(), 0xffffffff) #define MICROPY_END_ATOMIC_SECTION(x) (void)x; mp_thread_unix_end_atomic_section() #endif -#define MICROPY_EVENT_POLL_HOOK mp_hal_delay_us(500); +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(bool); \ + mp_handle_pending(true); \ + mp_hal_delay_us(500); \ + } while (0); #include #define MICROPY_UNIX_MACHINE_IDLE sched_yield(); diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index 46f2ccd105..5d3e2a2482 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -66,11 +66,6 @@ static inline int mp_hal_readline(vstr_t *vstr, const char *p) { #endif -// TODO: POSIX et al. define usleep() as guaranteedly capable only of 1s sleep: -// "The useconds argument shall be less than one million." -static inline void mp_hal_delay_ms(mp_uint_t ms) { - usleep((ms) * 1000); -} static inline void mp_hal_delay_us(mp_uint_t us) { usleep(us); } diff --git a/ports/unix/mpnimbleport.c b/ports/unix/mpnimbleport.c new file mode 100644 index 0000000000..29f558f74d --- /dev/null +++ b/ports/unix/mpnimbleport.c @@ -0,0 +1,74 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" + +#if MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE + +#include "nimble/nimble_npl.h" + +#include "extmod/nimble/modbluetooth_nimble.h" +#include "extmod/nimble/hal/hal_uart.h" + +#define DEBUG_printf(...) // printf(__VA_ARGS__) + +// Called by the UART polling thread in mpbthciport.c. +bool mp_bluetooth_hci_poll(void) { + // DEBUG_printf("mp_bluetooth_hci_poll (unix nimble) %d\n", mp_bluetooth_nimble_ble_state); + + if (mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF) { + DEBUG_printf("mp_bluetooth_hci_poll (unix nimble) -- shutdown\n"); + return false; + } + + if (mp_bluetooth_nimble_ble_state >= MP_BLUETOOTH_NIMBLE_BLE_STATE_WAITING_FOR_SYNC) { + // Run any timers. + mp_bluetooth_nimble_os_callout_process(); + + // Process incoming UART data, and run events as they are generated. + mp_bluetooth_nimble_hci_uart_process(true); + + // Run any remaining events (e.g. if there was no UART data). + mp_bluetooth_nimble_os_eventq_run_all(); + } + + return true; +} + +bool mp_bluetooth_hci_active(void) { + return mp_bluetooth_nimble_ble_state != MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; +} + +// Extra port-specific helpers. +void mp_bluetooth_nimble_hci_uart_wfi(void) { + // This is called while NimBLE is waiting in ble_npl_sem_pend, i.e. waiting for an HCI ACK. + // Do not need to run events here, only processing incoming HCI data. + mp_bluetooth_nimble_hci_uart_process(false); +} + +#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_NIMBLE diff --git a/ports/unix/mpnimbleport.h b/ports/unix/mpnimbleport.h new file mode 100644 index 0000000000..a2935e6fdf --- /dev/null +++ b/ports/unix/mpnimbleport.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jim Mussared + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_UNIX_NIMBLE_PORT_H +#define MICROPY_INCLUDED_UNIX_NIMBLE_PORT_H + +#define MICROPY_HW_BLE_UART_ID (0) +#define MICROPY_HW_BLE_UART_BAUDRATE (1000000) + +#endif // MICROPY_INCLUDED_UNIX_NIMBLE_PORT_H diff --git a/ports/unix/unix_mphal.c b/ports/unix/unix_mphal.c index d19f3f717c..89bb645bff 100644 --- a/ports/unix/unix_mphal.c +++ b/ports/unix/unix_mphal.c @@ -167,6 +167,21 @@ mp_uint_t mp_hal_ticks_us(void) { } uint64_t mp_hal_time_ns(void) { - time_t now = time(NULL); - return (uint64_t)now * 1000000000ULL; + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; +} + +void mp_hal_delay_ms(mp_uint_t ms) { + #ifdef MICROPY_EVENT_POLL_HOOK + mp_uint_t start = mp_hal_ticks_ms(); + while (mp_hal_ticks_ms() - start < ms) { + // MICROPY_EVENT_POLL_HOOK does mp_hal_delay_us(500) (i.e. usleep(500)). + MICROPY_EVENT_POLL_HOOK + } + #else + // TODO: POSIX et al. define usleep() as guaranteedly capable only of 1s sleep: + // "The useconds argument shall be less than one million." + usleep(ms * 1000); + #endif } diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index d16c85e2c2..7902f461bb 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -30,6 +30,7 @@ #define MICROPY_VFS (1) #define MICROPY_PY_UOS_VFS (1) +#define MICROPY_DEBUG_PARSE_RULE_NAME (1) #define MICROPY_OPT_MATH_FACTORIAL (1) #define MICROPY_FLOAT_HIGH_QUALITY_HASH (1) #define MICROPY_ENABLE_SCHEDULER (1) @@ -39,6 +40,7 @@ #define MICROPY_WARNINGS_CATEGORY (1) #define MICROPY_MODULE_GETATTR (1) #define MICROPY_PY_DELATTR_SETATTR (1) +#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1) #define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #define MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE (1) #define MICROPY_PY_BUILTINS_NEXT2 (1) diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 57bf36509c..aa554200bb 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -7,13 +7,19 @@ CFLAGS += \ -fprofile-arcs -ftest-coverage \ -Wformat -Wmissing-declarations -Wmissing-prototypes \ -Wold-style-definition -Wpointer-arith -Wshadow -Wuninitialized -Wunused-parameter \ - -DMICROPY_UNIX_COVERAGE + -DMICROPY_UNIX_COVERAGE \ + -DMODULE_CEXAMPLE_ENABLED=1 -DMODULE_CPPEXAMPLE_ENABLED=1 LDFLAGS += -fprofile-arcs -ftest-coverage +USER_C_MODULES = $(TOP)/examples/usercmodule + MICROPY_VFS_FAT = 1 MICROPY_VFS_LFS1 = 1 MICROPY_VFS_LFS2 = 1 FROZEN_DIR=variants/coverage/frzstr FROZEN_MPY_DIR=variants/coverage/frzmpy + +SRC_C += coverage.c +SRC_CXX += coveragecpp.cpp diff --git a/ports/unix/variants/dev/mpconfigvariant.mk b/ports/unix/variants/dev/mpconfigvariant.mk index b48cc42b05..16d033c6ce 100644 --- a/ports/unix/variants/dev/mpconfigvariant.mk +++ b/ports/unix/variants/dev/mpconfigvariant.mk @@ -5,4 +5,5 @@ FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py MICROPY_VFS_FAT = 1 MICROPY_VFS_LFS1 = 1 MICROPY_VFS_LFS2 = 1 -MICROPY_PY_BLUETOOTH = 1 + +MICROPY_PY_BLUETOOTH ?= 1 diff --git a/py/argcheck.c b/py/argcheck.c index 553724d31f..8d26779b91 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -100,7 +100,10 @@ void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n pos_found++; given_arg = pos[i]; } else { - mp_map_elem_t *kw = mp_map_lookup(kws, MP_OBJ_NEW_QSTR(allowed[i].qst), MP_MAP_LOOKUP); + mp_map_elem_t *kw = NULL; + if (kws != NULL) { + kw = mp_map_lookup(kws, MP_OBJ_NEW_QSTR(allowed[i].qst), MP_MAP_LOOKUP); + } if (kw == NULL) { if (allowed[i].flags & MP_ARG_REQUIRED) { #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE @@ -134,7 +137,7 @@ void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n mp_raise_TypeError(MP_ERROR_TEXT("extra positional arguments given")); #endif } - if (kws_found < kws->used) { + if (kws != NULL && kws_found < kws->used) { #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE mp_arg_error_terse_mismatch(); #else diff --git a/py/asmthumb.c b/py/asmthumb.c index 3ea0163303..ae35632fac 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -47,6 +47,7 @@ #define SIGNED_FIT12(x) (((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800) #define SIGNED_FIT23(x) (((x) & 0xffc00000) == 0) || (((x) & 0xffc00000) == 0xffc00000) +#if MICROPY_EMIT_THUMB_ARMV7M // Note: these actually take an imm12 but the high-bit is not encoded here #define OP_ADD_W_RRI_HI(reg_src) (0xf200 | (reg_src)) #define OP_ADD_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff)) @@ -55,6 +56,7 @@ #define OP_LDR_W_HI(reg_base) (0xf8d0 | (reg_base)) #define OP_LDR_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12)) +#endif static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) { return mp_asm_base_get_cur_to_write_bytes(&as->base, n); @@ -122,7 +124,7 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) { // If this Thumb machine code is run from ARM state then add a prelude // to switch to Thumb state for the duration of the function. - #if MICROPY_DYNAMIC_COMPILER || MICROPY_EMIT_ARM || (defined(__arm__) && !defined(__thumb2__)) + #if MICROPY_DYNAMIC_COMPILER || MICROPY_EMIT_ARM || (defined(__arm__) && !defined(__thumb2__) && !defined(__thumb__)) #if MICROPY_DYNAMIC_COMPILER if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_ARMV6) #endif @@ -171,11 +173,21 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) { } asm_thumb_op16(as, OP_PUSH_RLIST_LR(reglist)); if (stack_adjust > 0) { + #if MICROPY_EMIT_THUMB_ARMV7M if (UNSIGNED_FIT7(stack_adjust)) { asm_thumb_op16(as, OP_SUB_SP(stack_adjust)); } else { asm_thumb_op32(as, OP_SUB_W_RRI_HI(ASM_THUMB_REG_SP), OP_SUB_W_RRI_LO(ASM_THUMB_REG_SP, stack_adjust * 4)); } + #else + int adj = stack_adjust; + // we don't expect the stack_adjust to be massive + while (!UNSIGNED_FIT7(adj)) { + asm_thumb_op16(as, OP_SUB_SP(127)); + adj -= 127; + } + asm_thumb_op16(as, OP_SUB_SP(adj)); + #endif } as->push_reglist = reglist; as->stack_adjust = stack_adjust; @@ -183,11 +195,21 @@ void asm_thumb_entry(asm_thumb_t *as, int num_locals) { void asm_thumb_exit(asm_thumb_t *as) { if (as->stack_adjust > 0) { + #if MICROPY_EMIT_THUMB_ARMV7M if (UNSIGNED_FIT7(as->stack_adjust)) { asm_thumb_op16(as, OP_ADD_SP(as->stack_adjust)); } else { asm_thumb_op32(as, OP_ADD_W_RRI_HI(ASM_THUMB_REG_SP), OP_ADD_W_RRI_LO(ASM_THUMB_REG_SP, as->stack_adjust * 4)); } + #else + int adj = as->stack_adjust; + // we don't expect the stack_adjust to be massive + while (!UNSIGNED_FIT7(adj)) { + asm_thumb_op16(as, OP_ADD_SP(127)); + adj -= 127; + } + asm_thumb_op16(as, OP_ADD_SP(adj)); + #endif } asm_thumb_op16(as, OP_POP_RLIST_PC(as->push_reglist)); } @@ -241,6 +263,8 @@ void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src) { asm_thumb_op16(as, 0x4600 | op_lo); } +#if MICROPY_EMIT_THUMB_ARMV7M + // if loading lo half with movw, the i16 value will be zero extended into the r32 register! size_t asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src) { assert(reg_dest < ASM_THUMB_REG_R15); @@ -250,6 +274,16 @@ size_t asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i1 return loc; } +#else + +void asm_thumb_mov_rlo_i16(asm_thumb_t *as, uint rlo_dest, int i16_src) { + asm_thumb_mov_rlo_i8(as, rlo_dest, (i16_src >> 8) & 0xff); + asm_thumb_lsl_rlo_rlo_i5(as, rlo_dest, rlo_dest, 8); + asm_thumb_add_rlo_i8(as, rlo_dest, i16_src & 0xff); +} + +#endif + #define OP_B_N(byte_offset) (0xe000 | (((byte_offset) >> 1) & 0x07ff)) bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) { @@ -274,8 +308,13 @@ bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) { asm_thumb_op16(as, OP_BCC_N(cond, rel)); return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT9(rel); } else { + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); return true; + #else + // this method should not be called for ARMV6M + return false; + #endif } } @@ -296,8 +335,30 @@ size_t asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) { size_t loc = mp_asm_base_get_code_pos(&as->base); + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32); asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVT, reg_dest, i32 >> 16); + #else + // should only be called with lo reg for ARMV6M + assert(reg_dest < ASM_THUMB_REG_R8); + + // sanity check that generated code is aligned + assert(!as->base.code_base || !(3u & (uintptr_t)as->base.code_base)); + + // basically: + // (nop) + // ldr reg_dest, _data + // b 1f + // _data: .word i32 + // 1: + if (as->base.code_offset & 2u) { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + } + asm_thumb_ldr_rlo_pcrel_i8(as, reg_dest, 0); + asm_thumb_op16(as, OP_B_N(2)); + asm_thumb_op16(as, i32 & 0xffff); + asm_thumb_op16(as, i32 >> 16); + #endif return loc; } @@ -305,27 +366,68 @@ size_t asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) { void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) { if (reg_dest < 8 && UNSIGNED_FIT8(i32)) { asm_thumb_mov_rlo_i8(as, reg_dest, i32); - } else if (UNSIGNED_FIT16(i32)) { - asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32); } else { - asm_thumb_mov_reg_i32(as, reg_dest, i32); + #if MICROPY_EMIT_THUMB_ARMV7M + if (UNSIGNED_FIT16(i32)) { + asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32); + } else { + asm_thumb_mov_reg_i32(as, reg_dest, i32); + } + #else + uint rlo_dest = reg_dest; + assert(rlo_dest < ASM_THUMB_REG_R8); // should never be called for ARMV6M + + bool negate = i32 < 0 && ((i32 + i32) & 0xffffffffu); // don't negate 0x80000000 + if (negate) { + i32 = -i32; + } + + uint clz = __builtin_clz(i32); + uint ctz = i32 ? __builtin_ctz(i32) : 0; + assert(clz + ctz <= 32); + if (clz + ctz >= 24) { + asm_thumb_mov_rlo_i8(as, rlo_dest, (i32 >> ctz) & 0xff); + asm_thumb_lsl_rlo_rlo_i5(as, rlo_dest, rlo_dest, ctz); + } else if (UNSIGNED_FIT16(i32)) { + asm_thumb_mov_rlo_i16(as, rlo_dest, i32); + } else { + if (negate) { + // no point in negating if we're storing in 32 bit anyway + negate = false; + i32 = -i32; + } + asm_thumb_mov_reg_i32(as, rlo_dest, i32); + } + if (negate) { + asm_thumb_neg_rlo_rlo(as, rlo_dest, rlo_dest); + } + #endif } } #define OP_STR_TO_SP_OFFSET(rlo_dest, word_offset) (0x9000 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff)) #define OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset) (0x9800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff)) +static void asm_thumb_mov_local_check(asm_thumb_t *as, int word_offset) { + if (as->base.pass >= MP_ASM_PASS_EMIT) { + assert(word_offset >= 0); + if (!UNSIGNED_FIT8(word_offset)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("too many locals for native method")); + } + } +} + void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num, uint rlo_src) { assert(rlo_src < ASM_THUMB_REG_R8); int word_offset = local_num; - assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); + asm_thumb_mov_local_check(as, word_offset); asm_thumb_op16(as, OP_STR_TO_SP_OFFSET(rlo_src, word_offset)); } void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) { assert(rlo_dest < ASM_THUMB_REG_R8); int word_offset = local_num; - assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0); + asm_thumb_mov_local_check(as, word_offset); asm_thumb_op16(as, OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset)); } @@ -341,21 +443,63 @@ void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num) void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label) { mp_uint_t dest = get_label_dest(as, label); mp_int_t rel = dest - as->base.code_offset; - rel -= 4 + 4; // adjust for mov_reg_i16 and then PC+4 prefetch of add_reg_reg rel |= 1; // to stay in Thumb state when jumping to this address + #if MICROPY_EMIT_THUMB_ARMV7M + rel -= 4 + 4; // adjust for mov_reg_i16 and then PC+4 prefetch of add_reg_reg asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, rlo_dest, rel); // 4 bytes + #else + rel -= 8 + 4; // adjust for four instructions and then PC+4 prefetch of add_reg_reg + // 6 bytes + asm_thumb_mov_rlo_i16(as, rlo_dest, rel); + // 2 bytes - not always needed, but we want to keep the size the same + asm_thumb_sxth_rlo_rlo(as, rlo_dest, rlo_dest); + #endif asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes } +#if MICROPY_EMIT_THUMB_ARMV7M static inline void asm_thumb_ldr_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { asm_thumb_op32(as, OP_LDR_W_HI(reg_base), OP_LDR_W_LO(reg_dest, word_offset * 4)); } +#endif void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(word_offset)) { asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_base, word_offset); } else { + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, word_offset); + #else + word_offset -= 31; + if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8) { + if (UNSIGNED_FIT8(word_offset) && (word_offset < 64 || reg_dest != reg_base)) { + if (word_offset < 64) { + if (reg_dest != reg_base) { + asm_thumb_mov_reg_reg(as, reg_dest, reg_base); + } + asm_thumb_add_rlo_i8(as, reg_dest, word_offset * 4); + } else { + asm_thumb_mov_rlo_i8(as, reg_dest, word_offset); + asm_thumb_lsl_rlo_rlo_i5(as, reg_dest, reg_dest, 2); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_base); + } + } else { + if (reg_dest != reg_base) { + asm_thumb_mov_rlo_i16(as, reg_dest, word_offset * 4); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_dest); + } else { + uint reg_other = reg_dest ^ 7; + asm_thumb_op16(as, OP_PUSH_RLIST((1 << reg_other))); + asm_thumb_mov_rlo_i16(as, reg_other, word_offset * 4); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_other); + asm_thumb_op16(as, OP_POP_RLIST((1 << reg_other))); + } + } + } else { + assert(0); // should never be called for ARMV6M + } + asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_dest, 31); + #endif } } @@ -378,7 +522,20 @@ void asm_thumb_b_label(asm_thumb_t *as, uint label) { } else { // is a forwards jump, so need to assume it's large large_jump: + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel)); + #else + if (SIGNED_FIT12(rel)) { + // this code path has to be the same number of instructions irrespective of rel + asm_thumb_op16(as, OP_B_N(rel)); + } else { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + if (dest != (mp_uint_t)-1) { + // we have an actual branch > 12 bits; this is not handled yet + mp_raise_NotImplementedError(MP_ERROR_TEXT("native method too big")); + } + } + #endif } } @@ -397,10 +554,28 @@ void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { } else { // is a forwards jump, so need to assume it's large large_jump: + #if MICROPY_EMIT_THUMB_ARMV7M asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); + #else + // reverse the sense of the branch to jump over a longer branch + asm_thumb_op16(as, OP_BCC_N(cond ^ 1, 0)); + asm_thumb_b_label(as, label); + #endif } } +void asm_thumb_bcc_rel9(asm_thumb_t *as, int cond, int rel) { + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + assert(SIGNED_FIT9(rel)); + asm_thumb_op16(as, OP_BCC_N(cond, rel)); +} + +void asm_thumb_b_rel12(asm_thumb_t *as, int rel) { + rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction + assert(SIGNED_FIT12(rel)); + asm_thumb_op16(as, OP_B_N(rel)); +} + #define OP_BLX(reg) (0x4780 | ((reg) << 3)) #define OP_SVC(arg) (0xdf00 | (arg)) diff --git a/py/asmthumb.h b/py/asmthumb.h index ba193fbab4..acdcfed701 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -157,6 +157,7 @@ static inline void asm_thumb_sub_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint #define ASM_THUMB_FORMAT_3_CMP (0x2800) #define ASM_THUMB_FORMAT_3_ADD (0x3000) #define ASM_THUMB_FORMAT_3_SUB (0x3800) +#define ASM_THUMB_FORMAT_3_LDR (0x4800) #define ASM_THUMB_FORMAT_3_ENCODE(op, rlo, i8) ((op) | ((rlo) << 8) | (i8)) @@ -177,6 +178,9 @@ static inline void asm_thumb_add_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { static inline void asm_thumb_sub_rlo_i8(asm_thumb_t *as, uint rlo, int i8) { asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_SUB, rlo, i8); } +static inline void asm_thumb_ldr_rlo_pcrel_i8(asm_thumb_t *as, uint rlo, uint i8) { + asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_LDR, rlo, i8); +} // FORMAT 4: ALU operations @@ -202,6 +206,12 @@ void asm_thumb_format_4(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src); static inline void asm_thumb_cmp_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_CMP, rlo_dest, rlo_src); } +static inline void asm_thumb_mvn_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_MVN, rlo_dest, rlo_src); +} +static inline void asm_thumb_neg_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_NEG, rlo_dest, rlo_src); +} // FORMAT 5: hi register operations (add, cmp, mov, bx) // For add/cmp/mov, at least one of the args must be a high register @@ -263,6 +273,32 @@ static inline void asm_thumb_ldrb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uin static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset) { asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, byte_offset); } +static inline void asm_thumb_lsl_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) { + asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_LSL, rlo_dest, rlo_src, shift); +} +static inline void asm_thumb_asr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) { + asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_ASR, rlo_dest, rlo_src, shift); +} + +// FORMAT 11: sign/zero extend + +#define ASM_THUMB_FORMAT_11_ENCODE(op, rlo_dest, rlo_src) \ + ((op) | ((rlo_src) << 3) | (rlo_dest)) + +#define ASM_THUMB_FORMAT_11_SXTH (0xb200) +#define ASM_THUMB_FORMAT_11_SXTB (0xb240) +#define ASM_THUMB_FORMAT_11_UXTH (0xb280) +#define ASM_THUMB_FORMAT_11_UXTB (0xb2c0) + +static inline void asm_thumb_format_11(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src) { + assert(rlo_dest < ASM_THUMB_REG_R8); + assert(rlo_src < ASM_THUMB_REG_R8); + asm_thumb_op16(as, ASM_THUMB_FORMAT_11_ENCODE(op, rlo_dest, rlo_src)); +} + +static inline void asm_thumb_sxth_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) { + asm_thumb_format_11(as, ASM_THUMB_FORMAT_11_SXTH, rlo_dest, rlo_src); +} // TODO convert these to above format style @@ -270,7 +306,12 @@ static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uin #define ASM_THUMB_OP_MOVT (0xf2c0) void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src); + +#if MICROPY_EMIT_THUMB_ARMV7M size_t asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src); +#else +void asm_thumb_mov_rlo_i16(asm_thumb_t *as, uint rlo_dest, int i16_src); +#endif // these return true if the destination is in range, false otherwise bool asm_thumb_b_n_label(asm_thumb_t *as, uint label); @@ -289,6 +330,8 @@ void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint re void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narrow or wide branch void asm_thumb_bcc_label(asm_thumb_t *as, int cc, uint label); // convenience: picks narrow or wide branch void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp); // convenience +void asm_thumb_bcc_rel9(asm_thumb_t *as, int cc, int rel); +void asm_thumb_b_rel12(asm_thumb_t *as, int rel); // Holds a pointer to mp_fun_table #define ASM_THUMB_REG_FUN_TABLE ASM_THUMB_REG_R7 @@ -344,7 +387,11 @@ void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp); // convenien #define ASM_MOV_LOCAL_REG(as, local_num, reg) asm_thumb_mov_local_reg((as), (local_num), (reg)) #define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_optimised((as), (reg_dest), (imm)) +#if MICROPY_EMIT_THUMB_ARMV7M #define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_thumb_mov_reg_i16((as), ASM_THUMB_OP_MOVW, (reg_dest), (imm)) +#else +#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_thumb_mov_rlo_i16((as), (reg_dest), (imm)) +#endif #define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_thumb_mov_reg_i32((as), (reg_dest), (imm)) #define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_thumb_mov_reg_local((as), (reg_dest), (local_num)) #define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src)) diff --git a/py/bc.h b/py/bc.h index 6467e67679..410ca800c8 100644 --- a/py/bc.h +++ b/py/bc.h @@ -226,10 +226,10 @@ const byte *mp_decode_uint_skip(const byte *ptr); mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc); mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args); void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args); -void mp_bytecode_print(const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table); -void mp_bytecode_print2(const byte *code, size_t len, const mp_uint_t *const_table); -const byte *mp_bytecode_print_str(const byte *ip); -#define mp_bytecode_print_inst(code, const_table) mp_bytecode_print2(code, 1, const_table) +void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table); +void mp_bytecode_print2(const mp_print_t *print, const byte *code, size_t len, const mp_uint_t *const_table); +const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip); +#define mp_bytecode_print_inst(print, code, const_table) mp_bytecode_print2(print, code, 1, const_table) // Helper macros to access pointer with least significant bits holding flags #define MP_TAGPTR_PTR(x) ((void *)((uintptr_t)(x) & ~((uintptr_t)3))) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index e722645c35..18a1480a10 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -204,6 +204,7 @@ typedef long mp_off_t; #define MICROPY_PY_URE_MATCH_GROUPS (CIRCUITPY_RE) #define MICROPY_PY_URE_MATCH_SPAN_START_END (CIRCUITPY_RE) #define MICROPY_PY_URE_SUB (CIRCUITPY_RE) +#define MICROPY_EPOCH_IS_1970 (0) // LONGINT_IMPL_xxx are defined in the Makefile. // diff --git a/py/dynruntime.h b/py/dynruntime.h index 215ffc5445..96d7d27b2f 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -166,9 +166,15 @@ static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { /******************************************************************************/ // General runtime functions -#define mp_load_name(qst) (mp_fun_table.load_name(qst)) -#define mp_load_global(qst) (mp_fun_table.load_global(qst)) -#define mp_store_global(qst, obj) (mp_fun_table.store_global((qst), (obj))) +#define mp_load_name(qst) (mp_fun_table.load_name((qst))) +#define mp_load_global(qst) (mp_fun_table.load_global((qst))) +#define mp_load_attr(base, attr) (mp_fun_table.load_attr((base), (attr))) +#define mp_load_method(base, attr, dest) (mp_fun_table.load_method((base), (attr), (dest))) +#define mp_load_super_method(attr, dest) (mp_fun_table.load_super_method((attr), (dest))) +#define mp_store_name(qst, obj) (mp_fun_table.store_name((qst), (obj))) +#define mp_store_global(qst, obj) (mp_fun_table.store_global((qst), (obj))) +#define mp_store_attr(base, attr, val) (mp_fun_table.store_attr((base), (attr), (val))) + #define mp_unary_op(op, obj) (mp_fun_table.unary_op((op), (obj))) #define mp_binary_op(op, lhs, rhs) (mp_fun_table.binary_op((op), (lhs), (rhs))) @@ -199,6 +205,13 @@ static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) { #define MP_DYNRUNTIME_MAKE_FUNCTION(f) \ (mp_make_function_from_raw_code((rc.fun_data = (f), &rc), MP_OBJ_NULL, MP_OBJ_NULL)) +#define mp_import_name(name, fromlist, level) \ + (mp_fun_table.import_name((name), (fromlist), (level))) +#define mp_import_from(module, name) \ + (mp_fun_table.import_from((module), (name))) +#define mp_import_all(module) \ + (mp_fun_table.import_all((module)) + /******************************************************************************/ // Exceptions diff --git a/py/emitglue.c b/py/emitglue.c index 99b4f9ba3b..5049a76869 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -92,7 +92,7 @@ void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code, #endif #if MICROPY_DEBUG_PRINTERS if (mp_verbose_flag >= 2) { - mp_bytecode_print(rc, code, len, const_table); + mp_bytecode_print(&mp_plat_print, rc, code, len, const_table); } #endif } diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 1800cb43f9..65168a5211 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -108,7 +108,7 @@ STATIC mp_uint_t emit_inline_thumb_count_params(emit_inline_asm_t *emit, mp_uint return 0; } const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i])); - if (!(strlen(p) == 2 && p[0] == 'r' && p[1] == '0' + i)) { + if (!(strlen(p) == 2 && p[0] == 'r' && (mp_uint_t)p[1] == '0' + i)) { emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence r0 to r3")); return 0; } @@ -573,7 +573,11 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a goto unknown_op; } int label_num = get_arg_label(emit, op_str, pn_args[0]); - if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, op_len == 5 && op_str[4] == 'w')) { + bool wide = op_len == 5 && op_str[4] == 'w'; + if (wide && !ARMV7M) { + goto unknown_op; + } + if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, wide)) { goto branch_not_in_range; } } else if (ARMV7M && op_str[0] == 'i' && op_str[1] == 't') { @@ -701,23 +705,24 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a } else if (op == MP_QSTR_sub) { op_code = ASM_THUMB_FORMAT_3_SUB; goto op_format_3; - } else if (ARMV7M && op == MP_QSTR_movw) { + #if ARMV7M + } else if (op == MP_QSTR_movw) { op_code = ASM_THUMB_OP_MOVW; mp_uint_t reg_dest; op_movw_movt: reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff); asm_thumb_mov_reg_i16(&emit->as, op_code, reg_dest, i_src); - } else if (ARMV7M && op == MP_QSTR_movt) { + } else if (op == MP_QSTR_movt) { op_code = ASM_THUMB_OP_MOVT; goto op_movw_movt; - } else if (ARMV7M && op == MP_QSTR_movwt) { + } else if (op == MP_QSTR_movwt) { // this is a convenience instruction mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15); uint32_t i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff); asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff); asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff); - } else if (ARMV7M && op == MP_QSTR_ldrex) { + } else if (op == MP_QSTR_ldrex) { mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15); mp_parse_node_t pn_base, pn_offset; if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) { @@ -725,6 +730,7 @@ STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_a mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2; asm_thumb_op32(&emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8); } + #endif } else { // search table for ldr/str instructions for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_9_10_op_table); i++) { diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c index 7611162322..17b91163f1 100644 --- a/py/emitinlinextensa.c +++ b/py/emitinlinextensa.c @@ -92,7 +92,7 @@ STATIC mp_uint_t emit_inline_xtensa_count_params(emit_inline_asm_t *emit, mp_uin return 0; } const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i])); - if (!(strlen(p) == 2 && p[0] == 'a' && p[1] == '2' + i)) { + if (!(strlen(p) == 2 && p[0] == 'a' && (mp_uint_t)p[1] == '2' + i)) { emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence a2 to a5")); return 0; } diff --git a/py/emitnative.c b/py/emitnative.c index 06d508d20b..1427bc83c9 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -239,6 +239,7 @@ struct _emit_t { int pass; bool do_viper_types; + bool prelude_offset_uses_u16_encoding; mp_uint_t local_vtype_alloc; vtype_kind_t *local_vtype; @@ -367,6 +368,18 @@ STATIC void emit_native_mov_reg_qstr_obj(emit_t *emit, int reg_dest, qstr qst) { emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ } while (false) +#define emit_native_mov_state_imm_fix_u16_via(emit, local_num, imm, reg_temp) \ + do { \ + ASM_MOV_REG_IMM_FIX_U16((emit)->as, (reg_temp), (imm)); \ + emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ + } while (false) + +#define emit_native_mov_state_imm_fix_word_via(emit, local_num, imm, reg_temp) \ + do { \ + ASM_MOV_REG_IMM_FIX_WORD((emit)->as, (reg_temp), (imm)); \ + emit_native_mov_state_reg((emit), (local_num), (reg_temp)); \ + } while (false) + STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) { DEBUG_printf("start_pass(pass=%u, scope=%p)\n", pass, scope); @@ -577,16 +590,27 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_FUN_OBJ(emit), REG_PARENT_ARG_1); // Set code_state.ip (offset from start of this function to prelude info) + int code_state_ip_local = emit->code_state_start + OFFSETOF_CODE_STATE_IP; #if N_PRELUDE_AS_BYTES_OBJ // Prelude is a bytes object in const_table; store ip = prelude->data - fun_bc->bytecode ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, emit->scope->num_pos_args + emit->scope->num_kwonly_args + 1); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_LOCAL_3, REG_LOCAL_3, offsetof(mp_obj_str_t, data) / sizeof(uintptr_t)); ASM_LOAD_REG_REG_OFFSET(emit->as, REG_PARENT_ARG_1, REG_PARENT_ARG_1, OFFSETOF_OBJ_FUN_BC_BYTECODE); ASM_SUB_REG_REG(emit->as, REG_LOCAL_3, REG_PARENT_ARG_1); - emit_native_mov_state_reg(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, REG_LOCAL_3); + emit_native_mov_state_reg(emit, code_state_ip_local, REG_LOCAL_3); #else - // TODO this encoding may change size in the final pass, need to make it fixed - emit_native_mov_state_imm_via(emit, emit->code_state_start + OFFSETOF_CODE_STATE_IP, emit->prelude_offset, REG_PARENT_ARG_1); + if (emit->pass == MP_PASS_CODE_SIZE) { + // Commit to the encoding size based on the value of prelude_offset in this pass. + // By using 32768 as the cut-off it is highly unlikely that prelude_offset will + // grow beyond 65535 by the end of thiss pass, and so require the larger encoding. + emit->prelude_offset_uses_u16_encoding = emit->prelude_offset < 32768; + } + if (emit->prelude_offset_uses_u16_encoding) { + assert(emit->prelude_offset <= 65535); + emit_native_mov_state_imm_fix_u16_via(emit, code_state_ip_local, emit->prelude_offset, REG_PARENT_ARG_1); + } else { + emit_native_mov_state_imm_fix_word_via(emit, code_state_ip_local, emit->prelude_offset, REG_PARENT_ARG_1); + } #endif // Set code_state.n_state (only works on little endian targets due to n_state being uint16_t) @@ -2458,6 +2482,7 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { asm_x86_setcc_r8(emit->as, ops[op_idx], REG_RET); #elif N_THUMB asm_thumb_cmp_rlo_rlo(emit->as, REG_ARG_2, reg_rhs); + #if MICROPY_EMIT_THUMB_ARMV7M static uint16_t ops[6 + 6] = { // unsigned ASM_THUMB_OP_ITE_CC, @@ -2477,6 +2502,28 @@ STATIC void emit_native_binary_op(emit_t *emit, mp_binary_op_t op) { asm_thumb_op16(emit->as, ops[op_idx]); asm_thumb_mov_rlo_i8(emit->as, REG_RET, 1); asm_thumb_mov_rlo_i8(emit->as, REG_RET, 0); + #else + static uint16_t ops[6 + 6] = { + // unsigned + ASM_THUMB_CC_CC, + ASM_THUMB_CC_HI, + ASM_THUMB_CC_EQ, + ASM_THUMB_CC_LS, + ASM_THUMB_CC_CS, + ASM_THUMB_CC_NE, + // signed + ASM_THUMB_CC_LT, + ASM_THUMB_CC_GT, + ASM_THUMB_CC_EQ, + ASM_THUMB_CC_LE, + ASM_THUMB_CC_GE, + ASM_THUMB_CC_NE, + }; + asm_thumb_bcc_rel9(emit->as, ops[op_idx], 6); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 0); + asm_thumb_b_rel12(emit->as, 4); + asm_thumb_mov_rlo_i8(emit->as, REG_RET, 1); + #endif #elif N_ARM asm_arm_cmp_reg_reg(emit->as, REG_ARG_2, reg_rhs); static uint ccs[6 + 6] = { diff --git a/py/gc.c b/py/gc.c index 307af17fac..d4e3da818c 100644 --- a/py/gc.c +++ b/py/gc.c @@ -313,11 +313,7 @@ STATIC void gc_sweep(void) { } #endif free_tail = 1; - ATB_ANY_TO_FREE(block); - #if CLEAR_ON_SWEEP - memset((void *)PTR_FROM_BLOCK(block), 0, BYTES_PER_BLOCK); - #endif - DEBUG_printf("gc_sweep(%x)\n", PTR_FROM_BLOCK(block)); + DEBUG_printf("gc_sweep(%p)\n", (void *)PTR_FROM_BLOCK(block)); #ifdef LOG_HEAP_ACTIVITY gc_log_change(block, 0); @@ -325,7 +321,8 @@ STATIC void gc_sweep(void) { #if MICROPY_PY_GC_COLLECT_RETVAL MP_STATE_MEM(gc_collected)++; #endif - break; + // fall through to free the head + MP_FALLTHROUGH case AT_TAIL: if (free_tail) { diff --git a/py/lexer.c b/py/lexer.c index c04b9f1a8d..c6506ebcee 100644 --- a/py/lexer.c +++ b/py/lexer.c @@ -460,7 +460,8 @@ STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw, bool is_fstring) vstr_add_char(&lex->vstr, '\\'); break; } - // Otherwise fall through. + // Otherwise fall through. + MP_FALLTHROUGH case 'x': { mp_uint_t num = 0; if (!get_hex(lex, (c == 'x' ? 2 : c == 'u' ? 4 : 8), &num)) { diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index cec13b242a..bebbebab20 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -7,10 +7,12 @@ This script works with Python 2.6, 2.7, 3.3 and 3.4. from __future__ import print_function -import re -import sys import io import os +import re +import subprocess +import sys +import multiprocessing, multiprocessing.dummy # Python 2/3 compatibility: # - iterating through bytes is different @@ -65,6 +67,47 @@ del name2codepoint["and"] del name2codepoint["or"] +def preprocess(): + if any(src in args.dependencies for src in args.changed_sources): + sources = args.sources + elif any(args.changed_sources): + sources = args.changed_sources + else: + sources = args.sources + csources = [] + cxxsources = [] + for source in sources: + if source.endswith(".cpp"): + cxxsources.append(source) + else: + csources.append(source) + try: + os.makedirs(os.path.dirname(args.output[0])) + except OSError: + pass + + def pp(flags): + def run(files): + return subprocess.check_output(args.pp + flags + files) + + return run + + try: + cpus = multiprocessing.cpu_count() + except NotImplementedError: + cpus = 1 + p = multiprocessing.dummy.Pool(cpus) + with open(args.output[0], "wb") as out_file: + for flags, sources in ( + (args.cflags, csources), + (args.cxxflags, cxxsources), + ): + batch_size = (len(sources) + cpus - 1) // cpus + chunks = [sources[i : i + batch_size] for i in range(0, len(sources), batch_size or 1)] + for output in p.imap(pp(flags), chunks): + out_file.write(output) + + def write_out(fname, output): if output: for m, r in [("/", "__"), ("\\", "__"), (":", "@"), ("..", "@@")]: @@ -96,10 +139,9 @@ def process_file(f): if line.startswith(("# ", "#line")): m = re_line.match(line) assert m is not None - # print(m.groups()) lineno = int(m.group(1)) fname = m.group(2) - if not fname.endswith(".c"): + if os.path.splitext(fname)[1] not in [".c", ".cpp"]: continue if fname != last_fname: write_out(last_fname, output) @@ -114,7 +156,8 @@ def process_file(f): output.append('TRANSLATE("' + match[0] + '")') lineno += 1 - write_out(last_fname, output) + if last_fname: + write_out(last_fname, output) return "" diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 41e5956542..a89eb35e15 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -23,7 +23,7 @@ def get_version_info_from_git(): # Note: git describe doesn't work if no tag is available try: git_tag = subprocess.check_output( - ["git", "describe", "--dirty", "--always", "--tags"], + ["git", "describe", "--dirty", "--always", "--tags", "--match", "[1-9].*"], stderr=subprocess.STDOUT, universal_newlines=True, ).strip() @@ -91,6 +91,12 @@ def make_version_header(filename): else: version_string = ".".join(ver) + build_date = datetime.date.today() + if "SOURCE_DATE_EPOCH" in os.environ: + build_date = datetime.datetime.utcfromtimestamp( + int(os.environ["SOURCE_DATE_EPOCH"]) + ).date() + # Generate the file with the git and version info file_data = """\ // This file was generated by py/makeversionhdr.py diff --git a/py/map.c b/py/map.c index e0dc7dbc5a..dc5d4b061a 100644 --- a/py/map.c +++ b/py/map.c @@ -42,17 +42,6 @@ #define DEBUG_printf(...) (void)0 #endif -// Fixed empty map. Useful when need to call kw-receiving functions -// without any keywords from C, etc. -const mp_map_t mp_const_empty_map = { - .all_keys_are_qstrs = 0, - .is_fixed = 1, - .is_ordered = 1, - .used = 0, - .alloc = 0, - .table = NULL, -}; - // This table of sizes is used to control the growth of hash tables. // The first set of sizes are chosen so the allocation fits exactly in a // 4-word GC block, and it's not so important for these small values to be diff --git a/py/misc.h b/py/misc.h index 7da8b35fc9..99fadde696 100644 --- a/py/misc.h +++ b/py/misc.h @@ -55,12 +55,8 @@ typedef unsigned int uint; // Static assertion macro #define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)])) -// Explicit fallthrough delcarations for case statements -#ifdef __GNUC__ -#define FALLTHROUGH __attribute__((fallthrough)) -#else -#define FALLTHROUGH ((void)0) /* FALLTHROUGH */ -#endif +// Round-up integer division +#define MP_CEIL_DIVIDE(a, b) (((a) + (b) - 1) / (b)) /** memory allocation ******************************************/ diff --git a/py/mkrules.mk b/py/mkrules.mk index a7563790dc..81dd961ac6 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -45,10 +45,26 @@ $(Q)$(CC) $(CFLAGS) -c -MD -o $@ $< $(RM) -f $(@:.o=.d) endef +define compile_cxx +$(ECHO) "CXX $<" +$(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< +@# The following fixes the dependency file. +@# See http://make.paulandlesley.org/autodep.html for details. +@# Regex adjusted from the above to play better with Windows paths, etc. +@$(CP) $(@:.o=.d) $(@:.o=.P); \ + $(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \ + $(RM) -f $(@:.o=.d) +endef + vpath %.c . $(TOP) $(USER_C_MODULES) $(DEVICES_MODULES) $(BUILD)/%.o: %.c $(call compile_c) +vpath %.cpp . $(TOP) $(USER_C_MODULES) +$(BUILD)/%.o: %.cpp + $(call compile_cxx) + QSTR_GEN_EXTRA_CFLAGS += -DNO_QSTR # frozen.c and frozen_mpy.c are created in $(BUILD), so use our rule @@ -75,7 +91,7 @@ $(BUILD)/%.pp: %.c # to get built before we try to compile any of them. $(OBJ): | $(HEADER_BUILD)/qstrdefs.enum.h $(HEADER_BUILD)/mpversion.h -# The logic for qstr regeneration is: +# The logic for qstr regeneration (applied by makeqstrdefs.py) is: # - if anything in QSTR_GLOBAL_DEPENDENCIES is newer, then process all source files ($^) # - else, if list of newer prerequisites ($?) is not empty, then process just these ($?) # - else, process all source files ($^) [this covers "make -B" which can set $? to empty] @@ -172,7 +188,7 @@ LIBMICROPYTHON = libmicropython.a # tracking. Then LIBMICROPYTHON_EXTRA_CMD can e.g. touch some # other file to cause needed effect, e.g. relinking with new lib. lib $(LIBMICROPYTHON): $(OBJ) - $(AR) rcs $(LIBMICROPYTHON) $^ + $(Q)$(AR) rcs $(LIBMICROPYTHON) $^ $(LIBMICROPYTHON_EXTRA_CMD) clean: diff --git a/py/modmath.c b/py/modmath.c index 045486884e..103310db5e 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -100,7 +100,19 @@ mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) { // sqrt(x): returns the square root of x MATH_FUN_1(sqrt, sqrt) // pow(x, y): returns x to the power of y +#if MICROPY_PY_MATH_POW_FIX_NAN +mp_float_t pow_func(mp_float_t x, mp_float_t y) { + // pow(base, 0) returns 1 for any base, even when base is NaN + // pow(+1, exponent) returns 1 for any exponent, even when exponent is NaN + if (x == MICROPY_FLOAT_CONST(1.0) || y == MICROPY_FLOAT_CONST(0.0)) { + return MICROPY_FLOAT_CONST(1.0); + } + return MICROPY_FLOAT_C_FUN(pow)(x, y); +} +MATH_FUN_2(pow, pow_func) +#else MATH_FUN_2(pow, pow) +#endif // exp(x) MATH_FUN_1(exp, exp) #if MICROPY_PY_MATH_SPECIAL_FUNCTIONS @@ -194,17 +206,15 @@ MATH_FUN_1(lgamma, lgamma) #if MICROPY_PY_MATH_ISCLOSE STATIC mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_a, ARG_b, ARG_rel_tol, ARG_abs_tol }; + enum { ARG_rel_tol, ARG_abs_tol }; static const mp_arg_t allowed_args[] = { - {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ}, - {MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ}, {MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, {MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - const mp_float_t a = mp_obj_get_float(args[ARG_a].u_obj); - const mp_float_t b = mp_obj_get_float(args[ARG_b].u_obj); + mp_arg_parse_all(n_args - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + const mp_float_t a = mp_obj_get_float(pos_args[0]); + const mp_float_t b = mp_obj_get_float(pos_args[1]); const mp_float_t rel_tol = args[ARG_rel_tol].u_obj == MP_OBJ_NULL ? (mp_float_t)1e-9 : mp_obj_get_float(args[ARG_rel_tol].u_obj); const mp_float_t abs_tol = mp_obj_get_float(args[ARG_abs_tol].u_obj); diff --git a/py/mpconfig.h b/py/mpconfig.h index cc5c5d154f..bb0bd35728 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -285,6 +285,11 @@ #define MICROPY_PERSISTENT_CODE_SAVE (0) #endif +// Whether to support saving persistent code to a file via mp_raw_code_save_file +#ifndef MICROPY_PERSISTENT_CODE_SAVE_FILE +#define MICROPY_PERSISTENT_CODE_SAVE_FILE (0) +#endif + // Whether generated code can persist independently of the VM/runtime instance // This is enabled automatically when needed by other features #ifndef MICROPY_PERSISTENT_CODE @@ -306,6 +311,11 @@ #define MICROPY_EMIT_THUMB (0) #endif +// Whether to emit ARMv7-M instruction support in thumb native code +#ifndef MICROPY_EMIT_THUMB_ARMV7M +#define MICROPY_EMIT_THUMB_ARMV7M (1) +#endif + // Whether to enable the thumb inline assembler #ifndef MICROPY_EMIT_INLINE_THUMB #define MICROPY_EMIT_INLINE_THUMB (0) @@ -461,6 +471,11 @@ #define MICROPY_DEBUG_MP_OBJ_SENTINELS (0) #endif +// Whether to print parse rule names (rather than integers) in mp_parse_node_print +#ifndef MICROPY_DEBUG_PARSE_RULE_NAME +#define MICROPY_DEBUG_PARSE_RULE_NAME (0) +#endif + // Whether to enable a simple VM stack overflow check #ifndef MICROPY_DEBUG_VM_STACK_OVERFLOW #define MICROPY_DEBUG_VM_STACK_OVERFLOW (0) @@ -1196,6 +1211,11 @@ typedef double mp_float_t; #define MICROPY_PY_MATH_MODF_FIX_NEGZERO (0) #endif +// Whether to provide fix for pow(1, NaN) and pow(NaN, 0), which both should be 1 not NaN. +#ifndef MICROPY_PY_MATH_POW_FIX_NAN +#define MICROPY_PY_MATH_POW_FIX_NAN (0) +#endif + // Whether to provide "cmath" module #ifndef MICROPY_PY_CMATH #define MICROPY_PY_CMATH (0) @@ -1649,6 +1669,13 @@ typedef double mp_float_t; #endif #endif +// Explicitly annotate switch case fall throughs +#if defined(__GNUC__) && __GNUC__ >= 7 +#define MP_FALLTHROUGH __attribute__((fallthrough)); +#else +#define MP_FALLTHROUGH +#endif + #ifndef MP_HTOBE16 #if MP_ENDIANNESS_LITTLE #define MP_HTOBE16(x) ((uint16_t)((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))) diff --git a/py/mphal.h b/py/mphal.h index 1916cecbf3..13aae19a71 100644 --- a/py/mphal.h +++ b/py/mphal.h @@ -76,7 +76,7 @@ mp_uint_t mp_hal_ticks_cpu(void); #endif #ifndef mp_hal_time_ns -// Nanoseconds since 1970/1/1. +// Nanoseconds since the Epoch. uint64_t mp_hal_time_ns(void); #endif diff --git a/py/mpprint.c b/py/mpprint.c index c91ef912f0..b99b5d658d 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -484,10 +484,10 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { qstr qst = va_arg(args, qstr); size_t len; const char *str = (const char *)qstr_data(qst, &len); - if (prec < 0) { - prec = len; + if (prec >= 0 && (size_t)prec < len) { + len = prec; } - chrs += mp_print_strn(print, str, prec, flags, fill, width); + chrs += mp_print_strn(print, str, len, flags, fill, width); break; } case 's': { @@ -499,10 +499,11 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { break; } #endif - if (prec < 0) { - prec = strlen(str); + size_t len = strlen(str); + if (prec >= 0 && (size_t)prec < len) { + len = prec; } - chrs += mp_print_strn(print, str, prec, flags, fill, width); + chrs += mp_print_strn(print, str, len, flags, fill, width); break; } case 'd': { @@ -557,11 +558,9 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { case 'l': { unsigned long long int arg_value = va_arg(args, unsigned long long int); ++fmt; - if (*fmt == 'u' || *fmt == 'd') { - chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width); - break; - } - assert(!"unsupported fmt char"); + assert(*fmt == 'u' || *fmt == 'd' || !"unsupported fmt char"); + chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width); + break; } #endif default: diff --git a/py/mpz.c b/py/mpz.c index 0ec4dc706e..22f99ec0b3 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1613,7 +1613,6 @@ bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) { return true; } -// writes at most len bytes to buf (so buf should be zeroed before calling) void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf) { byte *b = buf; if (big_endian) { @@ -1645,6 +1644,15 @@ void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf) { } } } + + // fill remainder of buf with zero/sign extension of the integer + if (big_endian) { + len = b - buf; + } else { + len = buf + len - b; + buf = b; + } + memset(buf, z->neg ? 0xff : 0x00, len); } #if MICROPY_PY_BUILTINS_FLOAT diff --git a/py/obj.h b/py/obj.h index 1e898cda08..c9135546f2 100644 --- a/py/obj.h +++ b/py/obj.h @@ -457,8 +457,6 @@ typedef enum _mp_map_lookup_kind_t { MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND = 3, // only valid for mp_set_lookup } mp_map_lookup_kind_t; -extern const mp_map_t mp_const_empty_map; - static inline bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) { assert(pos < map->alloc); return (map)->table[pos].key != MP_OBJ_NULL && (map)->table[pos].key != MP_OBJ_SENTINEL; @@ -732,17 +730,22 @@ extern const struct _mp_obj_bool_t mp_const_false_obj; extern const struct _mp_obj_bool_t mp_const_true_obj; #endif -// Constant objects, globally accessible: b'', (), Ellipsis, NotImplemented, GeneratorExit() +// Constant objects, globally accessible: b'', (), {}, Ellipsis, NotImplemented, GeneratorExit() // The below macros are for convenience only. #define mp_const_empty_bytes (MP_OBJ_FROM_PTR(&mp_const_empty_bytes_obj)) #define mp_const_empty_tuple (MP_OBJ_FROM_PTR(&mp_const_empty_tuple_obj)) #define mp_const_notimplemented (MP_OBJ_FROM_PTR(&mp_const_notimplemented_obj)) extern const struct _mp_obj_str_t mp_const_empty_bytes_obj; extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj; +extern const struct _mp_obj_dict_t mp_const_empty_dict_obj; extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj; extern const struct _mp_obj_singleton_t mp_const_notimplemented_obj; extern const struct _mp_obj_exception_t mp_const_GeneratorExit_obj; +// Fixed empty map. Useful when calling keyword-receiving functions +// without any keywords from C, etc. +#define mp_const_empty_map (mp_const_empty_dict_obj.map) + // General API for objects // These macros are derived from more primitive ones and are used to diff --git a/py/objarray.h b/py/objarray.h index 062de8b577..a1bf6abfd1 100644 --- a/py/objarray.h +++ b/py/objarray.h @@ -49,4 +49,14 @@ typedef struct _mp_obj_array_t { void *items; } mp_obj_array_t; +#if MICROPY_PY_BUILTINS_MEMORYVIEW +static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, size_t offset, size_t len, void *items) { + self->base.type = &mp_type_memoryview; + self->typecode = typecode; + self->free = offset; + self->len = len; + self->items = items; +} +#endif + #endif // MICROPY_INCLUDED_PY_OBJARRAY_H diff --git a/py/objdict.c b/py/objdict.c index cc5662d935..6641a02ad3 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -36,6 +36,18 @@ #include "supervisor/linker.h" #include "supervisor/shared/translate.h" +const mp_obj_dict_t mp_const_empty_dict_obj = { + .base = { .type = &mp_type_dict }, + .map = { + .all_keys_are_qstrs = 0, + .is_fixed = 1, + .is_ordered = 1, + .used = 0, + .alloc = 0, + .table = NULL, + } +}; + STATIC mp_obj_t dict_update(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs); // This is a helper function to iterate through a dictionary. The state of diff --git a/py/objexcept.c b/py/objexcept.c index 6a078bd5a4..aaacdd5500 100644 --- a/py/objexcept.c +++ b/py/objexcept.c @@ -171,7 +171,7 @@ mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type, size_t n_args, con // reserved room (after the traceback data) for a tuple with 1 element. // Otherwise we are free to use the whole buffer after the traceback data. if (o_tuple == NULL && mp_emergency_exception_buf_size >= - EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(n_args)) { + (mp_int_t)(EMG_BUF_TUPLE_OFFSET + EMG_BUF_TUPLE_SIZE(n_args))) { o_tuple = (mp_obj_tuple_t *) ((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TUPLE_OFFSET); } @@ -430,7 +430,7 @@ mp_obj_t mp_obj_new_exception_msg_vlist(const mp_obj_type_t *exc_type, const com // that buffer to store the string object and its data (at least 16 bytes for // the string data), reserving room at the start for the traceback and 1-tuple. if ((o_str == NULL || o_str_buf == NULL) - && mp_emergency_exception_buf_size >= EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16) { + && mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_STR_OFFSET + sizeof(mp_obj_str_t) + 16)) { used_emg_buf = true; o_str = (mp_obj_str_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_STR_OFFSET); @@ -563,7 +563,7 @@ void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qs self->traceback_data = m_new_maybe(size_t, TRACEBACK_ENTRY_LEN); if (self->traceback_data == NULL) { #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF - if (mp_emergency_exception_buf_size >= EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE) { + if (mp_emergency_exception_buf_size >= (mp_int_t)(EMG_BUF_TRACEBACK_OFFSET + EMG_BUF_TRACEBACK_SIZE)) { // There is room in the emergency buffer for traceback data size_t *tb = (size_t *)((uint8_t *)MP_STATE_VM(mp_emergency_exception_buf) + EMG_BUF_TRACEBACK_OFFSET); diff --git a/py/objfloat.c b/py/objfloat.c index 670a052f1e..f6dd31a463 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -298,13 +298,19 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t if (lhs_val == 0 && rhs_val < 0 && !isinf(rhs_val)) { goto zero_division_error; } - if (lhs_val < 0 && rhs_val != MICROPY_FLOAT_C_FUN(floor)(rhs_val)) { + if (lhs_val < 0 && rhs_val != MICROPY_FLOAT_C_FUN(floor)(rhs_val) && !isnan(rhs_val)) { #if MICROPY_PY_BUILTINS_COMPLEX return mp_obj_complex_binary_op(MP_BINARY_OP_POWER, lhs_val, 0, rhs_in); #else mp_raise_ValueError(MP_ERROR_TEXT("complex values not supported")); #endif } + #if MICROPY_PY_MATH_POW_FIX_NAN // Also see modmath.c. + if (lhs_val == MICROPY_FLOAT_CONST(1.0) || rhs_val == MICROPY_FLOAT_CONST(0.0)) { + lhs_val = MICROPY_FLOAT_CONST(1.0); + break; + } + #endif lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); break; case MP_BINARY_OP_DIVMOD: { diff --git a/py/objfun.c b/py/objfun.c index 3f6cfe0e20..81afb366c3 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -353,6 +353,10 @@ void mp_obj_fun_bc_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (attr == MP_QSTR___name__) { dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(self_in)); } + if (attr == MP_QSTR___globals__) { + mp_obj_fun_bc_t *self = MP_OBJ_TO_PTR(self_in); + dest[0] = MP_OBJ_FROM_PTR(self->globals); + } } #endif diff --git a/py/objint.c b/py/objint.c index 1157ebe3be..42836a8a26 100644 --- a/py/objint.c +++ b/py/objint.c @@ -515,8 +515,8 @@ STATIC MP_DEFINE_CONST_CLASSMETHOD_OBJ(int_from_bytes_obj, MP_ROM_PTR(&int_from_ STATIC mp_obj_t int_to_bytes(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_length, ARG_byteorder, ARG_signed }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_length, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_byteorder, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_length, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_byteorder, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_signed, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 52e8855b3f..b804ce28af 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -124,7 +124,6 @@ mp_obj_t mp_obj_int_from_bytes_impl(bool big_endian, size_t len, const byte *buf void mp_obj_int_to_bytes_impl(mp_obj_t self_in, bool big_endian, size_t len, byte *buf) { assert(mp_obj_is_type(self_in, &mp_type_int)); mp_obj_int_t *self = MP_OBJ_TO_PTR(self_in); - memset(buf, 0, len); mpz_as_bytes(&self->mpz, big_endian, len, buf); } diff --git a/py/objset.c b/py/objset.c index 9539781ff0..8ff047d852 100644 --- a/py/objset.c +++ b/py/objset.c @@ -447,6 +447,7 @@ STATIC mp_obj_t set_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } return MP_OBJ_NEW_SMALL_INT(hash); } + MP_FALLTHROUGH #endif /* FALLTHROUGH */ default: diff --git a/py/objstr.c b/py/objstr.c index e0897226de..2dfb2bab29 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -132,10 +132,10 @@ STATIC void str_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t bool is_bytes = true; #endif if (kind == PRINT_RAW || (!MICROPY_PY_BUILTINS_STR_UNICODE && kind == PRINT_STR && !is_bytes)) { - mp_printf(print, "%.*s", str_len, str_data); + print->print_strn(print->data, (const char *)str_data, str_len); } else { if (is_bytes) { - mp_print_str(print, "b"); + print->print_strn(print->data, "b", 1); } mp_str_print_quoted(print, str_data, str_len, is_bytes); } @@ -214,6 +214,10 @@ STATIC mp_obj_t bytes_make_new(const mp_obj_type_t *type_in, size_t n_args, cons return mp_const_empty_bytes; } + if (mp_obj_is_type(args[0], &mp_type_bytes)) { + return args[0]; + } + if (mp_obj_is_str(args[0])) { if (n_args < 2 || n_args > 3) { goto wrong_args; diff --git a/py/objstrunicode.c b/py/objstrunicode.c index c8a9691647..e969b3edca 100644 --- a/py/objstrunicode.c +++ b/py/objstrunicode.c @@ -94,7 +94,7 @@ STATIC void uni_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t } #endif if (kind == PRINT_STR) { - mp_printf(print, "%.*s", str_len, str_data); + print->print_strn(print->data, (const char *)str_data, str_len); } else { uni_print_quoted(print, str_data, str_len); } diff --git a/py/objtype.c b/py/objtype.c index 642c5d9096..336fcc341b 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -1060,13 +1060,16 @@ STATIC void type_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (attr == MP_QSTR___dict__) { // Returns a read-only dict of the class attributes. // If the internal locals is not fixed, a copy will be created. - mp_obj_dict_t *dict = self->locals_dict; + const mp_obj_dict_t *dict = self->locals_dict; + if (!dict) { + dict = &mp_const_empty_dict_obj; + } if (dict->map.is_fixed) { dest[0] = MP_OBJ_FROM_PTR(dict); } else { dest[0] = mp_obj_dict_copy(MP_OBJ_FROM_PTR(dict)); - dict = MP_OBJ_TO_PTR(dest[0]); - dict->map.is_fixed = 1; + mp_obj_dict_t *dict_copy = MP_OBJ_TO_PTR(dest[0]); + dict_copy->map.is_fixed = 1; } return; } diff --git a/py/parse.c b/py/parse.c index 3e0b4eb460..689125f117 100644 --- a/py/parse.c +++ b/py/parse.c @@ -57,9 +57,6 @@ #define RULE_ARG_RULE (0x2000) #define RULE_ARG_OPT_RULE (0x3000) -// (un)comment to use rule names; for debugging -// #define USE_RULE_NAME (1) - // *FORMAT-OFF* enum { @@ -194,7 +191,7 @@ static const size_t FIRST_RULE_WITH_OFFSET_ABOVE_255 = #undef DEF_RULE_NC 0; -#if defined(USE_RULE_NAME) && USE_RULE_NAME +#if MICROPY_DEBUG_PARSE_RULE_NAME // Define an array of rule names corresponding to each rule STATIC const char *const rule_name_table[] = { #define DEF_RULE(rule, comp, kind, ...) #rule, @@ -374,35 +371,35 @@ size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_ } #if MICROPY_DEBUG_PRINTERS -void mp_parse_node_print(mp_parse_node_t pn, size_t indent) { +void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t indent) { if (MP_PARSE_NODE_IS_STRUCT(pn)) { - printf("[% 4d] ", (int)((mp_parse_node_struct_t *)pn)->source_line); + mp_printf(print, "[% 4d] ", (int)((mp_parse_node_struct_t *)pn)->source_line); } else { - printf(" "); + mp_printf(print, " "); } for (size_t i = 0; i < indent; i++) { - printf(" "); + mp_printf(print, " "); } if (MP_PARSE_NODE_IS_NULL(pn)) { - printf("NULL\n"); + mp_printf(print, "NULL\n"); } else if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pn); - printf("int(" INT_FMT ")\n", arg); + mp_printf(print, "int(" INT_FMT ")\n", arg); } else if (MP_PARSE_NODE_IS_LEAF(pn)) { uintptr_t arg = MP_PARSE_NODE_LEAF_ARG(pn); switch (MP_PARSE_NODE_LEAF_KIND(pn)) { case MP_PARSE_NODE_ID: - printf("id(%s)\n", qstr_str(arg)); + mp_printf(print, "id(%s)\n", qstr_str(arg)); break; case MP_PARSE_NODE_STRING: - printf("str(%s)\n", qstr_str(arg)); + mp_printf(print, "str(%s)\n", qstr_str(arg)); break; case MP_PARSE_NODE_BYTES: - printf("bytes(%s)\n", qstr_str(arg)); + mp_printf(print, "bytes(%s)\n", qstr_str(arg)); break; default: assert(MP_PARSE_NODE_LEAF_KIND(pn) == MP_PARSE_NODE_TOKEN); - printf("tok(%u)\n", (uint)arg); + mp_printf(print, "tok(%u)\n", (uint)arg); break; } } else { @@ -410,19 +407,19 @@ void mp_parse_node_print(mp_parse_node_t pn, size_t indent) { mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; if (MP_PARSE_NODE_STRUCT_KIND(pns) == RULE_const_object) { #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D - printf("literal const(%016llx)\n", (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32)); + mp_printf(print, "literal const(%016llx)\n", (uint64_t)pns->nodes[0] | ((uint64_t)pns->nodes[1] << 32)); #else - printf("literal const(%p)\n", (mp_obj_t)pns->nodes[0]); + mp_printf(print, "literal const(%p)\n", (mp_obj_t)pns->nodes[0]); #endif } else { size_t n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns); - #if defined(USE_RULE_NAME) && USE_RULE_NAME - printf("%s(%u) (n=%u)\n", rule_name_table[MP_PARSE_NODE_STRUCT_KIND(pns)], (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); + #if MICROPY_DEBUG_PARSE_RULE_NAME + mp_printf(print, "%s(%u) (n=%u)\n", rule_name_table[MP_PARSE_NODE_STRUCT_KIND(pns)], (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); #else - printf("rule(%u) (n=%u)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); + mp_printf(print, "rule(%u) (n=%u)\n", (uint)MP_PARSE_NODE_STRUCT_KIND(pns), (uint)n); #endif for (size_t i = 0; i < n; i++) { - mp_parse_node_print(pns->nodes[i], indent + 2); + mp_parse_node_print(print, pns->nodes[i], indent + 2); } } } @@ -430,10 +427,10 @@ void mp_parse_node_print(mp_parse_node_t pn, size_t indent) { #endif // MICROPY_DEBUG_PRINTERS /* -STATIC void result_stack_show(parser_t *parser) { - printf("result stack, most recent first\n"); +STATIC void result_stack_show(const mp_print_t *print, parser_t *parser) { + mp_printf(print, "result stack, most recent first\n"); for (ssize_t i = parser->result_stack_top - 1; i >= 0; i--) { - mp_parse_node_print(parser->result_stack[i], 0); + mp_parse_node_print(print, parser->result_stack[i], 0); } } */ diff --git a/py/parse.h b/py/parse.h index 1239e6e5eb..5f1e30c2ff 100644 --- a/py/parse.h +++ b/py/parse.h @@ -86,7 +86,7 @@ bool mp_parse_node_is_const_false(mp_parse_node_t pn); bool mp_parse_node_is_const_true(mp_parse_node_t pn); bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o); size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_node_t **nodes); -void mp_parse_node_print(mp_parse_node_t pn, size_t indent); +void mp_parse_node_print(const mp_print_t *print, mp_parse_node_t pn, size_t indent); typedef enum { MP_PARSE_SINGLE_INPUT, diff --git a/py/persistentcode.c b/py/persistentcode.c index 6777341039..aac6907682 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -853,10 +853,7 @@ void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { save_raw_code(print, rc, &qw); } -// here we define mp_raw_code_save_file depending on the port -// TODO abstract this away properly - -#if defined(__i386__) || defined(__x86_64__) || defined(_WIN32) || defined(__unix__) +#if MICROPY_PERSISTENT_CODE_SAVE_FILE #include #include @@ -881,8 +878,6 @@ void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename) { MP_THREAD_GIL_ENTER(); } -#else -#error mp_raw_code_save_file not implemented for this platform -#endif +#endif // MICROPY_PERSISTENT_CODE_SAVE_FILE #endif // MICROPY_PERSISTENT_CODE_SAVE diff --git a/py/py.mk b/py/py.mk index 6bbb09de61..c4f7eec7c6 100644 --- a/py/py.mk +++ b/py/py.mk @@ -35,7 +35,9 @@ ifneq ($(USER_C_MODULES),) # pre-define USERMOD variables as expanded so that variables are immediate # expanded as they're added to them SRC_USERMOD := +SRC_USERMOD_CXX := CFLAGS_USERMOD := +CXXFLAGS_USERMOD := LDFLAGS_USERMOD := $(foreach module, $(wildcard $(USER_C_MODULES)/*/micropython.mk), \ $(eval USERMOD_DIR = $(patsubst %/,%,$(dir $(module))))\ @@ -44,7 +46,9 @@ $(foreach module, $(wildcard $(USER_C_MODULES)/*/micropython.mk), \ ) SRC_MOD += $(patsubst $(USER_C_MODULES)/%.c,%.c,$(SRC_USERMOD)) +SRC_MOD_CXX += $(patsubst $(USER_C_MODULES)/%.cpp,%.cpp,$(SRC_USERMOD_CXX)) CFLAGS_MOD += $(CFLAGS_USERMOD) +CXXFLAGS_MOD += $(CXXFLAGS_USERMOD) LDFLAGS_MOD += $(LDFLAGS_USERMOD) endif diff --git a/py/qstr.h b/py/qstr.h index 6326426b0f..7820de2df9 100644 --- a/py/qstr.h +++ b/py/qstr.h @@ -32,7 +32,7 @@ // See qstrdefs.h for a list of qstr's that are available as constants. // Reference them as MP_QSTR_xxxx. // -// Note: it would be possible to define MP_QSTR_xxx as qstr_from_str_static("xxx") +// Note: it would be possible to define MP_QSTR_xxx as qstr_from_str("xxx") // for qstrs that are referenced this way, but you don't want to have them in ROM. // first entry in enum will be MP_QSTRnull=0, which indicates invalid/no qstr @@ -73,7 +73,6 @@ typedef struct _qstr_pool_t { const char *qstrs[]; } qstr_pool_t; -#define QSTR_FROM_STR_STATIC(s) (qstr_from_strn((s), strlen(s))) #define QSTR_TOTAL() (MP_STATE_VM(last_pool)->total_prev_len + MP_STATE_VM(last_pool)->len) void qstr_init(void); diff --git a/py/scope.c b/py/scope.c index 1b2cb9fe82..f9308267d3 100644 --- a/py/scope.c +++ b/py/scope.c @@ -72,7 +72,7 @@ void scope_free(scope_t *scope) { m_del(scope_t, scope, 1); } -id_info_t *scope_find_or_add_id(scope_t *scope, qstr qst, scope_kind_t kind) { +id_info_t *scope_find_or_add_id(scope_t *scope, qstr qst, id_info_kind_t kind) { id_info_t *id_info = scope_find(scope, qst); if (id_info != NULL) { return id_info; diff --git a/py/scope.h b/py/scope.h index d9efd5711f..8b05421072 100644 --- a/py/scope.h +++ b/py/scope.h @@ -29,14 +29,14 @@ #include "py/parse.h" #include "py/emitglue.h" -enum { +typedef enum { ID_INFO_KIND_UNDECIDED, ID_INFO_KIND_GLOBAL_IMPLICIT, ID_INFO_KIND_GLOBAL_EXPLICIT, ID_INFO_KIND_LOCAL, // in a function f, written and only referenced by f ID_INFO_KIND_CELL, // in a function f, read/written by children of f ID_INFO_KIND_FREE, // in a function f, belongs to the parent of f -}; +} id_info_kind_t; enum { ID_FLAG_IS_PARAM = 0x01, @@ -92,7 +92,7 @@ typedef struct _scope_t { scope_t *scope_new(scope_kind_t kind, mp_parse_node_t pn, qstr source_file, mp_uint_t emit_options); void scope_free(scope_t *scope); -id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, scope_kind_t kind); +id_info_t *scope_find_or_add_id(scope_t *scope, qstr qstr, id_info_kind_t kind); id_info_t *scope_find(scope_t *scope, qstr qstr); id_info_t *scope_find_global(scope_t *scope, qstr qstr); void scope_check_to_close_over(scope_t *scope, id_info_t *id); diff --git a/py/showbc.c b/py/showbc.c index db3c58f3c7..284f134433 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -32,9 +32,6 @@ #if MICROPY_DEBUG_PRINTERS -// redirect all printfs in this file to the platform print stream -#define printf(...) mp_printf(&mp_plat_print, __VA_ARGS__) - #define DECODE_UINT { \ unum = 0; \ do { \ @@ -80,7 +77,7 @@ const byte *mp_showbc_code_start; const mp_uint_t *mp_showbc_const_table; -void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) { +void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *ip, mp_uint_t len, const mp_uint_t *const_table) { mp_showbc_code_start = ip; // Decode prelude @@ -96,30 +93,30 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m qstr block_name = mp_decode_uint(&code_info); qstr source_file = mp_decode_uint(&code_info); #endif - printf("File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n", + mp_printf(print, "File %s, code block '%s' (descriptor: %p, bytecode @%p " UINT_FMT " bytes)\n", qstr_str(source_file), qstr_str(block_name), descr, mp_showbc_code_start, len); // raw bytecode dump size_t prelude_size = ip - mp_showbc_code_start + n_info + n_cell; - printf("Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", + mp_printf(print, "Raw bytecode (code_info_size=" UINT_FMT ", bytecode_size=" UINT_FMT "):\n", prelude_size, len - prelude_size); for (mp_uint_t i = 0; i < len; i++) { if (i > 0 && i % 16 == 0) { - printf("\n"); + mp_printf(print, "\n"); } - printf(" %02x", mp_showbc_code_start[i]); + mp_printf(print, " %02x", mp_showbc_code_start[i]); } - printf("\n"); + mp_printf(print, "\n"); // bytecode prelude: arg names (as qstr objects) - printf("arg names:"); + mp_printf(print, "arg names:"); for (mp_uint_t i = 0; i < n_pos_args + n_kwonly_args; i++) { - printf(" %s", qstr_str(MP_OBJ_QSTR_VALUE(const_table[i]))); + mp_printf(print, " %s", qstr_str(MP_OBJ_QSTR_VALUE(const_table[i]))); } - printf("\n"); + mp_printf(print, "\n"); - printf("(N_STATE %u)\n", (unsigned)n_state); - printf("(N_EXC_STACK %u)\n", (unsigned)n_exc_stack); + mp_printf(print, "(N_STATE %u)\n", (unsigned)n_state); + mp_printf(print, "(N_EXC_STACK %u)\n", (unsigned)n_exc_stack); // skip over code_info ip += n_info; @@ -127,14 +124,14 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m // bytecode prelude: initialise closed over variables for (size_t i = 0; i < n_cell; ++i) { uint local_num = *ip++; - printf("(INIT_CELL %u)\n", local_num); + mp_printf(print, "(INIT_CELL %u)\n", local_num); } // print out line number info { mp_int_t bc = 0; mp_uint_t source_line = 1; - printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); + mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); for (const byte *ci = code_info; *ci;) { if ((ci[0] & 0x80) == 0) { // 0b0LLBBBBB encoding @@ -147,27 +144,27 @@ void mp_bytecode_print(const void *descr, const byte *ip, mp_uint_t len, const m source_line += ((ci[0] << 4) & 0x700) | ci[1]; ci += 2; } - printf(" bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); + mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); } } - mp_bytecode_print2(ip, len - prelude_size, const_table); + mp_bytecode_print2(print, ip, len - prelude_size, const_table); } -const byte *mp_bytecode_print_str(const byte *ip) { +const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip) { mp_uint_t unum; qstr qst; switch (*ip++) { case MP_BC_LOAD_CONST_FALSE: - printf("LOAD_CONST_FALSE"); + mp_printf(print, "LOAD_CONST_FALSE"); break; case MP_BC_LOAD_CONST_NONE: - printf("LOAD_CONST_NONE"); + mp_printf(print, "LOAD_CONST_NONE"); break; case MP_BC_LOAD_CONST_TRUE: - printf("LOAD_CONST_TRUE"); + mp_printf(print, "LOAD_CONST_TRUE"); break; case MP_BC_LOAD_CONST_SMALL_INT: { @@ -179,197 +176,197 @@ const byte *mp_bytecode_print_str(const byte *ip) { do { num = (num * 128) | (*ip & 0x7f); } while ((*ip++ & 0x80) != 0); - printf("LOAD_CONST_SMALL_INT " INT_FMT, num); + mp_printf(print, "LOAD_CONST_SMALL_INT " INT_FMT, num); break; } case MP_BC_LOAD_CONST_STRING: DECODE_QSTR; - printf("LOAD_CONST_STRING '%s'", qstr_str(qst)); + mp_printf(print, "LOAD_CONST_STRING '%s'", qstr_str(qst)); break; case MP_BC_LOAD_CONST_OBJ: DECODE_OBJ; - printf("LOAD_CONST_OBJ %p=", MP_OBJ_TO_PTR(unum)); - mp_obj_print_helper(&mp_plat_print, (mp_obj_t)unum, PRINT_REPR); + mp_printf(print, "LOAD_CONST_OBJ %p=", MP_OBJ_TO_PTR(unum)); + mp_obj_print_helper(print, (mp_obj_t)unum, PRINT_REPR); break; case MP_BC_LOAD_NULL: - printf("LOAD_NULL"); + mp_printf(print, "LOAD_NULL"); break; case MP_BC_LOAD_FAST_N: DECODE_UINT; - printf("LOAD_FAST_N " UINT_FMT, unum); + mp_printf(print, "LOAD_FAST_N " UINT_FMT, unum); break; case MP_BC_LOAD_DEREF: DECODE_UINT; - printf("LOAD_DEREF " UINT_FMT, unum); + mp_printf(print, "LOAD_DEREF " UINT_FMT, unum); break; case MP_BC_LOAD_NAME: DECODE_QSTR; - printf("LOAD_NAME %s", qstr_str(qst)); + mp_printf(print, "LOAD_NAME %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { - printf(" (cache=%u)", *ip++); + mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_LOAD_GLOBAL: DECODE_QSTR; - printf("LOAD_GLOBAL %s", qstr_str(qst)); + mp_printf(print, "LOAD_GLOBAL %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { - printf(" (cache=%u)", *ip++); + mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_LOAD_ATTR: DECODE_QSTR; - printf("LOAD_ATTR %s", qstr_str(qst)); + mp_printf(print, "LOAD_ATTR %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { - printf(" (cache=%u)", *ip++); + mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_LOAD_METHOD: DECODE_QSTR; - printf("LOAD_METHOD %s", qstr_str(qst)); + mp_printf(print, "LOAD_METHOD %s", qstr_str(qst)); break; case MP_BC_LOAD_SUPER_METHOD: DECODE_QSTR; - printf("LOAD_SUPER_METHOD %s", qstr_str(qst)); + mp_printf(print, "LOAD_SUPER_METHOD %s", qstr_str(qst)); break; case MP_BC_LOAD_BUILD_CLASS: - printf("LOAD_BUILD_CLASS"); + mp_printf(print, "LOAD_BUILD_CLASS"); break; case MP_BC_LOAD_SUBSCR: - printf("LOAD_SUBSCR"); + mp_printf(print, "LOAD_SUBSCR"); break; case MP_BC_STORE_FAST_N: DECODE_UINT; - printf("STORE_FAST_N " UINT_FMT, unum); + mp_printf(print, "STORE_FAST_N " UINT_FMT, unum); break; case MP_BC_STORE_DEREF: DECODE_UINT; - printf("STORE_DEREF " UINT_FMT, unum); + mp_printf(print, "STORE_DEREF " UINT_FMT, unum); break; case MP_BC_STORE_NAME: DECODE_QSTR; - printf("STORE_NAME %s", qstr_str(qst)); + mp_printf(print, "STORE_NAME %s", qstr_str(qst)); break; case MP_BC_STORE_GLOBAL: DECODE_QSTR; - printf("STORE_GLOBAL %s", qstr_str(qst)); + mp_printf(print, "STORE_GLOBAL %s", qstr_str(qst)); break; case MP_BC_STORE_ATTR: DECODE_QSTR; - printf("STORE_ATTR %s", qstr_str(qst)); + mp_printf(print, "STORE_ATTR %s", qstr_str(qst)); if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE) { - printf(" (cache=%u)", *ip++); + mp_printf(print, " (cache=%u)", *ip++); } break; case MP_BC_STORE_SUBSCR: - printf("STORE_SUBSCR"); + mp_printf(print, "STORE_SUBSCR"); break; case MP_BC_DELETE_FAST: DECODE_UINT; - printf("DELETE_FAST " UINT_FMT, unum); + mp_printf(print, "DELETE_FAST " UINT_FMT, unum); break; case MP_BC_DELETE_DEREF: DECODE_UINT; - printf("DELETE_DEREF " UINT_FMT, unum); + mp_printf(print, "DELETE_DEREF " UINT_FMT, unum); break; case MP_BC_DELETE_NAME: DECODE_QSTR; - printf("DELETE_NAME %s", qstr_str(qst)); + mp_printf(print, "DELETE_NAME %s", qstr_str(qst)); break; case MP_BC_DELETE_GLOBAL: DECODE_QSTR; - printf("DELETE_GLOBAL %s", qstr_str(qst)); + mp_printf(print, "DELETE_GLOBAL %s", qstr_str(qst)); break; case MP_BC_DUP_TOP: - printf("DUP_TOP"); + mp_printf(print, "DUP_TOP"); break; case MP_BC_DUP_TOP_TWO: - printf("DUP_TOP_TWO"); + mp_printf(print, "DUP_TOP_TWO"); break; case MP_BC_POP_TOP: - printf("POP_TOP"); + mp_printf(print, "POP_TOP"); break; case MP_BC_ROT_TWO: - printf("ROT_TWO"); + mp_printf(print, "ROT_TWO"); break; case MP_BC_ROT_THREE: - printf("ROT_THREE"); + mp_printf(print, "ROT_THREE"); break; case MP_BC_JUMP: DECODE_SLABEL; - printf("JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_POP_JUMP_IF_TRUE: DECODE_SLABEL; - printf("POP_JUMP_IF_TRUE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_JUMP_IF_TRUE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_POP_JUMP_IF_FALSE: DECODE_SLABEL; - printf("POP_JUMP_IF_FALSE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_JUMP_IF_FALSE " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_JUMP_IF_TRUE_OR_POP: DECODE_SLABEL; - printf("JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP_IF_TRUE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_JUMP_IF_FALSE_OR_POP: DECODE_SLABEL; - printf("JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "JUMP_IF_FALSE_OR_POP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_SETUP_WITH: DECODE_ULABEL; // loop-like labels are always forward - printf("SETUP_WITH " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_WITH " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_WITH_CLEANUP: - printf("WITH_CLEANUP"); + mp_printf(print, "WITH_CLEANUP"); break; case MP_BC_UNWIND_JUMP: DECODE_SLABEL; - printf("UNWIND_JUMP " UINT_FMT " %d", (mp_uint_t)(ip + unum - mp_showbc_code_start), *ip); + mp_printf(print, "UNWIND_JUMP " UINT_FMT " %d", (mp_uint_t)(ip + unum - mp_showbc_code_start), *ip); ip += 1; break; case MP_BC_SETUP_EXCEPT: DECODE_ULABEL; // except labels are always forward - printf("SETUP_EXCEPT " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_EXCEPT " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_SETUP_FINALLY: DECODE_ULABEL; // except labels are always forward - printf("SETUP_FINALLY " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "SETUP_FINALLY " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_END_FINALLY: @@ -377,169 +374,169 @@ const byte *mp_bytecode_print_str(const byte *ip) { // if TOS is an integer, does something else // if TOS is None, just pops it and continues // else error - printf("END_FINALLY"); + mp_printf(print, "END_FINALLY"); break; case MP_BC_GET_ITER: - printf("GET_ITER"); + mp_printf(print, "GET_ITER"); break; case MP_BC_GET_ITER_STACK: - printf("GET_ITER_STACK"); + mp_printf(print, "GET_ITER_STACK"); break; case MP_BC_FOR_ITER: DECODE_ULABEL; // the jump offset if iteration finishes; for labels are always forward - printf("FOR_ITER " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "FOR_ITER " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_POP_EXCEPT_JUMP: DECODE_ULABEL; // these labels are always forward - printf("POP_EXCEPT_JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); + mp_printf(print, "POP_EXCEPT_JUMP " UINT_FMT, (mp_uint_t)(ip + unum - mp_showbc_code_start)); break; case MP_BC_BUILD_TUPLE: DECODE_UINT; - printf("BUILD_TUPLE " UINT_FMT, unum); + mp_printf(print, "BUILD_TUPLE " UINT_FMT, unum); break; case MP_BC_BUILD_LIST: DECODE_UINT; - printf("BUILD_LIST " UINT_FMT, unum); + mp_printf(print, "BUILD_LIST " UINT_FMT, unum); break; case MP_BC_BUILD_MAP: DECODE_UINT; - printf("BUILD_MAP " UINT_FMT, unum); + mp_printf(print, "BUILD_MAP " UINT_FMT, unum); break; case MP_BC_STORE_MAP: - printf("STORE_MAP"); + mp_printf(print, "STORE_MAP"); break; case MP_BC_BUILD_SET: DECODE_UINT; - printf("BUILD_SET " UINT_FMT, unum); + mp_printf(print, "BUILD_SET " UINT_FMT, unum); break; #if MICROPY_PY_BUILTINS_SLICE case MP_BC_BUILD_SLICE: DECODE_UINT; - printf("BUILD_SLICE " UINT_FMT, unum); + mp_printf(print, "BUILD_SLICE " UINT_FMT, unum); break; #endif case MP_BC_STORE_COMP: DECODE_UINT; - printf("STORE_COMP " UINT_FMT, unum); + mp_printf(print, "STORE_COMP " UINT_FMT, unum); break; case MP_BC_UNPACK_SEQUENCE: DECODE_UINT; - printf("UNPACK_SEQUENCE " UINT_FMT, unum); + mp_printf(print, "UNPACK_SEQUENCE " UINT_FMT, unum); break; case MP_BC_UNPACK_EX: DECODE_UINT; - printf("UNPACK_EX " UINT_FMT, unum); + mp_printf(print, "UNPACK_EX " UINT_FMT, unum); break; case MP_BC_MAKE_FUNCTION: DECODE_PTR; - printf("MAKE_FUNCTION %p", (void *)(uintptr_t)unum); + mp_printf(print, "MAKE_FUNCTION %p", (void *)(uintptr_t)unum); break; case MP_BC_MAKE_FUNCTION_DEFARGS: DECODE_PTR; - printf("MAKE_FUNCTION_DEFARGS %p", (void *)(uintptr_t)unum); + mp_printf(print, "MAKE_FUNCTION_DEFARGS %p", (void *)(uintptr_t)unum); break; case MP_BC_MAKE_CLOSURE: { DECODE_PTR; mp_uint_t n_closed_over = *ip++; - printf("MAKE_CLOSURE %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); + mp_printf(print, "MAKE_CLOSURE %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); break; } case MP_BC_MAKE_CLOSURE_DEFARGS: { DECODE_PTR; mp_uint_t n_closed_over = *ip++; - printf("MAKE_CLOSURE_DEFARGS %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); + mp_printf(print, "MAKE_CLOSURE_DEFARGS %p " UINT_FMT, (void *)(uintptr_t)unum, n_closed_over); break; } case MP_BC_CALL_FUNCTION: DECODE_UINT; - printf("CALL_FUNCTION n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + mp_printf(print, "CALL_FUNCTION n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); break; case MP_BC_CALL_FUNCTION_VAR_KW: DECODE_UINT; - printf("CALL_FUNCTION_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + mp_printf(print, "CALL_FUNCTION_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); break; case MP_BC_CALL_METHOD: DECODE_UINT; - printf("CALL_METHOD n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + mp_printf(print, "CALL_METHOD n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); break; case MP_BC_CALL_METHOD_VAR_KW: DECODE_UINT; - printf("CALL_METHOD_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); + mp_printf(print, "CALL_METHOD_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff); break; case MP_BC_RETURN_VALUE: - printf("RETURN_VALUE"); + mp_printf(print, "RETURN_VALUE"); break; case MP_BC_RAISE_LAST: - printf("RAISE_LAST"); + mp_printf(print, "RAISE_LAST"); break; case MP_BC_RAISE_OBJ: - printf("RAISE_OBJ"); + mp_printf(print, "RAISE_OBJ"); break; case MP_BC_RAISE_FROM: - printf("RAISE_FROM"); + mp_printf(print, "RAISE_FROM"); break; case MP_BC_YIELD_VALUE: - printf("YIELD_VALUE"); + mp_printf(print, "YIELD_VALUE"); break; case MP_BC_YIELD_FROM: - printf("YIELD_FROM"); + mp_printf(print, "YIELD_FROM"); break; case MP_BC_IMPORT_NAME: DECODE_QSTR; - printf("IMPORT_NAME '%s'", qstr_str(qst)); + mp_printf(print, "IMPORT_NAME '%s'", qstr_str(qst)); break; case MP_BC_IMPORT_FROM: DECODE_QSTR; - printf("IMPORT_FROM '%s'", qstr_str(qst)); + mp_printf(print, "IMPORT_FROM '%s'", qstr_str(qst)); break; case MP_BC_IMPORT_STAR: - printf("IMPORT_STAR"); + mp_printf(print, "IMPORT_STAR"); break; default: if (ip[-1] < MP_BC_LOAD_CONST_SMALL_INT_MULTI + 64) { - printf("LOAD_CONST_SMALL_INT " INT_FMT, (mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16); + mp_printf(print, "LOAD_CONST_SMALL_INT " INT_FMT, (mp_int_t)ip[-1] - MP_BC_LOAD_CONST_SMALL_INT_MULTI - 16); } else if (ip[-1] < MP_BC_LOAD_FAST_MULTI + 16) { - printf("LOAD_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_LOAD_FAST_MULTI); + mp_printf(print, "LOAD_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_LOAD_FAST_MULTI); } else if (ip[-1] < MP_BC_STORE_FAST_MULTI + 16) { - printf("STORE_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_STORE_FAST_MULTI); + mp_printf(print, "STORE_FAST " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_STORE_FAST_MULTI); } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NUM_BYTECODE) { - printf("UNARY_OP " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_UNARY_OP_MULTI); + mp_printf(print, "UNARY_OP " UINT_FMT, (mp_uint_t)ip[-1] - MP_BC_UNARY_OP_MULTI); } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + MP_BINARY_OP_NUM_BYTECODE) { mp_uint_t op = ip[-1] - MP_BC_BINARY_OP_MULTI; - printf("BINARY_OP " UINT_FMT " %s", op, qstr_str(mp_binary_op_method_name[op])); + mp_printf(print, "BINARY_OP " UINT_FMT " %s", op, qstr_str(mp_binary_op_method_name[op])); } else { - printf("code %p, byte code 0x%02x not implemented\n", ip - 1, ip[-1]); + mp_printf(print, "code %p, byte code 0x%02x not implemented\n", ip - 1, ip[-1]); assert(0); return ip; } @@ -549,13 +546,13 @@ const byte *mp_bytecode_print_str(const byte *ip) { return ip; } -void mp_bytecode_print2(const byte *ip, size_t len, const mp_uint_t *const_table) { +void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, const mp_uint_t *const_table) { mp_showbc_code_start = ip; mp_showbc_const_table = const_table; while (ip < len + mp_showbc_code_start) { - printf("%02u ", (uint)(ip - mp_showbc_code_start)); - ip = mp_bytecode_print_str(ip); - printf("\n"); + mp_printf(print, "%02u ", (uint)(ip - mp_showbc_code_start)); + ip = mp_bytecode_print_str(print, ip); + mp_printf(print, "\n"); } } diff --git a/py/vm.c b/py/vm.c index 05d0744e37..aec02c1610 100644 --- a/py/vm.c +++ b/py/vm.c @@ -41,7 +41,7 @@ // *FORMAT-OFF* #if 0 -#define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(ip, 1, code_state->fun_bc->const_table); +#define TRACE(ip) printf("sp=%d ", (int)(sp - &code_state->state[0] + 1)); mp_bytecode_print2(&mp_plat_print, ip, 1, code_state->fun_bc->const_table); #else #define TRACE(ip) #endif diff --git a/py/vmentrytable.h b/py/vmentrytable.h index 63b525d48e..b270dc9845 100644 --- a/py/vmentrytable.h +++ b/py/vmentrytable.h @@ -30,6 +30,10 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Winitializer-overrides" #endif // __clang__ +#if __GNUC__ >= 5 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverride-init" +#endif // __GNUC__ >= 5 #include "supervisor/linker.h" @@ -129,3 +133,6 @@ static entry_table_type const PLACE_IN_DTCM_DATA(entry_table[256]) = { #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ +#if __GNUC__ >= 5 +#pragma GCC diagnostic pop +#endif // __GNUC__ >= 5 diff --git a/shared-module/bitops/__init__.c b/shared-module/bitops/__init__.c index 87665809d0..7e1cf7d322 100644 --- a/shared-module/bitops/__init__.c +++ b/shared-module/bitops/__init__.c @@ -30,11 +30,7 @@ #include #include -#ifdef __GNUC__ -#define FALLTHROUGH __attribute__((fallthrough)) -#else -#define FALLTHROUGH ((void)0) /* FALLTHROUGH */ -#endif +#include "py/mpconfig.h" // adapted from "Hacker's Delight" - Figure 7-2 Transposing an 8x8-bit matrix // basic idea is: @@ -55,23 +51,23 @@ static void transpose_var(uint32_t *result, const uint8_t *src, int src_stride, case 7: x |= *src << 16; src -= src_stride; - FALLTHROUGH; + MP_FALLTHROUGH; case 6: x |= *src << 8; src -= src_stride; - FALLTHROUGH; + MP_FALLTHROUGH; case 5: x |= *src; src -= src_stride; - FALLTHROUGH; + MP_FALLTHROUGH; case 4: y |= *src << 24; src -= src_stride; - FALLTHROUGH; + MP_FALLTHROUGH; case 3: y |= *src << 16; src -= src_stride; - FALLTHROUGH; + MP_FALLTHROUGH; case 2: y |= *src << 8; src -= src_stride; diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index 47cb903f3f..6de7fe7883 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -156,7 +156,7 @@ void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _d switch (self->input_colorspace) { case DISPLAYIO_COLORSPACE_RGB565_SWAPPED: pixel = __builtin_bswap16(pixel); - FALLTHROUGH; + MP_FALLTHROUGH; case DISPLAYIO_COLORSPACE_RGB565: { uint32_t r8 = (pixel >> 11) << 3; uint32_t g8 = ((pixel >> 5) << 2) & 0xff; @@ -167,7 +167,7 @@ void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _d case DISPLAYIO_COLORSPACE_RGB555_SWAPPED: pixel = __builtin_bswap16(pixel); - FALLTHROUGH; + MP_FALLTHROUGH; case DISPLAYIO_COLORSPACE_RGB555: { uint32_t r8 = (pixel >> 10) << 3; uint32_t g8 = ((pixel >> 5) << 3) & 0xff; diff --git a/tests/basics/class_dict.py b/tests/basics/class_dict.py index f80ded678b..508ae5e2e5 100644 --- a/tests/basics/class_dict.py +++ b/tests/basics/class_dict.py @@ -17,3 +17,8 @@ class Foo: d = Foo.__dict__ print(d["a"], d["b"]) + + +# dict of a class that has no locals_dict (return empty dict). +d = type(type('')).__dict__ +print(d is not None) diff --git a/tests/basics/class_inplace_op2.py b/tests/basics/class_inplace_op2.py new file mode 100644 index 0000000000..0e3f2add41 --- /dev/null +++ b/tests/basics/class_inplace_op2.py @@ -0,0 +1,73 @@ +# Test inplace special methods enabled by MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS + + +class A: + def __imul__(self, other): + print("__imul__") + return self + + def __imatmul__(self, other): + print("__imatmul__") + return self + + def __ifloordiv__(self, other): + print("__ifloordiv__") + return self + + def __itruediv__(self, other): + print("__itruediv__") + return self + + def __imod__(self, other): + print("__imod__") + return self + + def __ipow__(self, other): + print("__ipow__") + return self + + def __ior__(self, other): + print("__ior__") + return self + + def __ixor__(self, other): + print("__ixor__") + return self + + def __iand__(self, other): + print("__iand__") + return self + + def __ilshift__(self, other): + print("__ilshift__") + return self + + def __irshift__(self, other): + print("__irshift__") + return self + + +a = A() + +try: + a *= None +except TypeError: + print("SKIP") + raise SystemExit + +a @= None +a //= None +a /= None +a %= None +a **= None +a |= None +a ^= None +a &= None +a <<= None +a >>= None + +# Normal operator should not fallback to inplace operator +try: + a * None +except TypeError: + print("TypeError") diff --git a/tests/basics/class_inplace_op2.py.exp b/tests/basics/class_inplace_op2.py.exp new file mode 100644 index 0000000000..8c323b5178 --- /dev/null +++ b/tests/basics/class_inplace_op2.py.exp @@ -0,0 +1,12 @@ +__imul__ +__imatmul__ +__ifloordiv__ +__itruediv__ +__imod__ +__ipow__ +__ior__ +__ixor__ +__iand__ +__ilshift__ +__irshift__ +TypeError diff --git a/tests/basics/containment.py b/tests/basics/containment.py index 4c94a9bae4..24317ddd23 100644 --- a/tests/basics/containment.py +++ b/tests/basics/containment.py @@ -1,8 +1,8 @@ # sets, see set_containment for i in 1, 2: for o in {1:2}, {1:2}.keys(): - print("{} in {}: {}".format(i, o, i in o)) - print("{} not in {}: {}".format(i, o, i not in o)) + print("{} in {}: {}".format(i, str(o), i in o)) + print("{} not in {}: {}".format(i, str(o), i not in o)) haystack = "supercalifragilistc" for needle in [haystack[i:] for i in range(len(haystack))]: diff --git a/tests/basics/fun_globals.py b/tests/basics/fun_globals.py new file mode 100644 index 0000000000..3f32e8bdb0 --- /dev/null +++ b/tests/basics/fun_globals.py @@ -0,0 +1,21 @@ +# test the __globals__ attribute of a function + + +def foo(): + pass + + +if not hasattr(foo, "__globals__"): + print("SKIP") + raise SystemExit + +print(type(foo.__globals__)) +print(foo.__globals__ is globals()) + +foo.__globals__["bar"] = 123 +print(bar) + +try: + foo.__globals__ = None +except AttributeError: + print("AttributeError") diff --git a/tests/basics/special_methods.py b/tests/basics/special_methods.py index b56bc1c9c4..d8af8e0797 100644 --- a/tests/basics/special_methods.py +++ b/tests/basics/special_methods.py @@ -100,6 +100,10 @@ cud1 = Cud() cud2 = Cud() str(cud1) +cud1 == cud1 +cud1 == cud2 +cud1 != cud1 +cud1 != cud2 cud1 < cud2 cud1 <= cud2 cud1 == cud2 diff --git a/tests/basics/special_methods2.py b/tests/basics/special_methods2.py index 09e43fff29..31f330ab42 100644 --- a/tests/basics/special_methods2.py +++ b/tests/basics/special_methods2.py @@ -129,12 +129,3 @@ print(dir(cud1)) # test that dir() does not delegate to __dir__ for the type print('a' in dir(Cud)) - -# TODO: the following operations are not supported on every ports -# -# ne is not supported, !(eq) is called instead -#cud1 != cud2 -# -# in the following test, cpython still calls __eq__ -# cud3=cud1 -# cud3==cud1 diff --git a/tests/basics/struct1_intbig.py b/tests/basics/struct1_intbig.py index 380293f36c..24541c8a42 100644 --- a/tests/basics/struct1_intbig.py +++ b/tests/basics/struct1_intbig.py @@ -36,3 +36,11 @@ print(struct.unpack("": + for type_ in "bhiq": + fmt = endian + type_ + b = struct.pack(fmt, -2 + bigzero) + print(fmt, b, struct.unpack(fmt, b)) diff --git a/tests/cmdline/cmd_parsetree.py.exp b/tests/cmdline/cmd_parsetree.py.exp index ce187a0701..abf547bc03 100644 --- a/tests/cmdline/cmd_parsetree.py.exp +++ b/tests/cmdline/cmd_parsetree.py.exp @@ -1,31 +1,31 @@ ---------------- -[ 4] rule(1) (n=9) +[ 4] \(rule\|file_input_2\)(1) (n=9) tok(10) -[ 4] rule(22) (n=4) +[ 4] \(rule\|for_stmt\)(22) (n=4) id(i) -[ 4] rule(45) (n=1) +[ 4] \(rule\|atom_paren\)(45) (n=1) NULL -[ 5] rule(8) (n=0) +[ 5] \(rule\|pass_stmt\)(8) (n=0) NULL -[ 6] rule(5) (n=2) +[ 6] \(rule\|expr_stmt\)(5) (n=2) id(a) tok(20) -[ 7] rule(5) (n=2) +[ 7] \(rule\|expr_stmt\)(5) (n=2) id(b) str(str) -[ 8] rule(5) (n=2) +[ 8] \(rule\|expr_stmt\)(5) (n=2) id(c) [ 8] literal \.\+ -[ 9] rule(5) (n=2) +[ 9] \(rule\|expr_stmt\)(5) (n=2) id(d) bytes(bytes) -[ 10] rule(5) (n=2) +[ 10] \(rule\|expr_stmt\)(5) (n=2) id(e) [ 10] literal \.\+ -[ 11] rule(5) (n=2) +[ 11] \(rule\|expr_stmt\)(5) (n=2) id(f) [ 11] literal \.\+ -[ 12] rule(5) (n=2) +[ 12] \(rule\|expr_stmt\)(5) (n=2) id(g) int(123) ---------------- diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp index 7054df449e..e13bf0abed 100644 --- a/tests/cmdline/cmd_showbc.py.exp +++ b/tests/cmdline/cmd_showbc.py.exp @@ -1,7 +1,7 @@ File cmdline/cmd_showbc.py, code block '' (descriptor: \.\+, bytecode @\.\+ bytes) Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+): ######## -\.\+63 +\.\+51 63 arg names: (N_STATE 3) (N_EXC_STACK 0) diff --git a/tests/extmod/uasyncio_set_exception_handler.py b/tests/extmod/uasyncio_set_exception_handler.py index ad62a79b7b..fe7b83eb4d 100644 --- a/tests/extmod/uasyncio_set_exception_handler.py +++ b/tests/extmod/uasyncio_set_exception_handler.py @@ -32,13 +32,23 @@ async def main(): # Create a task that raises and uses the custom exception handler asyncio.create_task(task(0)) print("sleep") - await asyncio.sleep(0) + for _ in range(2): + await asyncio.sleep(0) # Create 2 tasks to test order of printing exception asyncio.create_task(task(1)) asyncio.create_task(task(2)) print("sleep") + for _ in range(2): + await asyncio.sleep(0) + + # Create a task, let it run, then await it (no exception should be printed) + t = asyncio.create_task(task(3)) await asyncio.sleep(0) + try: + await t + except ValueError as er: + print(repr(er)) print("done") diff --git a/tests/extmod/uasyncio_set_exception_handler.py.exp b/tests/extmod/uasyncio_set_exception_handler.py.exp index 4744641e54..fb4711469d 100644 --- a/tests/extmod/uasyncio_set_exception_handler.py.exp +++ b/tests/extmod/uasyncio_set_exception_handler.py.exp @@ -5,4 +5,5 @@ custom_handler ValueError(0, 1) sleep custom_handler ValueError(1, 2) custom_handler ValueError(2, 3) +ValueError(3, 4) done diff --git a/tests/extmod/uasyncio_task_done.py b/tests/extmod/uasyncio_task_done.py new file mode 100644 index 0000000000..2700da8c34 --- /dev/null +++ b/tests/extmod/uasyncio_task_done.py @@ -0,0 +1,66 @@ +# Test the Task.done() method + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def task(t, exc=None): + print("task start") + if t >= 0: + await asyncio.sleep(t) + if exc: + raise exc + print("task done") + + +async def main(): + # Task that finishes immediately. + print("=" * 10) + t = asyncio.create_task(task(-1)) + print(t.done()) + await asyncio.sleep(0) + print(t.done()) + await t + print(t.done()) + + # Task that starts, runs and finishes. + print("=" * 10) + t = asyncio.create_task(task(0.01)) + print(t.done()) + await asyncio.sleep(0) + print(t.done()) + await t + print(t.done()) + + # Task that raises immediately. + print("=" * 10) + t = asyncio.create_task(task(-1, ValueError)) + print(t.done()) + await asyncio.sleep(0) + print(t.done()) + try: + await t + except ValueError as er: + print(repr(er)) + print(t.done()) + + # Task that raises after a delay. + print("=" * 10) + t = asyncio.create_task(task(0.01, ValueError)) + print(t.done()) + await asyncio.sleep(0) + print(t.done()) + try: + await t + except ValueError as er: + print(repr(er)) + print(t.done()) + + +asyncio.run(main()) diff --git a/tests/extmod/uasyncio_task_done.py.exp b/tests/extmod/uasyncio_task_done.py.exp new file mode 100644 index 0000000000..ddda04c5ec --- /dev/null +++ b/tests/extmod/uasyncio_task_done.py.exp @@ -0,0 +1,24 @@ +========== +False +task start +task done +True +True +========== +False +task start +False +task done +True +========== +False +task start +True +ValueError() +True +========== +False +task start +False +ValueError() +True diff --git a/tests/extmod/uasyncio_wait_for.py b/tests/extmod/uasyncio_wait_for.py index 92fd174b84..9612d16204 100644 --- a/tests/extmod/uasyncio_wait_for.py +++ b/tests/extmod/uasyncio_wait_for.py @@ -31,30 +31,85 @@ async def task_raise(): raise ValueError +async def task_cancel_other(t, other): + print("task_cancel_other start") + await asyncio.sleep(t) + print("task_cancel_other cancel") + other.cancel() + + +async def task_wait_for_cancel(id, t, t_wait): + print("task_wait_for_cancel start") + try: + await asyncio.wait_for(task(id, t), t_wait) + except asyncio.CancelledError as er: + print("task_wait_for_cancel cancelled") + raise er + + +async def task_wait_for_cancel_ignore(t_wait): + print("task_wait_for_cancel_ignore start") + try: + await asyncio.wait_for(task_catch(), t_wait) + except asyncio.CancelledError as er: + print("task_wait_for_cancel_ignore cancelled") + raise er + + async def main(): + sep = "-" * 10 + # When task finished before the timeout print(await asyncio.wait_for(task(1, 0.01), 10)) + print(sep) # When timeout passes and task is cancelled try: print(await asyncio.wait_for(task(2, 10), 0.01)) except asyncio.TimeoutError: print("timeout") + print(sep) # When timeout passes and task is cancelled, but task ignores the cancellation request try: print(await asyncio.wait_for(task_catch(), 0.1)) except asyncio.TimeoutError: print("TimeoutError") + print(sep) # When task raises an exception try: print(await asyncio.wait_for(task_raise(), 1)) except ValueError: print("ValueError") + print(sep) # Timeout of None means wait forever print(await asyncio.wait_for(task(3, 0.1), None)) + print(sep) + + # When task is cancelled by another task + t = asyncio.create_task(task(4, 10)) + asyncio.create_task(task_cancel_other(0.01, t)) + try: + print(await asyncio.wait_for(t, 1)) + except asyncio.CancelledError as er: + print(repr(er)) + print(sep) + + # When wait_for gets cancelled + t = asyncio.create_task(task_wait_for_cancel(4, 1, 2)) + await asyncio.sleep(0.01) + t.cancel() + await asyncio.sleep(0.01) + print(sep) + + # When wait_for gets cancelled and awaited task ignores the cancellation request + t = asyncio.create_task(task_wait_for_cancel_ignore(2)) + await asyncio.sleep(0.01) + t.cancel() + await asyncio.sleep(0.01) + print(sep) print("finish") diff --git a/tests/extmod/uasyncio_wait_for.py.exp b/tests/extmod/uasyncio_wait_for.py.exp index 41a5f2481e..a4201d31ff 100644 --- a/tests/extmod/uasyncio_wait_for.py.exp +++ b/tests/extmod/uasyncio_wait_for.py.exp @@ -1,15 +1,35 @@ task start 1 task end 1 2 +---------- task start 2 timeout +---------- task_catch start ignore cancel task_catch done TimeoutError +---------- task start ValueError +---------- task start 3 task end 3 6 +---------- +task start 4 +task_cancel_other start +task_cancel_other cancel +CancelledError() +---------- +task_wait_for_cancel start +task start 4 +task_wait_for_cancel cancelled +---------- +task_wait_for_cancel_ignore start +task_catch start +task_wait_for_cancel_ignore cancelled +ignore cancel +task_catch done +---------- finish diff --git a/tests/extmod/uasyncio_wait_for_fwd.py b/tests/extmod/uasyncio_wait_for_fwd.py new file mode 100644 index 0000000000..33738085ce --- /dev/null +++ b/tests/extmod/uasyncio_wait_for_fwd.py @@ -0,0 +1,60 @@ +# Test asyncio.wait_for, with forwarding cancellation + +try: + import uasyncio as asyncio +except ImportError: + try: + import asyncio + except ImportError: + print("SKIP") + raise SystemExit + + +async def awaiting(t, return_if_fail): + try: + print("awaiting started") + await asyncio.sleep(t) + except asyncio.CancelledError as er: + # CPython wait_for raises CancelledError inside task but TimeoutError in wait_for + print("awaiting canceled") + if return_if_fail: + return False # return has no effect if Cancelled + else: + raise er + except Exception as er: + print("caught exception", er) + raise er + + +async def test_cancellation_forwarded(catch, catch_inside): + print("----------") + + async def wait(): + try: + await asyncio.wait_for(awaiting(2, catch_inside), 1) + except asyncio.TimeoutError as er: + print("Got timeout error") + raise er + except asyncio.CancelledError as er: + print("Got canceled") + if not catch: + raise er + + async def cancel(t): + print("cancel started") + await asyncio.sleep(0.01) + print("cancel wait()") + t.cancel() + + t = asyncio.create_task(wait()) + k = asyncio.create_task(cancel(t)) + try: + await t + except asyncio.CancelledError: + print("waiting got cancelled") + + +asyncio.run(test_cancellation_forwarded(False, False)) +asyncio.run(test_cancellation_forwarded(False, True)) +asyncio.run(test_cancellation_forwarded(True, True)) +asyncio.run(test_cancellation_forwarded(True, False)) diff --git a/tests/extmod/uasyncio_wait_for_fwd.py.exp b/tests/extmod/uasyncio_wait_for_fwd.py.exp new file mode 100644 index 0000000000..9f22f1a7d1 --- /dev/null +++ b/tests/extmod/uasyncio_wait_for_fwd.py.exp @@ -0,0 +1,26 @@ +---------- +cancel started +awaiting started +cancel wait() +Got canceled +awaiting canceled +waiting got cancelled +---------- +cancel started +awaiting started +cancel wait() +Got canceled +awaiting canceled +waiting got cancelled +---------- +cancel started +awaiting started +cancel wait() +Got canceled +awaiting canceled +---------- +cancel started +awaiting started +cancel wait() +Got canceled +awaiting canceled diff --git a/tests/extmod/uctypes_array_load_store.py b/tests/extmod/uctypes_array_load_store.py new file mode 100644 index 0000000000..0a82d78960 --- /dev/null +++ b/tests/extmod/uctypes_array_load_store.py @@ -0,0 +1,22 @@ +# Test uctypes array, load and store, with array size > 1 + +try: + import uctypes +except ImportError: + print("SKIP") + raise SystemExit + +N = 5 + +for endian in ("NATIVE", "LITTLE_ENDIAN", "BIG_ENDIAN"): + for type_ in ("INT8", "UINT8", "INT16", "UINT16", "INT32", "UINT32", "INT64", "UINT64"): + desc = {"arr": (uctypes.ARRAY | 0, getattr(uctypes, type_) | N)} + sz = uctypes.sizeof(desc) + data = bytearray(sz) + s = uctypes.struct(uctypes.addressof(data), desc, getattr(uctypes, endian)) + for i in range(N): + try: + s.arr[i] = i - 2 + except OverflowError: + print("OverflowError") + print(endian, type_, sz, *(s.arr[i] for i in range(N))) diff --git a/tests/extmod/uctypes_array_load_store.py.exp b/tests/extmod/uctypes_array_load_store.py.exp new file mode 100644 index 0000000000..1e914cd015 --- /dev/null +++ b/tests/extmod/uctypes_array_load_store.py.exp @@ -0,0 +1,42 @@ +NATIVE INT8 5 -2 -1 0 1 2 +OverflowError +OverflowError +NATIVE UINT8 5 0 0 0 1 2 +NATIVE INT16 10 -2 -1 0 1 2 +NATIVE UINT16 10 65534 65535 0 1 2 +NATIVE INT32 20 -2 -1 0 1 2 +NATIVE UINT32 20 4294967294 4294967295 0 1 2 +NATIVE INT64 40 -2 -1 0 1 2 +NATIVE UINT64 40 18446744073709551614 18446744073709551615 0 1 2 +LITTLE_ENDIAN INT8 5 -2 -1 0 1 2 +OverflowError +OverflowError +LITTLE_ENDIAN UINT8 5 0 0 0 1 2 +LITTLE_ENDIAN INT16 10 -2 -1 0 1 2 +OverflowError +OverflowError +LITTLE_ENDIAN UINT16 10 0 0 0 1 2 +LITTLE_ENDIAN INT32 20 -2 -1 0 1 2 +OverflowError +OverflowError +LITTLE_ENDIAN UINT32 20 0 0 0 1 2 +LITTLE_ENDIAN INT64 40 -2 -1 0 1 2 +OverflowError +OverflowError +LITTLE_ENDIAN UINT64 40 0 0 0 1 2 +BIG_ENDIAN INT8 5 -2 -1 0 1 2 +OverflowError +OverflowError +BIG_ENDIAN UINT8 5 0 0 0 1 2 +BIG_ENDIAN INT16 10 -2 -1 0 1 2 +OverflowError +OverflowError +BIG_ENDIAN UINT16 10 0 0 0 1 2 +BIG_ENDIAN INT32 20 -2 -1 0 1 2 +OverflowError +OverflowError +BIG_ENDIAN UINT32 20 0 0 0 1 2 +BIG_ENDIAN INT64 40 -2 -1 0 1 2 +OverflowError +OverflowError +BIG_ENDIAN UINT64 40 0 0 0 1 2 diff --git a/tests/extmod/urandom_seed_default.py b/tests/extmod/urandom_seed_default.py new file mode 100644 index 0000000000..a032b9362b --- /dev/null +++ b/tests/extmod/urandom_seed_default.py @@ -0,0 +1,30 @@ +# test urandom.seed() without any arguments + +try: + import urandom as random +except ImportError: + try: + import random + except ImportError: + print("SKIP") + raise SystemExit + +try: + random.seed() +except ValueError: + # no default seed on this platform + print("SKIP") + raise SystemExit + + +def rng_seq(): + return [random.getrandbits(16) for _ in range(10)] + + +# seed with default and check that doesn't produce the same RNG sequence +random.seed() +seq = rng_seq() +random.seed() +print(seq == rng_seq()) +random.seed(None) +print(seq == rng_seq()) diff --git a/tests/extmod/ure_sub.py b/tests/extmod/ure_sub.py index 953e7bf62a..ae6ad28d62 100644 --- a/tests/extmod/ure_sub.py +++ b/tests/extmod/ure_sub.py @@ -43,6 +43,9 @@ print( ) ) +# \g immediately followed by another \g +print(re.sub("(abc)", r"\g<1>\g<1>", "abc")) + # no matches at all print(re.sub("a", "b", "c")) @@ -69,3 +72,6 @@ try: re.sub(123, "a", "a") except TypeError: print("TypeError") + +# Include \ in the sub replacement +print(re.sub("b", "\\\\b", "abc")) diff --git a/tests/extmod/utime_res.py b/tests/extmod/utime_res.py new file mode 100644 index 0000000000..4b62433483 --- /dev/null +++ b/tests/extmod/utime_res.py @@ -0,0 +1,65 @@ +# test utime resolutions + +try: + import utime +except ImportError: + print("SKIP") + raise SystemExit + + +def gmtime_time(): + return utime.gmtime(utime.time()) + + +def localtime_time(): + return utime.localtime(utime.time()) + + +def test(): + TEST_TIME = 2500 + EXPECTED_MAP = ( + # (function name, min. number of results in 2.5 sec) + ("time", 3), + ("gmtime", 3), + ("localtime", 3), + ("gmtime_time", 3), + ("localtime_time", 3), + ("ticks_ms", 15), + ("ticks_us", 15), + ("ticks_ns", 15), + ("ticks_cpu", 15), + ) + + # call time functions + results_map = {} + end_time = utime.ticks_ms() + TEST_TIME + while utime.ticks_diff(end_time, utime.ticks_ms()) > 0: + utime.sleep_ms(100) + for func_name, _ in EXPECTED_MAP: + try: + time_func = getattr(utime, func_name, None) or globals()[func_name] + now = time_func() # may raise AttributeError + except (KeyError, AttributeError): + continue + try: + results_map[func_name].add(now) + except KeyError: + results_map[func_name] = {now} + + # check results + for func_name, min_len in EXPECTED_MAP: + print("Testing %s" % func_name) + results = results_map.get(func_name) + if results is None: + pass + elif func_name == "ticks_cpu" and results == {0}: + # ticks_cpu() returns 0 on some ports (e.g. unix) + pass + elif len(results) < min_len: + print( + "%s() returns %s result%s in %s ms, expecting >= %s" + % (func_name, len(results), "s"[: len(results) != 1], TEST_TIME, min_len) + ) + + +test() diff --git a/tests/extmod/utime_res.py.exp b/tests/extmod/utime_res.py.exp new file mode 100644 index 0000000000..08c2d82950 --- /dev/null +++ b/tests/extmod/utime_res.py.exp @@ -0,0 +1,9 @@ +Testing time +Testing gmtime +Testing localtime +Testing gmtime_time +Testing localtime_time +Testing ticks_ms +Testing ticks_us +Testing ticks_ns +Testing ticks_cpu diff --git a/tests/extmod/utime_time_ns.py b/tests/extmod/utime_time_ns.py new file mode 100644 index 0000000000..0d13f839d4 --- /dev/null +++ b/tests/extmod/utime_time_ns.py @@ -0,0 +1,24 @@ +# test utime.time_ns() + +try: + import utime + + utime.sleep_us + utime.time_ns +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +t0 = utime.time_ns() +utime.sleep_us(5000) +t1 = utime.time_ns() + +# Check that time_ns increases. +print(t0 < t1) + +# Check that time_ns counts correctly, but be very lenient with the bounds (2ms to 50ms). +if 2000000 < t1 - t0 < 50000000: + print(True) +else: + print(t0, t1, t1 - t0) diff --git a/tests/extmod/utime_time_ns.py.exp b/tests/extmod/utime_time_ns.py.exp new file mode 100644 index 0000000000..dbde422651 --- /dev/null +++ b/tests/extmod/utime_time_ns.py.exp @@ -0,0 +1,2 @@ +True +True diff --git a/tests/extmod/vfs_basic.py b/tests/extmod/vfs_basic.py index 62b2a27738..9a9ef2ca61 100644 --- a/tests/extmod/vfs_basic.py +++ b/tests/extmod/vfs_basic.py @@ -74,6 +74,14 @@ print(uos.statvfs("/")[9] >= 32) # getcwd when in root dir print(uos.getcwd()) +# test operations on the root directory with nothing mounted, they should all fail +for func in ("chdir", "listdir", "mkdir", "remove", "rmdir", "stat"): + for arg in ("x", "/x"): + try: + getattr(uos, func)(arg) + except OSError: + print(func, arg, "OSError") + # basic mounting and listdir uos.mount(Filesystem(1), "/test_mnt") print(uos.listdir()) diff --git a/tests/extmod/vfs_basic.py.exp b/tests/extmod/vfs_basic.py.exp index ebca310304..536bb4c805 100644 --- a/tests/extmod/vfs_basic.py.exp +++ b/tests/extmod/vfs_basic.py.exp @@ -1,6 +1,18 @@ (16384, 0, 0, 0, 0, 0, 0, 0, 0, 0) True / +chdir x OSError +chdir /x OSError +listdir x OSError +listdir /x OSError +mkdir x OSError +mkdir /x OSError +remove x OSError +remove /x OSError +rmdir x OSError +rmdir /x OSError +stat x OSError +stat /x OSError 1 mount False False ['test_mnt'] ('test_mnt', 16384, 0) diff --git a/tests/extmod/vfs_lfs_mount.py b/tests/extmod/vfs_lfs_mount.py index 9207f4a8c6..c9926708c6 100644 --- a/tests/extmod/vfs_lfs_mount.py +++ b/tests/extmod/vfs_lfs_mount.py @@ -16,12 +16,12 @@ class RAMBlockDevice: def __init__(self, blocks): self.data = bytearray(blocks * self.ERASE_BLOCK_SIZE) - def readblocks(self, block, buf, off): + def readblocks(self, block, buf, off=0): addr = block * self.ERASE_BLOCK_SIZE + off for i in range(len(buf)): buf[i] = self.data[addr + i] - def writeblocks(self, block, buf, off): + def writeblocks(self, block, buf, off=0): addr = block * self.ERASE_BLOCK_SIZE + off for i in range(len(buf)): self.data[addr + i] = buf[i] @@ -35,9 +35,17 @@ class RAMBlockDevice: return 0 -def test(bdev, vfs_class): +def test(vfs_class): print("test", vfs_class) + bdev = RAMBlockDevice(30) + + # mount bdev unformatted + try: + uos.mount(bdev, "/lfs") + except Exception as er: + print(repr(er)) + # mkfs vfs_class.mkfs(bdev) @@ -67,12 +75,33 @@ def test(bdev, vfs_class): # umount uos.umount("/lfs") + # mount read-only + vfs = vfs_class(bdev) + uos.mount(vfs, "/lfs", readonly=True) + + # test reading works + with open("/lfs/subdir/lfsmod2.py") as f: + print("lfsmod2.py:", f.read()) + + # test writing fails + try: + open("/lfs/test_write", "w") + except OSError as er: + print(repr(er)) + + # umount + uos.umount("/lfs") + + # mount bdev again + uos.mount(bdev, "/lfs") + + # umount + uos.umount("/lfs") + # clear imported modules sys.modules.clear() -bdev = RAMBlockDevice(30) - # initialise path import sys @@ -81,5 +110,5 @@ sys.path.append("/lfs") sys.path.append("") # run tests -test(bdev, uos.VfsLfs1) -test(bdev, uos.VfsLfs2) +test(uos.VfsLfs1) +test(uos.VfsLfs2) diff --git a/tests/extmod/vfs_lfs_mount.py.exp b/tests/extmod/vfs_lfs_mount.py.exp index b5c5215314..68561b4807 100644 --- a/tests/extmod/vfs_lfs_mount.py.exp +++ b/tests/extmod/vfs_lfs_mount.py.exp @@ -1,8 +1,16 @@ test +OSError(19,) hello from lfs package hello from lfs +lfsmod2.py: print("hello from lfs") + +OSError(30,) test +OSError(19,) hello from lfs package hello from lfs +lfsmod2.py: print("hello from lfs") + +OSError(36,) diff --git a/tests/extmod/vfs_lfs_superblock.py b/tests/extmod/vfs_lfs_superblock.py new file mode 100644 index 0000000000..1ac5675554 --- /dev/null +++ b/tests/extmod/vfs_lfs_superblock.py @@ -0,0 +1,47 @@ +# Test for VfsLfs using a RAM device, when the first superblock does not exist + +try: + import uos + + uos.VfsLfs2 +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +class RAMBlockDevice: + def __init__(self, block_size, data): + self.block_size = block_size + self.data = data + + def readblocks(self, block, buf, off): + addr = block * self.block_size + off + for i in range(len(buf)): + buf[i] = self.data[addr + i] + + def ioctl(self, op, arg): + if op == 4: # block count + return len(self.data) // self.block_size + if op == 5: # block size + return self.block_size + if op == 6: # erase block + return 0 + + +# This is a valid littlefs2 filesystem with a block size of 64 bytes. +# The first block (where the first superblock is stored) is fully erased. +lfs2_data = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x02\x00\x00\x00\xf0\x0f\xff\xf7littlefs/\xe0\x00\x10\x00\x00\x02\x00@\x00\x00\x00\x04\x00\x00\x00\xff\x00\x00\x00\xff\xff\xff\x7f\xfe\x03\x00\x00p\x1f\xfc\x08\x1b\xb4\x14\xa7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x00\x00\xff\xef\xff\xf7test.txt \x00\x00\x08p\x1f\xfc\x08\x83\xf1u\xba\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + +# Create the block device from the static data (it will be read-only). +bdev = RAMBlockDevice(64, lfs2_data) + +# Create the VFS explicitly, no auto-detection is needed for this. +vfs = uos.VfsLfs2(bdev) +print(list(vfs.ilistdir())) + +# Mount the block device directly; this relies on auto-detection. +uos.mount(bdev, "/userfs") +print(uos.listdir("/userfs")) + +# Clean up. +uos.umount("/userfs") diff --git a/tests/extmod/vfs_lfs_superblock.py.exp b/tests/extmod/vfs_lfs_superblock.py.exp new file mode 100644 index 0000000000..c71bf50e82 --- /dev/null +++ b/tests/extmod/vfs_lfs_superblock.py.exp @@ -0,0 +1,2 @@ +[] +[] diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py new file mode 100644 index 0000000000..3bea99365d --- /dev/null +++ b/tests/extmod/vfs_posix.py @@ -0,0 +1,23 @@ +# Test for VfsPosix + +try: + import uos + + uos.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + + +# getcwd and chdir +curdir = uos.getcwd() +uos.chdir("/") +print(uos.getcwd()) +uos.chdir(curdir) +print(uos.getcwd() == curdir) + +# stat +print(type(uos.stat("/"))) + +# listdir and ilistdir +print(type(uos.listdir("/"))) diff --git a/tests/extmod/vfs_posix.py.exp b/tests/extmod/vfs_posix.py.exp new file mode 100644 index 0000000000..1b1f59b43e --- /dev/null +++ b/tests/extmod/vfs_posix.py.exp @@ -0,0 +1,4 @@ +/ +True + + diff --git a/tests/float/inf_nan_arith.py b/tests/float/inf_nan_arith.py new file mode 100644 index 0000000000..c27e38bc52 --- /dev/null +++ b/tests/float/inf_nan_arith.py @@ -0,0 +1,20 @@ +# Test behaviour of inf and nan in basic float operations + +inf = float("inf") +nan = float("nan") + +values = (-2, -1, 0, 1, 2, inf, nan) + +for x in values: + for y in values: + print(x, y) + print(" + - *", x + y, x - y, x * y) + try: + print(" /", x / y) + except ZeroDivisionError: + print(" / ZeroDivisionError") + try: + print(" ** pow", x ** y, pow(x, y)) + except ZeroDivisionError: + print(" ** pow ZeroDivisionError") + print(" == != < <= > >=", x == y, x != y, x < y, x <= y, x > y, x >= y) diff --git a/tests/float/math_domain.py b/tests/float/math_domain.py index e63628cf50..0c25dc08b6 100644 --- a/tests/float/math_domain.py +++ b/tests/float/math_domain.py @@ -38,7 +38,7 @@ for name, f, args in ( # double argument functions for name, f, args in ( - ("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3))), + ("pow", math.pow, ((0, 2), (-1, 2), (0, -1), (-1, 2.3), (nan, 0), (1, nan))), ("fmod", math.fmod, ((1.2, inf), (1.2, -inf), (1.2, 0), (inf, 1.2))), ("atan2", math.atan2, ((0, 0), (-inf, inf), (-inf, -inf), (inf, -inf))), ("copysign", math.copysign, ()), diff --git a/tests/micropython/extreme_exc.py b/tests/micropython/extreme_exc.py index dae5b15186..9d2f24745f 100644 --- a/tests/micropython/extreme_exc.py +++ b/tests/micropython/extreme_exc.py @@ -126,7 +126,7 @@ def main(): ) except Exception as er: e = er - lst[0] = None + lst[0][0] = None lst = None print(repr(e)[:10]) diff --git a/tests/pybnative/for.py b/tests/micropython/native_for.py similarity index 85% rename from tests/pybnative/for.py rename to tests/micropython/native_for.py index 50177a9ba4..c640a8d08b 100644 --- a/tests/pybnative/for.py +++ b/tests/micropython/native_for.py @@ -1,4 +1,4 @@ -import pyb +# test for native for loops @micropython.native diff --git a/tests/pybnative/for.py.exp b/tests/micropython/native_for.py.exp similarity index 100% rename from tests/pybnative/for.py.exp rename to tests/micropython/native_for.py.exp diff --git a/tests/misc/sys_settrace_features.py b/tests/misc/sys_settrace_features.py index a123044892..8a38b7534f 100644 --- a/tests/misc/sys_settrace_features.py +++ b/tests/misc/sys_settrace_features.py @@ -20,8 +20,8 @@ def print_stacktrace(frame, level=0): " ", frame.f_globals["__name__"], frame.f_code.co_name, - # reduce full path to some pseudo-relative - "misc" + "".join(frame.f_code.co_filename.split("tests/misc")[-1:]), + # Keep just the filename. + "sys_settrace_" + frame.f_code.co_filename.split("sys_settrace_")[-1], frame.f_lineno, ) ) @@ -60,7 +60,9 @@ def trace_tick_handler_bob(frame, event, arg): def trace_tick_handler(frame, event, arg): # Ignore CPython specific helpers. - if frame.f_globals["__name__"].find("importlib") != -1: + to_ignore = ["importlib", "zipimport", "encodings"] + frame_name = frame.f_globals["__name__"] + if any(name in frame_name for name in to_ignore): return print("### trace_handler::main event:", event) @@ -94,9 +96,9 @@ def do_tests(): print("Who loves the sun?") print("Not every-", factorial(3)) - from sys_settrace_subdir import trace_generic + from sys_settrace_subdir import sys_settrace_generic - trace_generic.run_tests() + sys_settrace_generic.run_tests() return diff --git a/tests/misc/sys_settrace_generator.py b/tests/misc/sys_settrace_generator.py index 4ace0f50e8..43065df4ae 100644 --- a/tests/misc/sys_settrace_generator.py +++ b/tests/misc/sys_settrace_generator.py @@ -17,8 +17,8 @@ def print_stacktrace(frame, level=0): " ", frame.f_globals["__name__"], frame.f_code.co_name, - # reduce full path to some pseudo-relative - "misc" + "".join(frame.f_code.co_filename.split("tests/misc")[-1:]), + # Keep just the filename. + "sys_settrace_" + frame.f_code.co_filename.split("sys_settrace_")[-1], frame.f_lineno, ) ) diff --git a/tests/misc/sys_settrace_generator.py.exp b/tests/misc/sys_settrace_generator.py.exp index de9d0bf1c3..a83450afe3 100644 --- a/tests/misc/sys_settrace_generator.py.exp +++ b/tests/misc/sys_settrace_generator.py.exp @@ -1,195 +1,195 @@ ### trace_handler::main event: call - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:41 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:41 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:42 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:42 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:48 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:48 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:49 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:49 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:50 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:50 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:52 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:52 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: call - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:42 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:52 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:42 + 1: @__main__:test_generator => sys_settrace_generator.py:52 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:43 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:52 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:43 + 1: @__main__:test_generator => sys_settrace_generator.py:52 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: return - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:43 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:52 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:43 + 1: @__main__:test_generator => sys_settrace_generator.py:52 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:56 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: call - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:43 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:43 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:43 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:43 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:44 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:44 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: return - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:44 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:44 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: call - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:44 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:44 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:44 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:44 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:45 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:45 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: return - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:45 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:45 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: call - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:45 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:45 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:45 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:45 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:46 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:46 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: return - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:46 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:46 + 1: @__main__:test_generator => sys_settrace_generator.py:56 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: exception - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:56 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:56 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:58 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:58 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:59 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:59 + 1: @__main__: => sys_settrace_generator.py:69 test_generator 7 8 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:61 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:61 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:62 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:62 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: call - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:42 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:42 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:43 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:43 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: return - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:43 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:43 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:64 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:64 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: call - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:43 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:43 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:43 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:43 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:44 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:44 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: return - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:44 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:44 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:64 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:64 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: call - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:44 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:44 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:44 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:44 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:45 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:45 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: return - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:45 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:45 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:64 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:64 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:63 + 1: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: call - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:45 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:45 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:45 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:45 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:46 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:46 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: return - 0: @__main__:make_gen => miscmisc/sys_settrace_generator.py:46 - 1: @__main__:test_generator => miscmisc/sys_settrace_generator.py:63 - 2: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:make_gen => sys_settrace_generator.py:46 + 1: @__main__:test_generator => sys_settrace_generator.py:63 + 2: @__main__: => sys_settrace_generator.py:69 ### trace_handler::main event: line - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:65 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:65 + 1: @__main__: => sys_settrace_generator.py:69 7 ### trace_handler::main event: return - 0: @__main__:test_generator => miscmisc/sys_settrace_generator.py:65 - 1: @__main__: => miscmisc/sys_settrace_generator.py:69 + 0: @__main__:test_generator => sys_settrace_generator.py:65 + 1: @__main__: => sys_settrace_generator.py:69 Total traces executed: 54 diff --git a/tests/misc/sys_settrace_loop.py b/tests/misc/sys_settrace_loop.py index 06d0dc17bf..1186bd91a0 100644 --- a/tests/misc/sys_settrace_loop.py +++ b/tests/misc/sys_settrace_loop.py @@ -17,8 +17,8 @@ def print_stacktrace(frame, level=0): " ", frame.f_globals["__name__"], frame.f_code.co_name, - # reduce full path to some pseudo-relative - "misc" + "".join(frame.f_code.co_filename.split("tests/misc")[-1:]), + # Keep just the filename. + "sys_settrace_" + frame.f_code.co_filename.split("sys_settrace_")[-1], frame.f_lineno, ) ) diff --git a/tests/misc/sys_settrace_loop.py.exp b/tests/misc/sys_settrace_loop.py.exp index f56f98fae0..ff9ef577c5 100644 --- a/tests/misc/sys_settrace_loop.py.exp +++ b/tests/misc/sys_settrace_loop.py.exp @@ -1,72 +1,72 @@ ### trace_handler::main event: call - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:41 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:41 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:43 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:43 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:44 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:44 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:45 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:45 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:44 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:44 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:45 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:45 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:44 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:44 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:45 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:45 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:44 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:44 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:45 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:45 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:46 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:46 + 1: @__main__: => sys_settrace_loop.py:58 test_for_loop 3 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:49 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:49 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:50 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:50 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:51 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:51 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:53 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:53 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:52 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:52 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:53 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:53 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:52 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:52 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:53 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:53 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:52 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:52 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:53 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:53 + 1: @__main__: => sys_settrace_loop.py:58 ### trace_handler::main event: line - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:54 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:54 + 1: @__main__: => sys_settrace_loop.py:58 test_while_loop 3 ### trace_handler::main event: return - 0: @__main__:test_loop => miscmisc/sys_settrace_loop.py:54 - 1: @__main__: => miscmisc/sys_settrace_loop.py:58 + 0: @__main__:test_loop => sys_settrace_loop.py:54 + 1: @__main__: => sys_settrace_loop.py:58 Total traces executed: 23 diff --git a/tests/misc/sys_settrace_subdir/trace_generic.py b/tests/misc/sys_settrace_subdir/sys_settrace_generic.py similarity index 91% rename from tests/misc/sys_settrace_subdir/trace_generic.py rename to tests/misc/sys_settrace_subdir/sys_settrace_generic.py index 111a9d19ff..a60ca955d7 100644 --- a/tests/misc/sys_settrace_subdir/trace_generic.py +++ b/tests/misc/sys_settrace_subdir/sys_settrace_generic.py @@ -41,10 +41,10 @@ def test_lambda(): # import def test_import(): - from sys_settrace_subdir import trace_importme + from sys_settrace_subdir import sys_settrace_importme - trace_importme.dummy() - trace_importme.saysomething() + sys_settrace_importme.dummy() + sys_settrace_importme.saysomething() # class diff --git a/tests/misc/sys_settrace_subdir/trace_importme.py b/tests/misc/sys_settrace_subdir/sys_settrace_importme.py similarity index 100% rename from tests/misc/sys_settrace_subdir/trace_importme.py rename to tests/misc/sys_settrace_subdir/sys_settrace_importme.py diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 7ab4e85c57..cdb2730eda 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -6,7 +6,9 @@ import sys, os, time, re, select import argparse +import itertools import subprocess +import tempfile sys.path.append("../tools") import pyboard @@ -18,6 +20,9 @@ else: CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/micropython") +# For diff'ing test output +DIFF = os.getenv("MICROPY_DIFF", "diff -u") + PYTHON_TRUTH = CPYTHON3 INSTANCE_READ_TIMEOUT_S = 10 @@ -323,6 +328,18 @@ def run_test_on_instances(test_file, num_instances, instances): return error, skip, output_str +def print_diff(a, b): + a_fd, a_path = tempfile.mkstemp(text=True) + b_fd, b_path = tempfile.mkstemp(text=True) + os.write(a_fd, a.encode()) + os.write(b_fd, b.encode()) + os.close(a_fd) + os.close(b_fd) + subprocess.run(DIFF.split(" ") + [a_path, b_path]) + os.unlink(a_path) + os.unlink(b_path) + + def run_tests(test_files, instances_truth, instances_test): skipped_tests = [] passed_tests = [] @@ -372,6 +389,8 @@ def run_tests(test_files, instances_truth, instances_test): print(output_test, end="") print("### TRUTH ###") print(output_truth, end="") + print("### DIFF ###") + print_diff(output_truth, output_test) if cmd_args.show_output: print() @@ -400,6 +419,13 @@ def main(): cmd_parser.add_argument( "-i", "--instance", action="append", default=[], help="instance(s) to run the tests on" ) + cmd_parser.add_argument( + "-p", + "--permutations", + type=int, + default=1, + help="repeat the test with this many permutations of the instance order", + ) cmd_parser.add_argument("files", nargs="+", help="input test files") cmd_args = cmd_parser.parse_args() @@ -432,8 +458,14 @@ def main(): for _ in range(max_instances - len(instances_test)): instances_test.append(PyInstanceSubProcess([MICROPYTHON])) + all_pass = True try: - all_pass = run_tests(test_files, instances_truth, instances_test) + for i, instances_test_permutation in enumerate(itertools.permutations(instances_test)): + if i >= cmd_args.permutations: + break + + all_pass &= run_tests(test_files, instances_truth, instances_test_permutation) + finally: for i in instances_truth: i.close() diff --git a/tests/run-tests b/tests/run-tests index b05e8e5fda..9f35fb08b8 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -23,12 +23,16 @@ def base_path(*p): # is of lower version, you can point MICROPY_CPYTHON3 environment var # to the correct executable. if os.name == 'nt': - CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3.exe') + CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python') MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', base_path('../ports/windows/micropython.exe')) else: CPYTHON3 = os.getenv('MICROPY_CPYTHON3', 'python3') MICROPYTHON = os.getenv('MICROPY_MICROPYTHON', base_path('../ports/unix/micropython')) +# Use CPython options to not save .pyc files, to only access the core standard library +# (not site packages which may clash with u-module names), and improve start up time. +CPYTHON3_CMD = [CPYTHON3, "-Wignore", "-BS"] + # mpy-cross is only needed if --via-mpy command-line arg is passed MPYCROSS = os.getenv('MICROPY_MPYCROSS', base_path('../mpy-cross/mpy-cross')) @@ -135,7 +139,8 @@ def run_micropython(pyb, args, test_file, is_special=False): os.close(emulator) os.close(subterminal) else: - output_mupy = subprocess.check_output(args + [test_file], stderr=subprocess.STDOUT) + output_mupy = subprocess.check_output(args + [test_file], env={}, stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as error: return error.output + b'CRASH' @@ -218,7 +223,7 @@ def run_micropython(pyb, args, test_file, is_special=False): if lines_exp[i][1].match(lines_mupy[i_mupy]): lines_mupy[i_mupy] = lines_exp[i][0] else: - # print("don't match: %r %s" % (lines_exp[i][1], lines_mupy[i_mupy])) # DEBUG + # print("don't match: %r %s" % (lines_exp[i][0], lines_mupy[i_mupy])) # DEBUG pass i_mupy += 1 if i_mupy >= len(lines_mupy): @@ -342,7 +347,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): upy_float_precision = int(upy_float_precision) has_complex = run_feature_check(pyb, args, base_path, 'complex.py') == b'complex\n' has_coverage = run_feature_check(pyb, args, base_path, 'coverage.py') == b'coverage\n' - cpy_byteorder = subprocess.check_output([CPYTHON3, base_path('feature_check/byteorder.py')]) + cpy_byteorder = subprocess.check_output(CPYTHON3_CMD + [base_path('feature_check/byteorder.py')]) skip_endian = (upy_byteorder != cpy_byteorder) # These tests don't test slice explicitly but rather use it to perform the test @@ -361,13 +366,9 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): 'struct_endian', ) - # Some tests shouldn't be run under Travis CI - if os.getenv('TRAVIS') == 'true': - skip_tests.add('basics/memoryerror.py') - skip_tests.add('thread/thread_gc1.py') # has reliability issues - skip_tests.add('thread/thread_lock4.py') # has reliability issues - skip_tests.add('thread/stress_heap.py') # has reliability issues - skip_tests.add('thread/stress_recurse.py') # has reliability issues + # Some tests shouldn't be run on GitHub Actions + if os.getenv('GITHUB_ACTIONS') == 'true': + skip_tests.add('thread/stress_schedule.py') # has reliability issues if upy_float_precision == 0: skip_tests.add('extmod/uctypes_le_float.py') @@ -457,7 +458,10 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add('basics/scope_implicit.py') # requires checking for unbound local skip_tests.add('basics/try_finally_return2.py') # requires raise_varargs skip_tests.add('basics/unboundlocal.py') # requires checking for unbound local + skip_tests.add('extmod/uasyncio_event.py') # unknown issue skip_tests.add('extmod/uasyncio_lock.py') # requires async with + skip_tests.add('extmod/uasyncio_micropython.py') # unknown issue + skip_tests.add('extmod/uasyncio_wait_for.py') # unknown issue skip_tests.add('misc/features.py') # requires raise_varargs skip_tests.add('misc/print_exception.py') # because native doesn't have proper traceback info skip_tests.add('misc/sys_exc_info.py') # sys.exc_info() is not supported for native @@ -524,7 +528,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): "LANG": "en_US.UTF-8"} # run CPython to work out expected output try: - output_expected = subprocess.check_output([CPYTHON3, '-Wignore', '-B', test_file], env=e, stderr=subprocess.STDOUT) + output_expected = subprocess.check_output(CPYTHON3_CMD + [test_file], env=e, stderr=subprocess.STDOUT) if args.write_exp: with open(test_file_expected, 'wb') as f: f.write(output_expected) @@ -565,7 +569,7 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): print("### Actual") print(output_mupy) print("FAIL ", test_file) - failed_tests.append(test_name) + failed_tests.append(test_file) test_count.add(1) diff --git a/tests/thread/stress_schedule.py b/tests/thread/stress_schedule.py index c5a402b3a3..8be7f2d737 100644 --- a/tests/thread/stress_schedule.py +++ b/tests/thread/stress_schedule.py @@ -14,7 +14,11 @@ except AttributeError: gc.disable() +_NUM_TASKS = 10000 +_TIMEOUT_MS = 10000 + n = 0 # How many times the task successfully ran. +t = None # Start time of test, assigned here to preallocate entry in globals dict. def task(x): @@ -34,9 +38,6 @@ def thread(): for i in range(8): _thread.start_new_thread(thread, ()) -_NUM_TASKS = const(10000) -_TIMEOUT_MS = const(10000) - # Wait up to 10 seconds for 10000 tasks to be scheduled. t = utime.ticks_ms() while n < _NUM_TASKS and utime.ticks_diff(utime.ticks_ms(), t) < _TIMEOUT_MS: diff --git a/tests/unix/extra_coverage.py b/tests/unix/extra_coverage.py index 36105f6bad..b4808993a7 100644 --- a/tests/unix/extra_coverage.py +++ b/tests/unix/extra_coverage.py @@ -46,6 +46,19 @@ stream.set_error(uerrno.EAGAIN) buf = uio.BufferedWriter(stream, 8) print(buf.write(bytearray(16))) +# function defined in C++ code +print("cpp", extra_cpp_coverage()) + +# test user C module +import cexample + +print(cexample.add_ints(3, 2)) + +# test user C module mixed with C++ code +import cppexample + +print(cppexample.cppfunc(1, 2)) + # test basic import of frozen scripts import frzstr1 diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 39d7978fa5..f7dd6172ed 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -4,7 +4,7 @@ 123 123 1ABCDEF -ab abc +ab abc ' abc' ' True' 'Tru' false true (null) @@ -143,6 +143,9 @@ OSError 0 None None +cpp None +5 +(3, 'hellocpp') frzstr1 frzstr1.py frzmpy1 diff --git a/tools/bootstrap_upip.sh b/tools/bootstrap_upip.sh deleted file mode 100755 index 8b9d199fa9..0000000000 --- a/tools/bootstrap_upip.sh +++ /dev/null @@ -1,34 +0,0 @@ -# SPDX-FileCopyrightText: 2014 MicroPython & CircuitPython contributors (https://github.com/adafruit/circuitpython/graphs/contributors) -# -# SPDX-License-Identifier: MIT - -# This script performs bootstrap installation of upip package manager from PyPI -# All the other packages can be installed using it. - -saved="$PWD" - -if [ "$1" = "" ]; then - dest=~/.micropython/lib/ -else - dest="$1" -fi - -if [ -z "$TMPDIR" ]; then - cd /tmp -else - cd $TMPDIR -fi - -# Remove any stale old version -rm -rf micropython-upip-* -wget -nd -rH -l1 -D files.pythonhosted.org https://pypi.org/project/micropython-upip/ --reject=html - -tar xfz micropython-upip-*.tar.gz -tmpd="$PWD" - -cd "$saved" -mkdir -p "$dest" -cp "$tmpd"/micropython-upip-*/upip*.py "$dest" - -echo "upip is installed. To use:" -echo "micropython -m upip --help" diff --git a/tools/ci.sh b/tools/ci.sh new file mode 100755 index 0000000000..c6b641dae9 --- /dev/null +++ b/tools/ci.sh @@ -0,0 +1,498 @@ +#!/bin/bash + +if which nproc > /dev/null; then + MAKEOPTS="-j$(nproc)" +else + MAKEOPTS="-j$(sysctl -n hw.ncpu)" +fi + +######################################################################################## +# general helper functions + +function ci_gcc_arm_setup { + sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi + arm-none-eabi-gcc --version +} + +######################################################################################## +# code formatting + +function ci_code_formatting_setup { + sudo apt-add-repository --yes --update ppa:pybricks/ppa + sudo apt-get install uncrustify + pip3 install black + uncrustify --version + black --version +} + +function ci_code_formatting_run { + tools/codeformat.py -v +} + +######################################################################################## +# commit formatting + +function ci_commit_formatting_run { + git remote add upstream https://github.com/micropython/micropython.git + git fetch --depth=100 upstream master + # For a PR, upstream/master..HEAD ends with a merge commit into master, exlude that one. + tools/verifygitlog.py -v upstream/master..HEAD --no-merges +} + +######################################################################################## +# code size + +function ci_code_size_setup { + sudo apt-get update + sudo apt-get install gcc-multilib + gcc --version + ci_gcc_arm_setup +} + +function ci_code_size_build { + # starts off at either the ref/pull/N/merge FETCH_HEAD, or the current branch HEAD + git checkout -b pull_request # save the current location + git remote add upstream https://github.com/micropython/micropython.git + git fetch --depth=100 upstream master + # build reference, save to size0 + # ignore any errors with this build, in case master is failing + git checkout `git merge-base --fork-point upstream/master pull_request` + git show -s + tools/metrics.py clean bm + tools/metrics.py build bm | tee ~/size0 || true + # build PR/branch, save to size1 + git checkout pull_request + git log upstream/master..HEAD + tools/metrics.py clean bm + tools/metrics.py build bm | tee ~/size1 +} + +######################################################################################## +# ports/cc3200 + +function ci_cc3200_setup { + ci_gcc_arm_setup +} + +function ci_cc3200_build { + make ${MAKEOPTS} -C ports/cc3200 BTARGET=application BTYPE=release + make ${MAKEOPTS} -C ports/cc3200 BTARGET=bootloader BTYPE=release +} + +######################################################################################## +# ports/esp32 + +function ci_esp32_idf3_setup { + sudo pip3 install pyserial 'pyparsing<2.4' + curl -L https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz | tar zxf - + git clone https://github.com/espressif/esp-idf.git +} + +function ci_esp32_idf3_path { + echo $(pwd)/xtensa-esp32-elf/bin +} + +function ci_esp32_idf3_build { + make ${MAKEOPTS} -C mpy-cross + git -C esp-idf checkout $(grep "ESPIDF_SUPHASH_V3 :=" ports/esp32/Makefile | cut -d " " -f 3) + git -C esp-idf submodule update --init components/json/cJSON components/esp32/lib components/esptool_py/esptool components/expat/expat components/lwip/lwip components/mbedtls/mbedtls components/micro-ecc/micro-ecc components/nghttp/nghttp2 components/nimble components/bt + make ${MAKEOPTS} -C ports/esp32 submodules + make ${MAKEOPTS} -C ports/esp32 +} + +function ci_esp32_idf4_setup { + sudo pip3 install pyserial 'pyparsing<2.4' + curl -L https://dl.espressif.com/dl/xtensa-esp32-elf-gcc8_2_0-esp-2019r2-linux-amd64.tar.gz | tar zxf - + git clone https://github.com/espressif/esp-idf.git +} + +function ci_esp32_idf4_path { + echo $(pwd)/xtensa-esp32-elf/bin +} + +function ci_esp32_idf4_build { + make ${MAKEOPTS} -C mpy-cross + git -C esp-idf checkout $(grep "ESPIDF_SUPHASH_V4 :=" ports/esp32/Makefile | cut -d " " -f 3) + git -C esp-idf submodule update --init components/bt/controller/lib components/bt/host/nimble/nimble components/esp_wifi/lib_esp32 components/esptool_py/esptool components/lwip/lwip components/mbedtls/mbedtls + make ${MAKEOPTS} -C ports/esp32 submodules + make ${MAKEOPTS} -C ports/esp32 +} + +######################################################################################## +# ports/esp8266 + +function ci_esp8266_setup { + sudo pip install pyserial esptool + wget https://github.com/jepler/esp-open-sdk/releases/download/2018-06-10/xtensa-lx106-elf-standalone.tar.gz + zcat xtensa-lx106-elf-standalone.tar.gz | tar x + # Remove this esptool.py so pip version is used instead + rm xtensa-lx106-elf/bin/esptool.py +} + +function ci_esp8266_path { + echo $(pwd)/xtensa-lx106-elf/bin +} + +function ci_esp8266_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/esp8266 submodules + make ${MAKEOPTS} -C ports/esp8266 + make ${MAKEOPTS} -C ports/esp8266 BOARD=GENERIC_512K + make ${MAKEOPTS} -C ports/esp8266 BOARD=GENERIC_1M +} + +######################################################################################## +# ports/nrf + +function ci_nrf_setup { + ci_gcc_arm_setup +} + +function ci_nrf_build { + ports/nrf/drivers/bluetooth/download_ble_stack.sh s140_nrf52_6_1_1 + make ${MAKEOPTS} -C ports/nrf submodules + make ${MAKEOPTS} -C ports/nrf BOARD=pca10040 + make ${MAKEOPTS} -C ports/nrf BOARD=microbit + make ${MAKEOPTS} -C ports/nrf BOARD=pca10056 SD=s140 + make ${MAKEOPTS} -C ports/nrf BOARD=pca10090 +} + +######################################################################################## +# ports/powerpc + +function ci_powerpc_setup { + sudo apt-get install gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross +} + +function ci_powerpc_build { + make ${MAKEOPTS} -C ports/powerpc UART=potato + make ${MAKEOPTS} -C ports/powerpc UART=lpc_serial +} + +######################################################################################## +# ports/qemu-arm + +function ci_qemu_arm_setup { + ci_gcc_arm_setup + sudo apt-get update + sudo apt-get install qemu-system + qemu-system-arm --version +} + +function ci_qemu_arm_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 + make ${MAKEOPTS} -C ports/qemu-arm clean + make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test test +} + +######################################################################################## +# ports/rp2 + +function ci_rp2_setup { + ci_gcc_arm_setup +} + +function ci_rp2_build { + make ${MAKEOPTS} -C mpy-cross + git submodule update --init lib/pico-sdk + git -C lib/pico-sdk submodule update --init lib/tinyusb + make ${MAKEOPTS} -C ports/rp2 +} + +######################################################################################## +# ports/samd + +function ci_samd_setup { + ci_gcc_arm_setup +} + +function ci_samd_build { + make ${MAKEOPTS} -C ports/samd submodules + make ${MAKEOPTS} -C ports/samd +} + +######################################################################################## +# ports/stm32 + +function ci_stm32_setup { + ci_gcc_arm_setup + pip3 install pyhy +} + +function ci_stm32_pyb_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/stm32 submodules + git submodule update --init lib/btstack + make ${MAKEOPTS} -C ports/stm32 BOARD=PYBV11 MICROPY_PY_WIZNET5K=5200 MICROPY_PY_CC3K=1 USER_C_MODULES=../../examples/usercmodule CFLAGS_EXTRA="-DMODULE_CEXAMPLE_ENABLED=1 -DMODULE_CPPEXAMPLE_ENABLED=1" + make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF2 + make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF6 NANBOX=1 MICROPY_BLUETOOTH_NIMBLE=0 MICROPY_BLUETOOTH_BTSTACK=1 + make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBV10 CFLAGS_EXTRA='-DMBOOT_FSLOAD=1 -DMBOOT_VFS_LFS2=1' + make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBD_SF6 +} + +function ci_stm32_nucleo_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/stm32 submodules + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_F091RC + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_H743ZI CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L073RZ + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L476RG DEBUG=1 + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_WB55 + make ${MAKEOPTS} -C ports/stm32/mboot BOARD=NUCLEO_WB55 +} + +######################################################################################## +# ports/teensy + +function ci_teensy_setup { + ci_gcc_arm_setup +} + +function ci_teensy_build { + make ${MAKEOPTS} -C ports/teensy +} + +######################################################################################## +# ports/unix + +CI_UNIX_OPTS_SYS_SETTRACE=( + MICROPY_PY_BTREE=0 + MICROPY_PY_FFI=0 + MICROPY_PY_USSL=0 + CFLAGS_EXTRA="-DMICROPY_PY_SYS_SETTRACE=1" +) + +CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS=( + MICROPY_PY_BTREE=0 + MICROPY_PY_FFI=0 + MICROPY_PY_USSL=0 + CFLAGS_EXTRA="-DMICROPY_STACKLESS=1 -DMICROPY_STACKLESS_STRICT=1 -DMICROPY_PY_SYS_SETTRACE=1" +) + +function ci_unix_build_helper { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/unix "$@" submodules + make ${MAKEOPTS} -C ports/unix "$@" deplibs + make ${MAKEOPTS} -C ports/unix "$@" +} + +function ci_unix_run_tests_helper { + make -C ports/unix "$@" test +} + +function ci_unix_run_tests_full_helper { + variant=$1 + shift + if [ $variant = standard ]; then + micropython=micropython + else + micropython=micropython-$variant + fi + make -C ports/unix VARIANT=$variant "$@" test_full + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/$micropython ./run-multitests.py multi_net/*.py) +} + +function ci_native_mpy_modules_build { + if [ "$1" = "" ]; then + arch=x64 + else + arch=$1 + fi + make -C examples/natmod/features1 ARCH=$arch + make -C examples/natmod/features2 ARCH=$arch + make -C examples/natmod/btree ARCH=$arch + make -C examples/natmod/framebuf ARCH=$arch + make -C examples/natmod/uheapq ARCH=$arch + make -C examples/natmod/urandom ARCH=$arch + make -C examples/natmod/ure ARCH=$arch + make -C examples/natmod/uzlib ARCH=$arch +} + +function ci_native_mpy_modules_32bit_build { + ci_native_mpy_modules_build x86 +} + +function ci_unix_minimal_build { + make ${MAKEOPTS} -C ports/unix VARIANT=minimal +} + +function ci_unix_minimal_run_tests { + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/micropython-minimal ./run-tests -e exception_chain -e self_type_check -e subclass_native_init -d basics) +} + +function ci_unix_standard_build { + ci_unix_build_helper VARIANT=standard +} + +function ci_unix_standard_run_tests { + ci_unix_run_tests_full_helper standard +} + +function ci_unix_standard_run_perfbench { + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/micropython ./run-perfbench.py 1000 1000) +} + +function ci_unix_coverage_setup { + sudo apt-get install lcov + sudo pip3 install setuptools + sudo pip3 install pyelftools + gcc --version + python3 --version +} + +function ci_unix_coverage_build { + ci_unix_build_helper VARIANT=coverage +} + +function ci_unix_coverage_run_tests { + ci_unix_run_tests_full_helper coverage +} + +function ci_unix_coverage_run_native_mpy_tests { + MICROPYPATH=examples/natmod/features2 ./ports/unix/micropython-coverage -m features2 + (cd tests && ./run-natmodtests.py "$@" extmod/{btree*,framebuf*,uheapq*,ure*,uzlib*}.py) +} + +function ci_unix_32bit_setup { + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 + sudo pip3 install setuptools + sudo pip3 install pyelftools + gcc --version + python2 --version + python3 --version +} + +function ci_unix_coverage_32bit_build { + ci_unix_build_helper VARIANT=coverage MICROPY_FORCE_32BIT=1 +} + +function ci_unix_coverage_32bit_run_tests { + ci_unix_run_tests_full_helper coverage MICROPY_FORCE_32BIT=1 +} + +function ci_unix_coverage_32bit_run_native_mpy_tests { + ci_unix_coverage_run_native_mpy_tests --arch x86 +} + +function ci_unix_nanbox_build { + # Use Python 2 to check that it can run the build scripts + ci_unix_build_helper PYTHON=python2 VARIANT=nanbox +} + +function ci_unix_nanbox_run_tests { + ci_unix_run_tests_full_helper nanbox PYTHON=python2 +} + +function ci_unix_float_build { + ci_unix_build_helper VARIANT=standard CFLAGS_EXTRA="-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT" +} + +function ci_unix_float_run_tests { + # TODO get this working: ci_unix_run_tests_full_helper standard CFLAGS_EXTRA="-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT" + ci_unix_run_tests_helper CFLAGS_EXTRA="-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT" +} + +function ci_unix_clang_setup { + sudo apt-get install clang + clang --version +} + +function ci_unix_stackless_clang_build { + make ${MAKEOPTS} -C mpy-cross CC=clang + make ${MAKEOPTS} -C ports/unix submodules + make ${MAKEOPTS} -C ports/unix CC=clang CFLAGS_EXTRA="-DMICROPY_STACKLESS=1 -DMICROPY_STACKLESS_STRICT=1" +} + +function ci_unix_stackless_clang_run_tests { + ci_unix_run_tests_helper CC=clang +} + +function ci_unix_float_clang_build { + make ${MAKEOPTS} -C mpy-cross CC=clang + make ${MAKEOPTS} -C ports/unix submodules + make ${MAKEOPTS} -C ports/unix CC=clang CFLAGS_EXTRA="-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT" +} + +function ci_unix_float_clang_run_tests { + ci_unix_run_tests_helper CC=clang +} + +function ci_unix_settrace_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE[@]}" +} + +function ci_unix_settrace_run_tests { + ci_unix_run_tests_helper "${CI_UNIX_OPTS_SYS_SETTRACE[@]}" +} + +function ci_unix_settrace_stackless_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" +} + +function ci_unix_settrace_stackless_run_tests { + ci_unix_run_tests_helper "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" +} + +function ci_unix_macos_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/unix submodules + #make ${MAKEOPTS} -C ports/unix deplibs + make ${MAKEOPTS} -C ports/unix + # check for additional compiler errors/warnings + make ${MAKEOPTS} -C ports/unix VARIANT=coverage submodules + make ${MAKEOPTS} -C ports/unix VARIANT=coverage +} + +function ci_unix_macos_run_tests { + # Issues with macOS tests: + # - OSX has poor time resolution and these uasyncio tests do not have correct output + # - import_pkg7 has a problem with relative imports + # - urandom_basic has a problem with getrandbits(0) + (cd tests && ./run-tests --exclude 'uasyncio_(basic|heaplock|lock|wait_task)' --exclude 'import_pkg7.py' --exclude 'urandom_basic.py') +} + +######################################################################################## +# ports/windows + +function ci_windows_setup { + sudo apt-get install gcc-mingw-w64 +} + +function ci_windows_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/windows CROSS_COMPILE=i686-w64-mingw32- +} + +######################################################################################## +# ports/zephyr + +function ci_zephyr_setup { + docker pull zephyrprojectrtos/ci:v0.11.8 + docker run --name zephyr-ci -d -it \ + -v "$(pwd)":/micropython \ + -e ZEPHYR_SDK_INSTALL_DIR=/opt/sdk/zephyr-sdk-0.11.3 \ + -e ZEPHYR_TOOLCHAIN_VARIANT=zephyr \ + -w /micropython/ports/zephyr \ + zephyrprojectrtos/ci:v0.11.8 + docker ps -a +} + +function ci_zephyr_install { + docker exec zephyr-ci west init --mr v2.4.0 /zephyrproject + docker exec -w /zephyrproject zephyr-ci west update + docker exec -w /zephyrproject zephyr-ci west zephyr-export +} + +function ci_zephyr_build { + docker exec zephyr-ci bash -c "make clean; ./make-minimal ${MAKEOPTS}" + docker exec zephyr-ci bash -c "make clean; ./make-minimal ${MAKEOPTS} BOARD=frdm_k64f" + docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS}" + docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=frdm_k64f" + docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=mimxrt1050_evk" + docker exec zephyr-ci bash -c "make clean; make ${MAKEOPTS} BOARD=reel_board" +} diff --git a/tools/make-frozen.py b/tools/make-frozen.py index 2d0dc3c777..74dd4a9026 100755 --- a/tools/make-frozen.py +++ b/tools/make-frozen.py @@ -55,7 +55,7 @@ print("const uint32_t mp_frozen_str_sizes[] = {") for f, st in modules: print("%d," % st.st_size) -print("};") +print("0};") print("const char mp_frozen_str_content[] = {") for f, st in modules: @@ -87,4 +87,4 @@ for f, st in modules: chrs.append('\\0"') print("".join(chrs)) -print("};") +print('"\\0"};') diff --git a/tools/makemanifest.py b/tools/makemanifest.py index 377f245596..c07a3a6c77 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -172,6 +172,8 @@ def mkdir(filename): def freeze_internal(kind, path, script, opt): path = convert_path(path) + if not os.path.isdir(path): + raise FreezeError("freeze path must be a directory") if script is None and kind == KIND_AS_STR: if any(f[0] == KIND_AS_STR for f in manifest_list): raise FreezeError("can only freeze one str directory") @@ -327,7 +329,7 @@ def main(): b" (qstr_pool_t*)&mp_qstr_const_pool, MP_QSTRnumber_of, 0, 0\n" b"};\n" b'const char mp_frozen_mpy_names[1] = {"\\0"};\n' - b"const mp_raw_code_t *const mp_frozen_mpy_content[0] = {};\n" + b"const mp_raw_code_t *const mp_frozen_mpy_content[1] = {NULL};\n" ) # Generate output diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 88b3df5734..28dad27ddc 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -909,6 +909,17 @@ def freeze_mpy(base_qstrs, raw_codes): print(" &raw_code_%s," % rc.escaped_name) print("};") + # If a port defines MICROPY_FROZEN_LIST_ITEM then list all modules wrapped in that macro. + print("#ifdef MICROPY_FROZEN_LIST_ITEM") + for rc in raw_codes: + module_name = rc.source_file.str + if module_name.endswith("/__init__.py"): + short_name = module_name[: -len("/__init__.py")] + else: + short_name = module_name[: -len(".py")] + print('MICROPY_FROZEN_LIST_ITEM("%s", "%s")' % (short_name, module_name)) + print("#endif") + def merge_mpy(raw_codes, output_file): assert len(raw_codes) <= 31 # so var-uints all fit in 1 byte @@ -931,7 +942,7 @@ def merge_mpy(raw_codes, output_file): merged_mpy.extend(header) bytecode = bytearray() - bytecode_len = 6 + len(raw_codes) * 4 + 2 + bytecode_len = 6 + len(raw_codes) * 5 + 2 bytecode.append(bytecode_len << 2) # kind and length bytecode.append(0b00000000) # signature prelude bytecode.append(0b00001000) # size prelude @@ -940,7 +951,7 @@ def merge_mpy(raw_codes, output_file): for idx in range(len(raw_codes)): bytecode.append(0x32) # MP_BC_MAKE_FUNCTION bytecode.append(idx) # index raw code - bytecode.extend(b"\x34\x00") # MP_BC_CALL_FUNCTION, 0 args + bytecode.extend(b"\x34\x00\x59") # MP_BC_CALL_FUNCTION, 0 args, MP_BC_POP_TOP bytecode.extend(b"\x51\x63") # MP_BC_LOAD_NONE, MP_BC_RETURN_VALUE bytecode.append(0) # n_obj diff --git a/tools/pyboard.py b/tools/pyboard.py index 17d5cd8092..1dd1c981c6 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -234,6 +234,7 @@ class ProcessPtyToTerminal: class Pyboard: def __init__(self, device, baudrate=115200, user="micro", password="python", wait=0): + self.use_raw_paste = True if device.startswith("exec:"): self.serial = ProcessToSerial(device[len("exec:") :]) elif device.startswith("execpty:"): @@ -340,6 +341,41 @@ class Pyboard: # return normal and error output return data, data_err + def raw_paste_write(self, command_bytes): + # Read initial header, with window size. + data = self.serial.read(2) + window_size = data[0] | data[1] << 8 + window_remain = window_size + + # Write out the command_bytes data. + i = 0 + while i < len(command_bytes): + while window_remain == 0 or self.serial.inWaiting(): + data = self.serial.read(1) + if data == b"\x01": + # Device indicated that a new window of data can be sent. + window_remain += window_size + elif data == b"\x04": + # Device indicated abrupt end. Acknowledge it and finish. + self.serial.write(b"\x04") + return + else: + # Unexpected data from device. + raise PyboardError("unexpected read during raw paste: {}".format(data)) + # Send out as much data as possible that fits within the allowed window. + b = command_bytes[i : min(i + window_remain, len(command_bytes))] + self.serial.write(b) + window_remain -= len(b) + i += len(b) + + # Indicate end of data. + self.serial.write(b"\x04") + + # Wait for device to acknowledge end of data. + data = self.read_until(1, b"\x04") + if not data.endswith(b"\x04"): + raise PyboardError("could not complete raw paste: {}".format(data)) + def exec_raw_no_follow(self, command): if isinstance(command, bytes): command_bytes = command @@ -351,7 +387,26 @@ class Pyboard: if not data.endswith(b">"): raise PyboardError("could not enter raw repl") - # write command + if self.use_raw_paste: + # Try to enter raw-paste mode. + self.serial.write(b"\x05A\x01") + data = self.serial.read(2) + if data == b"R\x00": + # Device understood raw-paste command but doesn't support it. + pass + elif data == b"R\x01": + # Device supports raw-paste mode, write out the command using this mode. + return self.raw_paste_write(command_bytes) + else: + # Device doesn't support raw-paste, fall back to normal raw REPL. + data = self.read_until(1, b"w REPL; CTRL-B to exit\r\n>") + if not data.endswith(b"w REPL; CTRL-B to exit\r\n>"): + print(data) + raise PyboardError("could not enter raw repl") + # Don't try to use raw-paste mode again for this connection. + self.use_raw_paste = False + + # Write command using standard raw REPL, 256 bytes every 10ms. for i in range(0, len(command_bytes), 256): self.serial.write(command_bytes[i : min(i + 256, len(command_bytes))]) time.sleep(0.01) diff --git a/tools/upip.py b/tools/upip.py index 212cd26d0b..e3bd842333 100644 --- a/tools/upip.py +++ b/tools/upip.py @@ -129,7 +129,11 @@ def url_open(url): proto, _, host, urlpath = url.split("/", 3) try: - ai = usocket.getaddrinfo(host, 443, 0, usocket.SOCK_STREAM) + port = 443 + if ":" in host: + host, port = host.split(":") + port = int(port) + ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) except OSError as e: fatal("Unable to resolve %s (no Internet?)" % host, e) # print("Address infos:", ai) @@ -147,7 +151,7 @@ def url_open(url): warn_ussl = False # MicroPython rawsocket module supports file interface directly - s.write("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n" % (urlpath, host)) + s.write("GET /%s HTTP/1.0\r\nHost: %s:%s\r\n\r\n" % (urlpath, host, port)) l = s.readline() protover, status, msg = l.split(None, 2) if status != b"200": diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py new file mode 100755 index 0000000000..0080b96bfa --- /dev/null +++ b/tools/verifygitlog.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 + +import re +import subprocess +import sys + +verbosity = 0 # Show what's going on, 0 1 or 2. +suggestions = 1 # Set to 0 to not include lengthy suggestions in error messages. + + +def verbose(*args): + if verbosity: + print(*args) + + +def very_verbose(*args): + if verbosity > 1: + print(*args) + + +def git_log(pretty_format, *args): + # Delete pretty argument from user args so it doesn't interfere with what we do. + args = ["git", "log"] + [arg for arg in args if "--pretty" not in args] + args.append("--pretty=format:" + pretty_format) + very_verbose("git_log", *args) + # Generator yielding each output line. + for line in subprocess.Popen(args, stdout=subprocess.PIPE).stdout: + yield line.decode().rstrip("\r\n") + + +def verify(sha): + verbose("verify", sha) + errors = [] + warnings = [] + + def error_text(err): + return "commit " + sha + ": " + err + + def error(err): + errors.append(error_text(err)) + + def warning(err): + warnings.append(error_text(err)) + + # Author and committer email. + for line in git_log("%ae%n%ce", sha, "-n1"): + very_verbose("email", line) + if "noreply" in line: + error("Unwanted email address: " + line) + + # Message body. + raw_body = list(git_log("%B", sha, "-n1")) + if not raw_body: + error("Message is empty") + return errors, warnings + + # Subject line. + subject_line = raw_body[0] + very_verbose("subject_line", subject_line) + if not re.match(r"^[^!]+: [A-Z]+.+ .+\.$", subject_line): + error("Subject line should contain ': ' and end in '.': " + subject_line) + if len(subject_line) >= 73: + error("Subject line should be 72 or less characters: " + subject_line) + + # Second one divides subject and body. + if len(raw_body) > 1 and raw_body[1]: + error("Second message line should be empty: " + raw_body[1]) + + # Message body lines. + for line in raw_body[2:]: + if len(line) >= 76: + error("Message lines should be 75 or less characters: " + line) + + if not raw_body[-1].startswith("Signed-off-by: ") or "@" not in raw_body[-1]: + warning("Message should be signed-off") + + return errors, warnings + + +def run(args): + verbose("run", *args) + has_errors = False + has_warnings = False + for sha in git_log("%h", *args): + errors, warnings = verify(sha) + has_errors |= any(errors) + has_warnings |= any(warnings) + for err in errors: + print("error:", err) + for err in warnings: + print("warning:", err) + if has_errors or has_warnings: + if suggestions: + print("See https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md") + else: + print("ok") + if has_errors: + sys.exit(1) + + +def show_help(): + print("usage: verifygitlog.py [-v -n -h] ...") + print("-v : increase verbosity, can be speficied multiple times") + print("-n : do not print multi-line suggestions") + print("-h : print this help message and exit") + print("... : arguments passed to git log to retrieve commits to verify") + print(" see https://www.git-scm.com/docs/git-log") + print(" passing no arguments at all will verify all commits") + print("examples:") + print("verifygitlog.py -n10 # Check last 10 commits") + print("verifygitlog.py -v master..HEAD # Check commits since master") + + +if __name__ == "__main__": + args = sys.argv[1:] + verbosity = args.count("-v") + suggestions = args.count("-n") == 0 + if "-h" in args: + show_help() + else: + args = [arg for arg in args if arg not in ["-v", "-n", "-h"]] + run(args)