diff --git a/boards/nrf5340dk-app/Kconfig b/boards/nrf5340dk-app/Kconfig index f3e6c5cac21df..849378542fdad 100644 --- a/boards/nrf5340dk-app/Kconfig +++ b/boards/nrf5340dk-app/Kconfig @@ -11,6 +11,7 @@ config BOARD_NRF5340DK_APP bool default y select CPU_MODEL_NRF5340_APP + select HAS_PERIPH_PWM select HAS_PERIPH_TIMER select HAS_PERIPH_UART select HAS_PERIPH_UART_HW_FC diff --git a/boards/nrf5340dk-app/Makefile.features b/boards/nrf5340dk-app/Makefile.features index ea35d40380dc9..b1ad32f7b307a 100644 --- a/boards/nrf5340dk-app/Makefile.features +++ b/boards/nrf5340dk-app/Makefile.features @@ -2,6 +2,7 @@ CPU_MODEL = nrf5340_app CPU = nrf53 # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart FEATURES_PROVIDED += periph_uart_hw_fc diff --git a/boards/nrf5340dk-app/include/periph_conf.h b/boards/nrf5340dk-app/include/periph_conf.h index 64d5820abc2c2..62f2ad08702c1 100644 --- a/boards/nrf5340dk-app/include/periph_conf.h +++ b/boards/nrf5340dk-app/include/periph_conf.h @@ -75,6 +75,25 @@ static const uart_conf_t uart_config[] = { #define UART_NUMOF ARRAY_SIZE(uart_config) /**< UART configuration NUMOF */ /** @} */ +/** + * @name PWM configuration + * @{ + */ +static const pwm_conf_t pwm_config[] = { + { + .dev = NRF_PWM0_S, + .pin = { + LED0_PIN, + LED1_PIN, + LED2_PIN, + LED3_PIN + } + }, +}; + +#define PWM_NUMOF ARRAY_SIZE(pwm_config) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/nrf9160dk/Kconfig b/boards/nrf9160dk/Kconfig index 1760c57488fdb..b810c5bfdff09 100644 --- a/boards/nrf9160dk/Kconfig +++ b/boards/nrf9160dk/Kconfig @@ -12,6 +12,7 @@ config BOARD_NRF9160DK default y select CPU_MODEL_NRF9160 select HAS_PERIPH_I2C + select HAS_PERIPH_PWM select HAS_PERIPH_SPI select HAS_PERIPH_TIMER select HAS_PERIPH_UART diff --git a/boards/nrf9160dk/Makefile.features b/boards/nrf9160dk/Makefile.features index a83af0f726c36..72b1f89b69483 100644 --- a/boards/nrf9160dk/Makefile.features +++ b/boards/nrf9160dk/Makefile.features @@ -3,6 +3,7 @@ CPU = nrf9160 # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_i2c +FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart diff --git a/boards/nrf9160dk/include/periph_conf.h b/boards/nrf9160dk/include/periph_conf.h index 5cbe629bfd7bb..e2991863d4c22 100644 --- a/boards/nrf9160dk/include/periph_conf.h +++ b/boards/nrf9160dk/include/periph_conf.h @@ -116,9 +116,27 @@ static const uart_conf_t uart_config[] = { #define UART_0_ISR (isr_uarte0_spim0_spis0_twim0_twis0) /**< UART0_IRQ */ #define UART_1_ISR (isr_uarte1_spim1_spis1_twim1_twis1) /**< UART1_IRQ */ -#define UART_NUMOF ARRAY_SIZE(uart_config) /**< UART confgiguration NUMOF */ +#define UART_NUMOF ARRAY_SIZE(uart_config) /**< UART configuration NUMOF */ /** @} */ +/** + * @name PWM configuration + * @{ + */ +static const pwm_conf_t pwm_config[] = { + { + .dev = NRF_PWM0_S, + .pin = { + LED0_PIN, + LED1_PIN, + LED2_PIN, + LED3_PIN + } + }, +}; + +#define PWM_NUMOF ARRAY_SIZE(pwm_config) + #ifdef __cplusplus } #endif diff --git a/cpu/nrf52/include/periph_cpu.h b/cpu/nrf52/include/periph_cpu.h index 662e1e9b702ae..e1a5f9fac4d21 100644 --- a/cpu/nrf52/include/periph_cpu.h +++ b/cpu/nrf52/include/periph_cpu.h @@ -153,54 +153,6 @@ typedef struct { #define i2c_pin_scl(dev) i2c_config[dev].scl /** @} */ -/** - * @name The PWM unit on the nRF52 supports 4 channels per device - */ -#define PWM_CHANNELS (4U) - -/** - * @name Generate PWM mode values - * - * To encode the PWM mode, we use two bit: - * - bit 0: select up or up-and-down counting - * - bit 15: select polarity - */ -#define PWM_MODE(ud, pol) (ud | (pol << 15)) - -/** - * @brief Override the PWM mode definitions - * @{ - */ -#define HAVE_PWM_MODE_T -typedef enum { - PWM_LEFT = PWM_MODE(0, 1), /**< left aligned PWM */ - PWM_RIGHT = PWM_MODE(0, 0), /**< right aligned PWM */ - PWM_CENTER = PWM_MODE(1, 1), /**< not supported */ - PWM_CENTER_INV = PWM_MODE(1, 0) /**< not supported */ -} pwm_mode_t; -/** @} */ - -/** - * @brief PWM configuration options - * - * Each device supports up to 4 channels. If you want to use less than 4 - * channels, just set the unused pins to GPIO_UNDEF. - * - * @note define unused pins only from right to left, so the defined channels - * always start with channel 0 to x and the undefined ones are from x+1 - * to PWM_CHANNELS. - * - * @warning All the channels not in active use must be set to GPIO_UNDEF; just - * initializing fewer members of pin would insert a 0 value, which - * would be interpreted as the P0.00 pin that's then driven. - */ -#if defined(PWM_PRESENT) || DOXYGEN -typedef struct { - NRF_PWM_Type *dev; /**< PWM device descriptor */ - gpio_t pin[PWM_CHANNELS]; /**< PWM out pins */ -} pwm_conf_t; -#endif - /** * @brief Size of the UART TX buffer for non-blocking mode. */ diff --git a/cpu/nrf53/include/periph_cpu.h b/cpu/nrf53/include/periph_cpu.h index 7dd0ab9974a95..46bd07ce58735 100644 --- a/cpu/nrf53/include/periph_cpu.h +++ b/cpu/nrf53/include/periph_cpu.h @@ -26,6 +26,15 @@ extern "C" { #endif +/** + * @brief Peripheral clocks speed + */ +#define PERIPH_CLOCK_1MHZ MHZ(1) /**< 1MHz peripheral clock */ +#define PERIPH_CLOCK_16MHZ MHZ(16) /**< 16MHz peripheral clock */ +#define PERIPH_CLOCK_32MHZ MHZ(32) /**< 32MHz peripheral clock */ +#define PERIPH_CLOCK_64MHZ MHZ(64) /**< 64MHz peripheral clock */ +#define PERIPH_CLOCK PERIPH_CLOCK_16MHZ /**< For driver compatibility */ + #ifndef DOXYGEN /** * @brief Wrapper to fix differences between nRF families vendor files diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index 82c84048294f0..18f61b844b42d 100644 --- a/cpu/nrf5x_common/include/periph_cpu_common.h +++ b/cpu/nrf5x_common/include/periph_cpu_common.h @@ -302,6 +302,55 @@ typedef struct { #endif /* ndef CPU_MODEL_NRF52832XXAA && ndef CPU_FAM_NRF51 */ +#if !defined(CPU_FAM_NRF51) && !defined(DOXYGEN) +/** + * @brief The PWM unit on the nRF52, nRF53 and nRF9160 + * supports 4 channels per device + */ +#define PWM_CHANNELS (4U) + +/** + * @brief Generate PWM mode values + * + * To encode the PWM mode, we use two bit: + * - bit 0: select up or up-and-down counting + * - bit 15: select polarity + */ +#define PWM_MODE(ud, pol) (ud | (pol << 15)) + +/** + * @brief Override the PWM mode definitions + */ +#define HAVE_PWM_MODE_T +typedef enum { + PWM_LEFT = PWM_MODE(0, 1), /**< left aligned PWM */ + PWM_RIGHT = PWM_MODE(0, 0), /**< right aligned PWM */ + PWM_CENTER = PWM_MODE(1, 1), /**< not supported */ + PWM_CENTER_INV = PWM_MODE(1, 0) /**< not supported */ +} pwm_mode_t; + +/** + * @brief PWM configuration options + * + * Each device supports up to 4 channels. If you want to use less than 4 + * channels, just set the unused pins to GPIO_UNDEF. + * + * @note define unused pins only from right to left, so the defined channels + * always start with channel 0 to x and the undefined ones are from x+1 + * to PWM_CHANNELS. + * + * @warning All the channels not in active use must be set to GPIO_UNDEF; just + * initializing fewer members of pin would insert a 0 value, which + * would be interpreted as the P0.00 pin that's then driven. + */ +#if defined(PWM_PRESENT) +typedef struct { + NRF_PWM_Type *dev; /**< PWM device descriptor */ + gpio_t pin[PWM_CHANNELS]; /**< PWM out pins */ +} pwm_conf_t; +#endif +#endif /* ndef CPU_FAM_NRF51 */ + #ifdef __cplusplus } #endif diff --git a/cpu/nrf5x_common/periph/Makefile b/cpu/nrf5x_common/periph/Makefile index d297d3c6790f3..cab8572513819 100644 --- a/cpu/nrf5x_common/periph/Makefile +++ b/cpu/nrf5x_common/periph/Makefile @@ -7,6 +7,14 @@ ifneq (,$(filter periph_i2c,$(USEMODULE))) endif endif +# Select the specific implementation for `periph_pwm` +# nRF51 has its own PWM driver variant in its periph driver folder +ifneq (,$(filter periph_pwm,$(USEMODULE))) + ifneq (,$(filter $(CPU_FAM),nrf52 nrf53 nrf9160)) + SRC += pwm_nrfxx.c + endif +endif + # Select the specific implementation for `periph_spi` ifneq (,$(filter periph_spi,$(USEMODULE))) ifneq (,$(filter $(CPU_FAM),nrf52 nrf9160)) diff --git a/cpu/nrf52/periph/pwm.c b/cpu/nrf5x_common/periph/pwm_nrfxx.c similarity index 99% rename from cpu/nrf52/periph/pwm.c rename to cpu/nrf5x_common/periph/pwm_nrfxx.c index 798adb8499d36..5b766bb1e4180 100644 --- a/cpu/nrf52/periph/pwm.c +++ b/cpu/nrf5x_common/periph/pwm_nrfxx.c @@ -7,7 +7,7 @@ */ /** - * @ingroup cpu_nrf52 + * @ingroup cpu_nrf5x_common * @{ * * @file diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h index 30cd28edbde70..700b8066a7561 100644 --- a/sys/include/net/nanocoap.h +++ b/sys/include/net/nanocoap.h @@ -92,6 +92,7 @@ #include "iolist.h" #include "macros/utils.h" #include "net/coap.h" +#include "modules.h" #else #include "coap.h" #include @@ -513,15 +514,37 @@ static inline unsigned coap_get_id(const coap_pkt_t *pkt) /** * @brief Get a message's token length [in byte] * + * If the `nanocoap_token_ext` module is enabled, this will include + * the extended token length. + * * @param[in] pkt CoAP packet * * @returns length of token in the given message (0-8 byte) */ static inline unsigned coap_get_token_len(const coap_pkt_t *pkt) { - return (pkt->hdr->ver_t_tkl & 0xf); + uint8_t tkl = pkt->hdr->ver_t_tkl & 0xf; + + if (!IS_USED(MODULE_NANOCOAP_TOKEN_EXT)) { + return tkl; + } + + void *ext = pkt->hdr + 1; + switch (tkl) { + case 13: + return tkl + *(uint8_t *)ext; + case 14: + return tkl + 255 + byteorder_bebuftohs(ext); + case 15: + assert(0); + /* fall-through */ + default: + return tkl; + } } +static inline uint8_t *coap_hdr_data_ptr(const coap_hdr_t *hdr); + /** * @brief Get pointer to a message's token * @@ -531,7 +554,7 @@ static inline unsigned coap_get_token_len(const coap_pkt_t *pkt) */ static inline void *coap_get_token(const coap_pkt_t *pkt) { - return (uint8_t*)pkt->hdr + sizeof(coap_hdr_t); + return coap_hdr_data_ptr(pkt->hdr); } /** @@ -587,6 +610,35 @@ static inline unsigned coap_get_ver(const coap_pkt_t *pkt) return (pkt->hdr->ver_t_tkl & 0x60) >> 6; } +/** + * @brief Get the size of the extended Token length field + * (RFC 8974) + * + * @note This requires the `nanocoap_token_ext` module to be enabled + * + * @param[in] hdr CoAP header + * + * @returns number of bytes used for extended token length + */ +static inline uint8_t coap_hdr_tkl_ext_len(const coap_hdr_t *hdr) +{ + if (!IS_USED(MODULE_NANOCOAP_TOKEN_EXT)) { + return 0; + } + + switch (hdr->ver_t_tkl & 0xf) { + case 13: + return 1; + case 14: + return 2; + case 15: + assert(0); + /* fall-through */ + default: + return 0; + } +} + /** * @brief Get the start of data after the header * @@ -596,7 +648,7 @@ static inline unsigned coap_get_ver(const coap_pkt_t *pkt) */ static inline uint8_t *coap_hdr_data_ptr(const coap_hdr_t *hdr) { - return ((uint8_t *)hdr) + sizeof(coap_hdr_t); + return ((uint8_t *)hdr) + sizeof(coap_hdr_t) + coap_hdr_tkl_ext_len(hdr); } /** diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c index bbcfcd771fb91..b5a651897925b 100644 --- a/sys/net/application_layer/nanocoap/nanocoap.c +++ b/sys/net/application_layer/nanocoap/nanocoap.c @@ -98,7 +98,13 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len) return -EBADMSG; } - if (coap_get_token_len(pkt) > COAP_TOKEN_LENGTH_MAX) { + if (IS_USED(MODULE_NANOCOAP_TOKEN_EXT)) { + if ((pkt->hdr->ver_t_tkl & 0xf) == 15) { + DEBUG("nanocoap: token length is reserved value 15," + "invalid for extended token length field.\n"); + return -EBADMSG; + } + } else if (coap_get_token_len(pkt) > COAP_TOKEN_LENGTH_MAX) { DEBUG("nanocoap: token length invalid\n"); return -EBADMSG; } @@ -586,18 +592,39 @@ 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)); + + uint16_t tkl_ext; + uint8_t tkl_ext_len, tkl; + + if (token_len > 268 && IS_USED(MODULE_NANOCOAP_TOKEN_EXT)) { + tkl_ext_len = 2; + tkl_ext = htons(token_len - 269); /* 269 = 255 + 14 */ + tkl = 14; + } + else if (token_len > 12 && IS_USED(MODULE_NANOCOAP_TOKEN_EXT)) { + tkl_ext_len = 1; + tkl_ext = token_len - 13; + tkl = 13; + } + else { + tkl = token_len; + tkl_ext_len = 0; + } memset(hdr, 0, sizeof(coap_hdr_t)); - hdr->ver_t_tkl = (COAP_V1 << 6) | (type << 4) | token_len; + hdr->ver_t_tkl = (COAP_V1 << 6) | (type << 4) | tkl; hdr->code = code; hdr->id = htons(id); + if (tkl_ext_len) { + memcpy(hdr + 1, &tkl_ext, tkl_ext_len); + } + if (token_len) { memcpy(coap_hdr_data_ptr(hdr), token, token_len); } - return sizeof(coap_hdr_t) + token_len; + return sizeof(coap_hdr_t) + token_len + tkl_ext_len; } void coap_pkt_init(coap_pkt_t *pkt, uint8_t *buf, size_t len, size_t header_len) diff --git a/tests/unittests/tests-nanocoap/Makefile.include b/tests/unittests/tests-nanocoap/Makefile.include index cd6b8172a7543..2b94b4ef2effc 100644 --- a/tests/unittests/tests-nanocoap/Makefile.include +++ b/tests/unittests/tests-nanocoap/Makefile.include @@ -1 +1,2 @@ USEMODULE += nanocoap +USEMODULE += nanocoap_token_ext diff --git a/tests/unittests/tests-nanocoap/tests-nanocoap.c b/tests/unittests/tests-nanocoap/tests-nanocoap.c index b3c2888eda380..75f5cb14377ee 100644 --- a/tests/unittests/tests-nanocoap/tests-nanocoap.c +++ b/tests/unittests/tests-nanocoap/tests-nanocoap.c @@ -1035,13 +1035,13 @@ static void test_nanocoap__add_get_proxy_uri(void) */ static void test_nanocoap__token_length_over_limit(void) { - /* RFC7252 states that TKL must be within 0-8: - * "Lengths 9-15 are reserved, MUST NOT be sent, - * and MUST be processed as a message format error." + /* RFC8974 states that TKL must not be 15: + * 15: Reserved. This value MUST NOT be sent and MUST be processed + * as a message-format error. */ uint16_t msgid = 0xABCD; uint8_t buf_invalid[] = { - 0x49, 0x01, 0xAB, 0xCD, + 0x4F, 0x01, 0xAB, 0xCD, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99 }; uint8_t buf_valid[] = { @@ -1059,11 +1059,64 @@ static void test_nanocoap__token_length_over_limit(void) TEST_ASSERT_EQUAL_INT(8, coap_get_token_len(&pkt)); TEST_ASSERT_EQUAL_INT(0, pkt.payload_len); - /* Invalid packet (TKL = 9) */ + /* Invalid packet (TKL = 15) */ res = coap_parse(&pkt, buf_invalid, sizeof(buf_invalid)); TEST_ASSERT_EQUAL_INT(-EBADMSG, res); } +/* + * Verifies that coap_parse() recognizes 8 bit extended token length + */ +static void test_nanocoap__token_length_ext_16(void) +{ + const char *token = "0123456789ABCDEF"; + + uint8_t buf[32]; + coap_hdr_t *hdr = (void *)buf; + + TEST_ASSERT_EQUAL_INT(21, coap_build_hdr(hdr, COAP_TYPE_CON, + (void *)token, strlen(token), + COAP_CODE_204, 23)); + coap_pkt_t pkt; + int res = coap_parse(&pkt, buf, 21); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(204, coap_get_code(&pkt)); + TEST_ASSERT_EQUAL_INT(23, coap_get_id(&pkt)); + TEST_ASSERT_EQUAL_INT(strlen(token), coap_get_token_len(&pkt)); + TEST_ASSERT_EQUAL_INT(0, memcmp(coap_get_token(&pkt), token, strlen(token))); + TEST_ASSERT_EQUAL_INT(0, pkt.payload_len); + TEST_ASSERT_EQUAL_INT(13, hdr->ver_t_tkl & 0xf); +} + +/* + * Verifies that coap_parse() recognizes 16 bit extended token length + */ +static void test_nanocoap__token_length_ext_269(void) +{ + const char *token = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr," + "sed diam nonumy eirmod tempor invidunt ut labore et dolore" + "magna aliquyam erat, sed diam voluptua. At vero eos et accusam" + "et justo duo dolores et ea rebum. Stet clita kasd gubergren," + "no sea takimata sanctus est Lore."; + uint8_t buf[280]; + coap_hdr_t *hdr = (void *)buf; + + TEST_ASSERT_EQUAL_INT(275, coap_build_hdr(hdr, COAP_TYPE_CON, + (void *)token, strlen(token), + COAP_CODE_204, 23)); + coap_pkt_t pkt; + int res = coap_parse(&pkt, buf, 275); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(204, coap_get_code(&pkt)); + TEST_ASSERT_EQUAL_INT(23, coap_get_id(&pkt)); + TEST_ASSERT_EQUAL_INT(strlen(token), coap_get_token_len(&pkt)); + TEST_ASSERT_EQUAL_INT(0, memcmp(coap_get_token(&pkt), token, strlen(token))); + TEST_ASSERT_EQUAL_INT(0, pkt.payload_len); + TEST_ASSERT_EQUAL_INT(14, hdr->ver_t_tkl & 0xf); +} + Test *tests_nanocoap_tests(void) { EMB_UNIT_TESTFIXTURES(fixtures) { @@ -1100,6 +1153,8 @@ Test *tests_nanocoap_tests(void) new_TestFixture(test_nanocoap__add_path_unterminated_string), new_TestFixture(test_nanocoap__add_get_proxy_uri), new_TestFixture(test_nanocoap__token_length_over_limit), + new_TestFixture(test_nanocoap__token_length_ext_16), + new_TestFixture(test_nanocoap__token_length_ext_269), }; EMB_UNIT_TESTCALLER(nanocoap_tests, NULL, NULL, fixtures);