diff --git a/core/Kconfig b/core/Kconfig index 4f5ec6f4b7..f8682c2daa 100644 --- a/core/Kconfig +++ b/core/Kconfig @@ -39,6 +39,9 @@ config MODULE_CORE_MSG_BUS help Messaging Bus API for inter process message broadcast. +config MODULE_CORE_MUTEX_DEBUG + bool "Aid debugging deadlocks by printing on whom mutex_lock() is waiting" + config MODULE_CORE_MUTEX_PRIORITY_INHERITANCE bool "Use priority inheritance to mitigate priority inversion for mutexes" diff --git a/core/include/mutex.h b/core/include/mutex.h index ac05572c55..1055bae145 100644 --- a/core/include/mutex.h +++ b/core/include/mutex.h @@ -97,6 +97,25 @@ * `MUTEX_LOCK`. * - The scheduler is run, so that if the unblocked waiting thread can * run now, in case it has a higher priority than the running thread. + * + * Debugging deadlocks + * ------------------- + * + * The module `core_mutex_debug` can be used to print on whom `mutex_lock()` + * is waiting. This information includes the thread ID of the owner and the + * program counter (PC) from where `mutex_lock()` was called. Note that the + * information is only valid if: + * + * - The mutex was locked by a thread, and not e.g. by `MUTEX_INIT_LOCKED` + * - The function `cpu_get_caller_pc()` is implemented for the target + * architecture. (The thread ID will remain valid, though.) + * - The caller PC is briefly 0 when the current owner passes over ownership + * to the next thread, but that thread didn't get CPU time yet to write its + * PC into the data structure. Even worse, on architectures where an aligned + * function-pointer-sized write is not atomic, the value may briefly be + * bogus. Chances are close to zero this ever hits and since this only + * effects debug output, the ostrich algorithm was chosen here. + * * @{ * * @file @@ -112,6 +131,7 @@ #include #include +#include "architecture.h" #include "kernel_defines.h" #include "list.h" #include "thread.h" @@ -130,7 +150,8 @@ typedef struct { * @internal */ list_node_t queue; -#if defined(DOXYGEN) || defined(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE) +#if defined(DOXYGEN) || defined(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE) \ + || defined(MODULE_CORE_MUTEX_DEBUG) /** * @brief The current owner of the mutex or `NULL` * @note Only available if module core_mutex_priority_inheritance @@ -141,6 +162,18 @@ typedef struct { * this will have the value of `NULL`. */ kernel_pid_t owner; +#endif +#if defined(DOXYGEN) || defined(MODULE_CORE_MUTEX_DEBUG) + /** + * @brief Program counter of the call to @ref mutex_lock that most + * recently acquired this mutex + * + * This is used when the module `core_mutex_debug` is used to debug + * deadlocks and is non-existing otherwise + */ + uinttxtptr_t owner_calling_pc; +#endif +#if defined(DOXYGEN) || defined(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE) /** * @brief Original priority of the owner * @note Only available if module core_mutex_priority_inheritance diff --git a/core/mutex.c b/core/mutex.c index a2529ac34e..53dd916a93 100644 --- a/core/mutex.c +++ b/core/mutex.c @@ -47,9 +47,18 @@ * function into both @ref mutex_lock and @ref mutex_lock_cancelable is, * therefore, beneficial for the majority of applications. */ -static inline __attribute__((always_inline)) void _block(mutex_t *mutex, - unsigned irq_state) +static inline __attribute__((always_inline)) +void _block(mutex_t *mutex, + unsigned irq_state, + uinttxtptr_t pc) { + /* pc is only used when MODULE_CORE_MUTEX_DEBUG */ + (void)pc; +#if IS_USED(MODULE_CORE_MUTEX_DEBUG) + printf("[mutex] waiting for thread %" PRIkernel_pid " (pc = 0x%" PRIxTXTPTR + ")\n", + mutex->owner, mutex->owner_calling_pc); +#endif thread_t *me = thread_get_active(); /* Fail visibly even if a blocking action is called from somewhere where @@ -80,10 +89,17 @@ static inline __attribute__((always_inline)) void _block(mutex_t *mutex, irq_restore(irq_state); thread_yield_higher(); /* We were woken up by scheduler. Waker removed us from queue. */ +#if IS_USED(MODULE_CORE_MUTEX_DEBUG) + mutex->owner_calling_pc = pc; +#endif } bool mutex_lock_internal(mutex_t *mutex, bool block) { + uinttxtptr_t pc = 0; +#if IS_USED(MODULE_CORE_MUTEX_DEBUG) + pc = cpu_get_caller_pc(); +#endif unsigned irq_state = irq_disable(); DEBUG("PID[%" PRIkernel_pid "] mutex_lock_internal(block=%u).\n", @@ -92,9 +108,15 @@ bool mutex_lock_internal(mutex_t *mutex, bool block) if (mutex->queue.next == NULL) { /* mutex is unlocked. */ mutex->queue.next = MUTEX_LOCKED; -#ifdef MODULE_CORE_MUTEX_PRIORITY_INHERITANCE +#if IS_USED(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE) \ + || IS_USED(MODULE_CORE_MUTEX_DEBUG) thread_t *me = thread_get_active(); mutex->owner = me->pid; +#endif +#if IS_USED(MODULE_CORE_MUTEX_DEBUG) + mutex->owner_calling_pc = pc; +#endif +#if IS_USED(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE) mutex->owner_original_priority = me->priority; #endif DEBUG("PID[%" PRIkernel_pid "] mutex_lock(): early out.\n", @@ -106,7 +128,7 @@ bool mutex_lock_internal(mutex_t *mutex, bool block) irq_restore(irq_state); return false; } - _block(mutex, irq_state); + _block(mutex, irq_state, pc); } return true; @@ -114,6 +136,10 @@ bool mutex_lock_internal(mutex_t *mutex, bool block) int mutex_lock_cancelable(mutex_cancel_t *mc) { + uinttxtptr_t pc = 0; +#if IS_USED(MODULE_CORE_MUTEX_DEBUG) + pc = cpu_get_caller_pc(); +#endif unsigned irq_state = irq_disable(); DEBUG("PID[%" PRIkernel_pid "] mutex_lock_cancelable()\n", @@ -131,9 +157,15 @@ int mutex_lock_cancelable(mutex_cancel_t *mc) if (mutex->queue.next == NULL) { /* mutex is unlocked. */ mutex->queue.next = MUTEX_LOCKED; -#ifdef MODULE_CORE_MUTEX_PRIORITY_INHERITANCE +#if IS_USED(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE) \ + || IS_USED(MODULE_CORE_MUTEX_DEBUG) thread_t *me = thread_get_active(); mutex->owner = me->pid; +#endif +#if IS_USED(MODULE_CORE_MUTEX_DEBUG) + mutex->owner_calling_pc = pc; +#endif +#if IS_USED(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE) mutex->owner_original_priority = me->priority; #endif DEBUG("PID[%" PRIkernel_pid "] mutex_lock_cancelable() early out.\n", @@ -142,7 +174,7 @@ int mutex_lock_cancelable(mutex_cancel_t *mc) return 0; } else { - _block(mutex, irq_state); + _block(mutex, irq_state, pc); if (mc->cancelled) { DEBUG("PID[%" PRIkernel_pid "] mutex_lock_cancelable() " "cancelled.\n", thread_getpid()); @@ -185,7 +217,7 @@ void mutex_unlock(mutex_t *mutex) uint16_t process_priority = process->priority; -#ifdef MODULE_CORE_MUTEX_PRIORITY_INHERITANCE +#if IS_USED(MODULE_CORE_MUTEX_PRIORITY_INHERITANCE) thread_t *owner = thread_get(mutex->owner); if ((owner) && (owner->priority != mutex->owner_original_priority)) { DEBUG("PID[%" PRIkernel_pid "] prio %u --> %u\n", @@ -194,6 +226,9 @@ void mutex_unlock(mutex_t *mutex) sched_change_priority(owner, mutex->owner_original_priority); } #endif +#if IS_USED(MODULE_CORE_MUTEX_DEBUG) + mutex->owner_calling_pc = 0; +#endif irq_restore(irqstate); sched_switch(process_priority); diff --git a/cpu/cc26xx_cc13xx/periph/uart.c b/cpu/cc26xx_cc13xx/periph/uart.c index 3f7db1c410..f1c01256fd 100644 --- a/cpu/cc26xx_cc13xx/periph/uart.c +++ b/cpu/cc26xx_cc13xx/periph/uart.c @@ -64,7 +64,7 @@ int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) int cts_pin = uart_config[uart].cts_pin; #endif - if (uart == 0) { + if ((UART_NUMOF == 1) || (uart == 0)) { /* UART0 requires serial domain to be enabled */ if (!power_is_domain_enabled(POWER_DOMAIN_SERIAL)) { power_enable_domain(POWER_DOMAIN_SERIAL); diff --git a/examples/nanocoap_server/Makefile b/examples/nanocoap_server/Makefile index 7f2520df74..4d2fe9afc2 100644 --- a/examples/nanocoap_server/Makefile +++ b/examples/nanocoap_server/Makefile @@ -18,6 +18,7 @@ USEMODULE += sock_udp USEMODULE += gnrc_icmpv6_echo USEMODULE += nanocoap_sock +USEMODULE += nanocoap_resources USEMODULE += xtimer @@ -42,6 +43,23 @@ ifneq (,$(filter $(BOARD),$(LOW_MEMORY_BOARDS))) USEMODULE += prng_minstd endif +# Enable fileserver for boards with plenty of memory +HIGH_MEMORY_BOARDS := native same54-xpro mcb2388 + +ifneq (,$(filter $(BOARD),$(HIGH_MEMORY_BOARDS))) + USEMODULE += gcoap_fileserver + USEMODULE += vfs_default + + # we don't want to run GCoAP + CFLAGS += -DCONFIG_GCOAP_NO_AUTO_INIT=1 + CFLAGS += -DCONFIG_NANOCOAP_SERVER_WELL_KNOWN_CORE=1 + + # always enable auto-format for native + ifeq ($(BOARD),native) + USEMODULE += vfs_auto_format + endif +endif + # Change this to 0 show compiler invocation lines by default: QUIET ?= 1 diff --git a/examples/nanocoap_server/coap_handler.c b/examples/nanocoap_server/coap_handler.c index 09ea785ed2..6bffe8b224 100644 --- a/examples/nanocoap_server/coap_handler.c +++ b/examples/nanocoap_server/coap_handler.c @@ -166,14 +166,38 @@ ssize_t _sha256_handler(coap_pkt_t* pkt, uint8_t *buf, size_t len, coap_request_ return pkt_pos - (uint8_t*)pkt->hdr; } -/* must be sorted by path (ASCII order) */ -const coap_resource_t coap_resources[] = { - COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER, - { "/echo/", COAP_GET | COAP_MATCH_SUBTREE, _echo_handler, NULL }, - { "/riot/board", COAP_GET, _riot_board_handler, NULL }, - { "/riot/value", COAP_GET | COAP_PUT | COAP_POST, _riot_value_handler, NULL }, - { "/riot/ver", COAP_GET, _riot_block2_handler, NULL }, - { "/sha256", COAP_POST, _sha256_handler, NULL }, +NANOCOAP_RESOURCE(echo) { + .path = "/echo/", .methods = COAP_GET | COAP_MATCH_SUBTREE, .handler = _echo_handler +}; +NANOCOAP_RESOURCE(board) { + .path = "/riot/board", .methods = COAP_GET, .handler = _riot_board_handler +}; +NANOCOAP_RESOURCE(value) { + .path = "/riot/value", .methods = COAP_GET | COAP_PUT | COAP_POST, .handler = _riot_value_handler +}; +NANOCOAP_RESOURCE(ver) { + .path = "/riot/ver", .methods = COAP_GET, .handler = _riot_block2_handler +}; +NANOCOAP_RESOURCE(sha256) { + .path = "/sha256", .methods = COAP_POST, .handler = _sha256_handler }; -const unsigned coap_resources_numof = ARRAY_SIZE(coap_resources); +/* we can also include the fileserver module */ +#ifdef MODULE_GCOAP_FILESERVER +#include "net/gcoap/fileserver.h" +#include "vfs_default.h" + +NANOCOAP_RESOURCE(fileserver) { + .path = "/vfs", + .methods = COAP_GET +#if IS_USED(MODULE_GCOAP_FILESERVER_PUT) + | COAP_PUT +#endif +#if IS_USED(MODULE_GCOAP_FILESERVER_DELETE) + | COAP_DELETE +#endif + | COAP_MATCH_SUBTREE, + .handler = gcoap_fileserver_handler, + .context = VFS_DEFAULT_DATA +}; +#endif diff --git a/makefiles/tools/openocd.inc.mk b/makefiles/tools/openocd.inc.mk index 537275348c..b5d3e36244 100644 --- a/makefiles/tools/openocd.inc.mk +++ b/makefiles/tools/openocd.inc.mk @@ -40,7 +40,8 @@ endif ifneq (,$(OPENOCD_CMD_RESET_HALT)) # Export OPENOCD_CMD_RESET_HALT only to the flash targets - $(call target-export-variables,flash%,OPENOCD_CMD_RESET_HALT) + $(call target-export-variables,flash,OPENOCD_CMD_RESET_HALT) + $(call target-export-variables,flash-only,OPENOCD_CMD_RESET_HALT) endif OPENOCD_DEBUG_TARGETS = debug debugr debug-server diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 1b65ead944..b8a0dd8b11 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -754,6 +754,15 @@ ifneq (,$(filter nanocoap_dtls,$(USEMODULE))) USEPKG += tinydtls endif +ifneq (,$(filter nanocoap_server_auto_init,$(USEMODULE))) + USEMODULE += nanocoap_server +endif + +ifneq (,$(filter nanocoap_server,$(USEMODULE))) + USEMODULE += nanocoap_resources + USEMODULE += nanocoap_sock +endif + ifneq (,$(filter nanocoap_sock,$(USEMODULE))) USEMODULE += sock_udp USEMODULE += sock_util diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index b2d2b54745..4d92bf2d87 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -160,6 +160,10 @@ extern void gcoap_init(void); AUTO_INIT(gcoap_init, AUTO_INIT_PRIO_MOD_GCOAP); #endif +#if IS_USED(MODULE_NANOCOAP_SERVER_AUTO_INIT) +extern void auto_init_nanocoap_server(void); +AUTO_INIT(auto_init_nanocoap_server, AUTO_INIT_PRIO_MOD_NANOCOAP); +#endif #if IS_USED(MODULE_DEVFS) extern void auto_init_devfs(void); AUTO_INIT(auto_init_devfs, diff --git a/sys/auto_init/include/auto_init_priorities.h b/sys/auto_init/include/auto_init_priorities.h index cdf916dfa9..beb87e6f05 100644 --- a/sys/auto_init/include/auto_init_priorities.h +++ b/sys/auto_init/include/auto_init_priorities.h @@ -174,6 +174,12 @@ extern "C" { #define AUTO_INIT_PRIO_MOD_UWB_CORE 1230 #endif #ifndef AUTO_INIT_PRIO_MOD_GCOAP +/** + * @brief nanoCoAP server priority + */ +#define AUTO_INIT_PRIO_MOD_NANOCOAP 1235 +#endif +#ifndef AUTO_INIT_PRIO_MOD_GCOAP /** * @brief GCoAP priority */ diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 73f51fe8ff..1fc958c366 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -90,6 +90,7 @@ #include "bitfield.h" #include "byteorder.h" #include "iolist.h" +#include "macros/utils.h" #include "net/coap.h" #else #include "coap.h" @@ -102,6 +103,10 @@ typedef void sock_udp_ep_t; #endif +#if defined(MODULE_NANOCOAP_RESOURCES) +#include "xfa.h" +#endif + #ifdef __cplusplus extern "C" { #endif @@ -402,15 +407,29 @@ typedef struct { uint8_t *opt; /**< Pointer to the placed option */ } coap_block_slicer_t; +#if defined(MODULE_NANOCOAP_RESOURCES) || DOXYGEN +/** + * @brief CoAP XFA resource entry + * + * @param name internal name of the resource entry, must be unique + */ +#define NANOCOAP_RESOURCE(name) \ + XFA_CONST(coap_resources_xfa, 0) coap_resource_t CONCAT(coap_resource_, name) = +#else /** * @brief Global CoAP resource list + * @deprecated Use @ref NANOCOAP_RESOURCE instead. + * The function will be removed after the 2024.01 release. */ extern const coap_resource_t coap_resources[]; /** * @brief Number of entries in global CoAP resource list + * @deprecated Use @ref NANOCOAP_RESOURCE instead. + * The function will be removed after the 2024.01 release. */ extern const unsigned coap_resources_numof; +#endif /** * @name Functions -- Header Read/Write @@ -2048,6 +2067,13 @@ extern ssize_t coap_well_known_core_default_handler(coap_pkt_t *pkt, \ coap_request_ctx_t *context); /**@}*/ +/** + * @brief Respond to `/.well-known/core` to list all resources on the server + */ +#ifndef CONFIG_NANOCOAP_SERVER_WELL_KNOWN_CORE +#define CONFIG_NANOCOAP_SERVER_WELL_KNOWN_CORE !IS_USED(MODULE_GCOAP) +#endif + /** * @brief Checks if a CoAP resource path matches a given URI * diff --git a/sys/include/net/nanocoap_sock.h b/sys/include/net/nanocoap_sock.h index 2e0841ef92..a704639cb0 100644 --- a/sys/include/net/nanocoap_sock.h +++ b/sys/include/net/nanocoap_sock.h @@ -22,17 +22,23 @@ * * ## Server Operation ## * - * See the nanocoap_server example, which is built on the nanocoap_server() - * function. A server must define an array of coap_resource_t resources for - * which it responds. See the declarations of `coap_resources` and - * `coap_resources_numof`. The array contents must be ordered by the resource - * path, specifically the ASCII encoding of the path characters (digit and - * capital precede lower case). Also see _Server path matching_ in the base - * [nanocoap](group__net__nanocoap.html) documentation. + * See the nanocoap_server example, which is built on the `nanocoap_server()` + * function. A server must define CoAP resources for which it responds. + * + * Each @ref coap_resource_t is added to the XFA with NANOCOAP_RESOURCE(name) + * followed by the declaration of the CoAP resource, e.g.: + * + * ```C + * NANOCOAP_RESOURCE(board) { + * .path = "/board", .methods = COAP_GET, .handler = _board_handler, + * }; + * ``` * * nanocoap itself provides the COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER entry for * `/.well-known/core`. * + * To use the CoAP resource XFA, enable the `nanocoap_resources` module. + * * ### Handler functions ### * * For each resource, you must implement a ::coap_handler_t handler function. @@ -153,6 +159,22 @@ extern "C" { #define CONFIG_NANOCOAP_SOCK_DTLS_TAG (0xc0ab) #endif +/** + * @brief CoAP server work buf size + * Used both for RX and TX, needs to hold payload block + header + */ +#ifndef CONFIG_NANOCOAP_SERVER_BUF_SIZE +#define CONFIG_NANOCOAP_SERVER_BUF_SIZE ((1 << (CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT + 3)) \ + + CONFIG_NANOCOAP_URI_MAX + 16) +#endif + +/** + * @brief CoAP server thread stack size + */ +#ifndef CONFIG_NANOCOAP_SERVER_STACK_SIZE +#define CONFIG_NANOCOAP_SERVER_STACK_SIZE THREAD_STACKSIZE_DEFAULT +#endif + /** * @brief NanoCoAP socket types */ @@ -214,6 +236,18 @@ static inline uint16_t nanocoap_sock_next_msg_id(nanocoap_sock_t *sock) */ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize); +/** + * @brief Create and start the nanoCoAP server thread + * + * To automatically start the nanoCoAP server on startup, select the + * `nanocoap_server_auto_init` module. + * + * @param[in] local UDP endpoint to bind to + * + * @return pid of the server thread + */ +kernel_pid_t nanocoap_server_start(const sock_udp_ep_t *local); + /** * @brief Create a CoAP client socket * diff --git a/sys/net/application_layer/gcoap/gcoap.c b/sys/net/application_layer/gcoap/gcoap.c index 136a3bf92b..370cb07a16 100644 --- a/sys/net/application_layer/gcoap/gcoap.c +++ b/sys/net/application_layer/gcoap/gcoap.c @@ -1390,6 +1390,17 @@ kernel_pid_t gcoap_init(void) gcoap_forward_proxy_init(); } +#ifdef MODULE_NANOCOAP_RESOURCES + /* add CoAP resources from XFA */ + XFA_USE_CONST(coap_resource_t, coap_resources_xfa); + static gcoap_listener_t _xfa_listener = { + .resources = coap_resources_xfa, + }; + _xfa_listener.resources_len = XFA_LEN(coap_resource_t, coap_resources_xfa), + + gcoap_register_listener(&_xfa_listener); +#endif + return _pid; } diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index f768ffd8d4..7a83124b59 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -41,6 +41,24 @@ #define COAP_RST (3) /** @} */ +#ifdef MODULE_NANOCOAP_RESOURCES +/** + * @brief CoAP resources XFA + */ +XFA_INIT_CONST(coap_resource_t, coap_resources_xfa); + +/** + * @brief Add well-known .core handler + */ +#if CONFIG_NANOCOAP_SERVER_WELL_KNOWN_CORE +NANOCOAP_RESOURCE(well_known_core) COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER; +#endif + +/* re-define coap_resources for compatibility with non-XFA version */ +#define coap_resources ((const coap_resource_t *)coap_resources_xfa) +#define coap_resources_numof XFA_LEN(coap_resource_t, coap_resources_xfa) +#endif + static int _decode_value(unsigned val, uint8_t **pkt_pos_ptr, uint8_t *pkt_end); static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes); static size_t _encode_uint(uint32_t *val); @@ -552,7 +570,8 @@ ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code, return len; } -ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, uint8_t *token, size_t token_len, unsigned code, uint16_t id) +ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, uint8_t *token, + size_t token_len, unsigned code, uint16_t id) { assert(!(type & ~0x3)); assert(!(token_len & ~0x1f)); @@ -658,7 +677,7 @@ static size_t _encode_uint(uint32_t *val) /* count number of used bytes */ uint32_t tmp = *val; - while(tmp) { + while (tmp) { size++; tmp >>= 8; } diff --git a/sys/net/application_layer/nanocoap/sock.c b/sys/net/application_layer/nanocoap/sock.c index 4f340f3db3..dccef29c75 100644 --- a/sys/net/application_layer/nanocoap/sock.c +++ b/sys/net/application_layer/nanocoap/sock.c @@ -837,3 +837,36 @@ int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize) return 0; } + +static kernel_pid_t _coap_server_pid; +static void *_nanocoap_server_thread(void *local) +{ + static uint8_t buf[CONFIG_NANOCOAP_SERVER_BUF_SIZE]; + + nanocoap_server(local, buf, sizeof(buf)); + + return NULL; +} + +kernel_pid_t nanocoap_server_start(const sock_udp_ep_t *local) +{ + static char stack[CONFIG_NANOCOAP_SERVER_STACK_SIZE]; + + if (_coap_server_pid) { + return _coap_server_pid; + } + _coap_server_pid = thread_create(stack, sizeof(stack), THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, _nanocoap_server_thread, + (void *)local, "nanoCoAP server"); + return _coap_server_pid; +} + +void auto_init_nanocoap_server(void) +{ + sock_udp_ep_t local = { + .port = COAP_PORT, + .family = AF_INET6, + }; + + nanocoap_server_start(&local); +} diff --git a/tests/nanocoap_cli/Makefile b/tests/nanocoap_cli/Makefile index cd7d3c6bfb..0b5193d830 100644 --- a/tests/nanocoap_cli/Makefile +++ b/tests/nanocoap_cli/Makefile @@ -14,6 +14,7 @@ USEMODULE += gnrc_ipv6_default # USEMODULE += gnrc_ipv6_nib_dns # include RDNSS option handling USEMODULE += nanocoap_sock +USEMODULE += nanocoap_resources # boards where basic nanocoap functionality fits, but no VFS LOW_MEMORY_BOARDS := \ diff --git a/tests/nanocoap_cli/request_handlers.c b/tests/nanocoap_cli/request_handlers.c index 4690dde838..56b78162da 100644 --- a/tests/nanocoap_cli/request_handlers.c +++ b/tests/nanocoap_cli/request_handlers.c @@ -72,10 +72,6 @@ static ssize_t _value_handler(coap_pkt_t *pkt, uint8_t *buf, size_t len, coap_re COAP_FORMAT_TEXT, (uint8_t*)rsp, p); } -/* must be sorted by path (ASCII order) */ -const coap_resource_t coap_resources[] = { - COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER, - { "/value", COAP_GET | COAP_PUT | COAP_POST, _value_handler, NULL }, +NANOCOAP_RESOURCE(value) { + .path = "/value", .methods = COAP_GET | COAP_PUT | COAP_POST, .handler = _value_handler, }; - -const unsigned coap_resources_numof = ARRAY_SIZE(coap_resources);