diff --git a/Makefile.dep b/Makefile.dep index 9cd813d3b2da..4bfb0e869b35 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -619,6 +619,13 @@ ifneq (,$(filter sock_dns,$(USEMODULE))) USEMODULE += sock_util endif +ifneq (,$(filter spiffs,$(USEMODULE))) + USEPKG += spiffs + USEMODULE += vfs + USEMODULE += spiffs_fs + USEMODULE += mtd +endif + # include package dependencies -include $(USEPKG:%=$(RIOTPKG)/%/Makefile.dep) diff --git a/boards/fox/include/periph_conf.h b/boards/fox/include/periph_conf.h index 3da99e442007..65c3e2461355 100644 --- a/boards/fox/include/periph_conf.h +++ b/boards/fox/include/periph_conf.h @@ -175,7 +175,7 @@ static const spi_conf_t spi_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (36000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/iotlab-common/include/periph_conf_common.h b/boards/iotlab-common/include/periph_conf_common.h index b0d104c9701b..fa32e90ba703 100644 --- a/boards/iotlab-common/include/periph_conf_common.h +++ b/boards/iotlab-common/include/periph_conf_common.h @@ -144,7 +144,7 @@ static const uart_conf_t uart_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (36000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/limifrog-v1/include/periph_conf.h b/boards/limifrog-v1/include/periph_conf.h index 6bf9eb2981e4..0762a4b8475e 100644 --- a/boards/limifrog-v1/include/periph_conf.h +++ b/boards/limifrog-v1/include/periph_conf.h @@ -166,7 +166,7 @@ static const spi_conf_t spi_config[] = { #define I2C_1_EN 1 #define I2C_NUMOF (I2C_0_EN + I2C_1_EN) #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (36000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_EVT_ISR isr_i2c1_ev diff --git a/boards/maple-mini/include/periph_conf.h b/boards/maple-mini/include/periph_conf.h index ee3d650fe69e..9cd4ecd76453 100644 --- a/boards/maple-mini/include/periph_conf.h +++ b/boards/maple-mini/include/periph_conf.h @@ -136,7 +136,7 @@ static const uart_conf_t uart_config[] = { #define I2C_0_EN 1 #define I2C_1_EN 0 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (36000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/msbiot/include/periph_conf.h b/boards/msbiot/include/periph_conf.h index db29cb2d6df8..2dda1f35afba 100644 --- a/boards/msbiot/include/periph_conf.h +++ b/boards/msbiot/include/periph_conf.h @@ -233,7 +233,7 @@ static const spi_conf_t spi_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (42000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/native/include/board.h b/boards/native/include/board.h index c2275795a604..84d66cdad39f 100644 --- a/boards/native/include/board.h +++ b/boards/native/include/board.h @@ -60,6 +60,31 @@ void _native_LED_RED_TOGGLE(void); extern mtd_dev_t *mtd0; #endif +#ifdef MODULE_SPIFFS +#define SPIFFS_READ_ONLY 0 +#define SPIFFS_SINGLETON 0 + +#define SPIFFS_HAL_CALLBACK_EXTRA 1 + +#define SPIFFS_CACHE 1 + +#if SPIFFS_SINGLETON == 1 +#define SPIFFS_CFG_PHYS_SZ(ignore) (0x800000) + +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4096) + +#define SPIFFS_CFG_PHYS_ADDR(ignore) (0) + +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) + +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (4096) +#endif + +#if SPIFFS_HAL_CALLBACK_EXTRA == 0 +#define SPIFFS_MTD_DEV (MTD_0) +#endif +#endif + #ifdef __cplusplus } #endif diff --git a/boards/nucleo-f103/include/periph_conf.h b/boards/nucleo-f103/include/periph_conf.h index b36481b2f32c..a8b9ad5b1def 100644 --- a/boards/nucleo-f103/include/periph_conf.h +++ b/boards/nucleo-f103/include/periph_conf.h @@ -132,7 +132,7 @@ static const uart_conf_t uart_config[] = { #define I2C_0_EN 1 #define I2C_1_EN 0 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (36000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/nucleo-f303/include/periph_conf.h b/boards/nucleo-f303/include/periph_conf.h index 8e4a89a29ba1..dcb3ba81ee56 100755 --- a/boards/nucleo-f303/include/periph_conf.h +++ b/boards/nucleo-f303/include/periph_conf.h @@ -195,7 +195,7 @@ static const spi_conf_t spi_config[] = { #define I2C_0_EN 1 #define I2C_1_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (36000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/nucleo-f401/include/periph_conf.h b/boards/nucleo-f401/include/periph_conf.h index 444fab10e06a..a9b5430016e0 100644 --- a/boards/nucleo-f401/include/periph_conf.h +++ b/boards/nucleo-f401/include/periph_conf.h @@ -208,7 +208,7 @@ static const spi_conf_t spi_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (42000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/nucleo-f410/include/periph_conf.h b/boards/nucleo-f410/include/periph_conf.h index 35d58f445a05..f42e3084d9f2 100644 --- a/boards/nucleo-f410/include/periph_conf.h +++ b/boards/nucleo-f410/include/periph_conf.h @@ -159,7 +159,7 @@ static const spi_conf_t spi_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (42000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/nucleo-f411/include/periph_conf.h b/boards/nucleo-f411/include/periph_conf.h index f5681a534f81..69934cc724af 100644 --- a/boards/nucleo-f411/include/periph_conf.h +++ b/boards/nucleo-f411/include/periph_conf.h @@ -205,7 +205,7 @@ static const spi_conf_t spi_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (42000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/nucleo-f446/include/periph_conf.h b/boards/nucleo-f446/include/periph_conf.h index 2cdf10bce948..1f44c716bd25 100644 --- a/boards/nucleo-f446/include/periph_conf.h +++ b/boards/nucleo-f446/include/periph_conf.h @@ -234,7 +234,7 @@ static const spi_conf_t spi_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (42000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/nucleo-l053/Makefile.features b/boards/nucleo-l053/Makefile.features index ae8430d7d88f..682b380690f8 100644 --- a/boards/nucleo-l053/Makefile.features +++ b/boards/nucleo-l053/Makefile.features @@ -1,6 +1,7 @@ # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer diff --git a/boards/nucleo-l073/Makefile.features b/boards/nucleo-l073/Makefile.features index ae8430d7d88f..682b380690f8 100644 --- a/boards/nucleo-l073/Makefile.features +++ b/boards/nucleo-l073/Makefile.features @@ -1,6 +1,7 @@ # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer diff --git a/boards/nucleo-l1/include/periph_conf.h b/boards/nucleo-l1/include/periph_conf.h index 9a385f34ebca..c74b43ecb1a9 100644 --- a/boards/nucleo-l1/include/periph_conf.h +++ b/boards/nucleo-l1/include/periph_conf.h @@ -204,7 +204,7 @@ static const spi_conf_t spi_config[] = { #define I2C_1_EN 1 #define I2C_NUMOF (I2C_0_EN + I2C_1_EN) #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (36000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_EVT_ISR isr_i2c1_ev diff --git a/boards/nucleo-l476/Makefile.features b/boards/nucleo-l476/Makefile.features index 7ab9ac62907b..20a7f47e1df3 100644 --- a/boards/nucleo-l476/Makefile.features +++ b/boards/nucleo-l476/Makefile.features @@ -1,6 +1,7 @@ # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer diff --git a/boards/nucleo144-f207/Makefile.features b/boards/nucleo144-f207/Makefile.features index 56c0fd5e5427..3504158d0298 100644 --- a/boards/nucleo144-f207/Makefile.features +++ b/boards/nucleo144-f207/Makefile.features @@ -1,6 +1,7 @@ # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_rtc diff --git a/boards/nucleo144-f413/Makefile.features b/boards/nucleo144-f413/Makefile.features index d0999394027b..f31982e64dc3 100644 --- a/boards/nucleo144-f413/Makefile.features +++ b/boards/nucleo144-f413/Makefile.features @@ -1,12 +1,13 @@ # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_cpuid -FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_hwrng +FEATURES_PROVIDED += periph_i2c +FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer FEATURES_PROVIDED += periph_uart -FEATURES_PROVIDED += periph_pwm # load the common Makefile.features for Nucleo boards include $(RIOTBOARD)/nucleo144-common/Makefile.features diff --git a/boards/nucleo144-f413/include/periph_conf.h b/boards/nucleo144-f413/include/periph_conf.h index 5a475031e442..a47fb1d92e77 100644 --- a/boards/nucleo144-f413/include/periph_conf.h +++ b/boards/nucleo144-f413/include/periph_conf.h @@ -218,7 +218,7 @@ static const spi_conf_t spi_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (42000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/nucleo144-f429/Makefile.features b/boards/nucleo144-f429/Makefile.features index 5d69098c65b8..8bab3dc608b4 100644 --- a/boards/nucleo144-f429/Makefile.features +++ b/boards/nucleo144-f429/Makefile.features @@ -1,6 +1,7 @@ # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_pwm FEATURES_PROVIDED += periph_spi diff --git a/boards/opencm904/include/board.h b/boards/opencm904/include/board.h index 12c9c129912e..4fb218f24a39 100755 --- a/boards/opencm904/include/board.h +++ b/boards/opencm904/include/board.h @@ -66,6 +66,15 @@ extern "C" { */ #define UART_STDIO_DEV UART_DEV(0) +/** + * @brief Override with ROBOTIS Bluetooth antenna baudrate for STDIO + * @{ + */ +#ifndef UART_STDIO_BAUDRATE +#define UART_STDIO_BAUDRATE (921600UL) +#endif +/** @} */ + /** * @brief Initialize board specific hardware, including clock, LEDs and std-IO */ diff --git a/boards/stm32f3discovery/include/periph_conf.h b/boards/stm32f3discovery/include/periph_conf.h index c095d8c48c8b..75a42f86a2cd 100644 --- a/boards/stm32f3discovery/include/periph_conf.h +++ b/boards/stm32f3discovery/include/periph_conf.h @@ -204,7 +204,7 @@ static const spi_conf_t spi_config[] = { #define I2C_0_EN 1 #define I2C_1_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (36000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/boards/stm32f4discovery/include/periph_conf.h b/boards/stm32f4discovery/include/periph_conf.h index 18af0c234504..e377a77fa91c 100644 --- a/boards/stm32f4discovery/include/periph_conf.h +++ b/boards/stm32f4discovery/include/periph_conf.h @@ -238,7 +238,7 @@ static const spi_conf_t spi_config[] = { #define I2C_NUMOF (1U) #define I2C_0_EN 1 #define I2C_IRQ_PRIO 1 -#define I2C_APBCLK (42000000U) +#define I2C_APBCLK (CLOCK_APB1) /* I2C 0 device configuration */ #define I2C_0_DEV I2C1 diff --git a/cpu/native/include/mtd_native.h b/cpu/native/include/mtd_native.h index bc412ccb246e..3cd0548247c9 100644 --- a/cpu/native/include/mtd_native.h +++ b/cpu/native/include/mtd_native.h @@ -7,8 +7,8 @@ */ /** - * @ingroup mtd - * @defgroup mtd_native Native MTD + * @ingroup drivers_mtd + * @defgroup drivers_mtd_native Native MTD * @{ * @brief mtd flash emulation for native * diff --git a/cpu/native/mtd/mtd_native.c b/cpu/native/mtd/mtd_native.c index be6e361e275f..5e6089df94d1 100644 --- a/cpu/native/mtd/mtd_native.c +++ b/cpu/native/mtd/mtd_native.c @@ -7,7 +7,6 @@ */ /** - * @ingroup mtd_native * @{ * @brief mtd flash emulation for native * diff --git a/cpu/stm32f4/periph/hwrng.c b/cpu/stm32_common/periph/hwrng.c similarity index 82% rename from cpu/stm32f4/periph/hwrng.c rename to cpu/stm32_common/periph/hwrng.c index 641d334f5cd4..98409479afdf 100644 --- a/cpu/stm32f4/periph/hwrng.c +++ b/cpu/stm32_common/periph/hwrng.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2014-2016 Freie Universität Berlin + * Copyright (C) 2014-2017 Freie Universität Berlin + * 2016 OTA keys S.A. * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -7,13 +8,14 @@ */ /** - * @ingroup cpu_stm32f4 + * @ingroup cpu_stm32_common * @{ * * @file * @brief Low-level random number generator driver implementation * * @author Hauke Petersen + * @author Aurelien Gonce * * @} */ @@ -38,6 +40,8 @@ void hwrng_read(void *buf, unsigned int num) /* power on and enable the device */ #if defined(CPU_MODEL_STM32F410RB) periph_clk_en(AHB1, RCC_AHB1ENR_RNGEN); +#elif defined(CPU_FAM_STM32L0) + periph_clk_en(AHB, RCC_AHBENR_RNGEN); #else periph_clk_en(AHB2, RCC_AHB2ENR_RNGEN); #endif @@ -60,6 +64,8 @@ void hwrng_read(void *buf, unsigned int num) RNG->CR = 0; #if defined(CPU_MODEL_STM32F410RB) periph_clk_dis(AHB1, RCC_AHB1ENR_RNGEN); +#elif defined(CPU_FAM_STM32L0) + periph_clk_dis(AHB, RCC_AHBENR_RNGEN); #else periph_clk_dis(AHB2, RCC_AHB2ENR_RNGEN); #endif diff --git a/cpu/stm32f2/periph/hwrng.c b/cpu/stm32f2/periph/hwrng.c deleted file mode 100644 index 52bf752ac489..000000000000 --- a/cpu/stm32f2/periph/hwrng.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2014 Freie Universität Berlin - * Copyright (C) 2016 OTA keys S.A. - * - * This file is subject to the terms and conditions of the GNU Lesser General - * Public License v2.1. See the file LICENSE in the top level directory for more - * details. - */ - -/** - * @ingroup cpu_stm32f2 - * @{ - * - * @file - * @brief Low-level random number generator driver implementation - * - * @author Hauke Petersen - * @author Aurelien Gonce - * - * @} - */ - -#include "cpu.h" -#include "periph/hwrng.h" -#include "periph_conf.h" - -/* ignore file in case no RNG device is defined */ -#ifdef RNG - -void hwrng_init(void) -{ - /* enable RNG reset state */ - periph_clk_en(AHB2, RCC_AHB2ENR_RNGEN); - /* release RNG from reset state */ - periph_clk_dis(AHB2, RCC_AHB2ENR_RNGEN); -} - - -void hwrng_read(void *buf, unsigned int num) -{ - /* cppcheck-suppress variableScope */ - uint32_t tmp; - unsigned int count = 0; - uint8_t *b = (uint8_t *)buf; - - /* enable RNG reset state */ - periph_clk_en(AHB2, RCC_AHB2ENR_RNGEN); - /* enable the RNG */ - RNG->CR |= RNG_CR_RNGEN; - - while (count < num) { - /* wait for random data to be ready to read */ - while (!(RNG->SR & RNG_SR_DRDY)); - /* read next 4 bytes */ - tmp = RNG->DR; - /* copy data into result vector */ - for (int i = 0; i < 4 && count < num; i++) { - b[count++] = (uint8_t)tmp; - tmp = tmp >> 8; - } - } - - /* disable the RNG */ - RNG->CR &= ~RNG_CR_RNGEN; - /* release RNG from reset state */ - periph_clk_dis(AHB2, RCC_AHB2ENR_RNGEN); -} - -#endif /* RANDOM_NUMOF */ diff --git a/cpu/stm32l4/cpu.c b/cpu/stm32l4/cpu.c index a270a4b4d022..ab818a34f2e7 100644 --- a/cpu/stm32l4/cpu.c +++ b/cpu/stm32l4/cpu.c @@ -143,6 +143,9 @@ static void cpu_clock_init(void) while (!(RCC->CSR & RCC_CSR_LSIRDY)) {} #endif + /* select the MSI clock for the 48MHz clock tree (USB, RNG) */ + RCC->CCIPR = (RCC_CCIPR_CLK48SEL_0 | RCC_CCIPR_CLK48SEL_1); + /* if configured: enable the HSE clock */ #if CLOCK_HSE RCC->CR |= RCC_CR_HSEON; diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index 3e891a1e8a38..5c083df737d2 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -219,6 +219,10 @@ ifneq (,$(filter feetech,$(USEMODULE))) USEMODULE += uart_half_duplex endif +ifneq (,$(filter dynamixel,$(USEMODULE))) + USEMODULE += uart_half_duplex +endif + ifneq (,$(filter mtd_spi_nor,$(USEMODULE))) USEMODULE += mtd FEATURES_REQUIRED += periph_spi diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 096e88bad380..cc2c4d4a1ebb 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -115,3 +115,6 @@ endif ifneq (,$(filter feetech,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/feetech/include endif +ifneq (,$(filter dynamixel,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/dynamixel/include +endif diff --git a/drivers/dynamixel/Makefile b/drivers/dynamixel/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/drivers/dynamixel/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/dynamixel/crc.c b/drivers/dynamixel/crc.c new file mode 100644 index 000000000000..5c6a63cff642 --- /dev/null +++ b/drivers/dynamixel/crc.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_dynamixel + * @{ + * + * @file + * @brief Dynamixel CRC computation + * + * @author Loïc Dauphin + * + * @} + */ + +#include "dynamixel_crc.h" + +static const uint16_t _crc_table[256] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; + +uint16_t dynamixel_crc_update(uint16_t crc_accum, const uint8_t *buffer, size_t size) +{ + for (size_t j = 0; j < size; j++) { + const uint16_t i = ((uint16_t)(crc_accum >> 8) ^ buffer[j]) & 0xFF; + crc_accum = (crc_accum << 8) ^ _crc_table[i]; + } + return crc_accum; +} diff --git a/drivers/dynamixel/dynamixel.c b/drivers/dynamixel/dynamixel.c new file mode 100644 index 000000000000..86e35fa8e826 --- /dev/null +++ b/drivers/dynamixel/dynamixel.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_dynamixel + * @{ + * + * @file + * @brief Driver implementation for Dynamixel devices + * + * @author Loïc Dauphin + * + * @} + */ + +#include "dynamixel.h" + +#include "dynamixel_protocol.h" +#include "dynamixel_reader.h" +#include "dynamixel_writer.h" + +#include + +void dynamixel_init(dynamixel_t *device, uart_half_duplex_t *stream, dynamixel_id_t id) +{ + device->stream = stream; + device->id = id; +} + +int dynamixel_ping(uart_half_duplex_t *stream, dynamixel_id_t id) +{ + dynamixel_writer_t pw; + + uart_half_duplex_set_tx(stream); + dynamixel_writer_init(&pw, stream->buffer, stream->size); + dynamixel_writer_ping_make(&pw, id); + uart_half_duplex_send(stream, pw.size); + + uart_half_duplex_set_rx(stream); + if (uart_half_duplex_recv(stream, DXL_STATUS_SIZE(3)) != DXL_STATUS_SIZE(3)) { + return DYNAMIXEL_TIMEOUT; + } + + return DYNAMIXEL_OK; +} + +int dynamixel_write(dynamixel_t *device, dynamixel_addr_t reg, const uint8_t *data, size_t length) +{ + uart_half_duplex_set_tx(device->stream); + if (device->stream->size < length) { + return DYNAMIXEL_BUFFER_TOO_SMALL; + } + + dynamixel_writer_t pw; + + dynamixel_writer_init(&pw, device->stream->buffer, device->stream->size); + dynamixel_writer_write_make(&pw, device->id, reg, data, length); + uart_half_duplex_send(device->stream, pw.size); + + uart_half_duplex_set_rx(device->stream); + if (uart_half_duplex_recv(device->stream, DXL_STATUS_SIZE(0)) != DXL_STATUS_SIZE(0)) { + return DYNAMIXEL_TIMEOUT; + } + + return DYNAMIXEL_OK; +} + +int dynamixel_write8(dynamixel_t *device, dynamixel_addr_t reg, uint8_t value) +{ + return dynamixel_write(device, reg, &value, 1); +} + +int dynamixel_write16(dynamixel_t *device, dynamixel_addr_t reg, uint16_t value) +{ + return dynamixel_write(device, reg, (uint8_t*)&value, 2); +} + +int dynamixel_read(dynamixel_t *device, dynamixel_addr_t reg, uint8_t *data, size_t length) +{ + uart_half_duplex_set_tx(device->stream); + if (device->stream->size < length) { + return DYNAMIXEL_BUFFER_TOO_SMALL; + } + + dynamixel_writer_t pw; + + dynamixel_writer_init(&pw, device->stream->buffer, device->stream->size); + dynamixel_writer_read_make(&pw, device->id, reg, length); + uart_half_duplex_send(device->stream, pw.size); + + uart_half_duplex_set_rx(device->stream); + const size_t esize = DXL_STATUS_SIZE(length); + if (uart_half_duplex_recv(device->stream, esize) != esize) { + return DYNAMIXEL_TIMEOUT; + } + + dynamixel_reader_t pr; + dynamixel_reader_init(&pr, device->stream->buffer, esize); + if (!dynamixel_reader_is_valid(&pr)) { + return DYNAMIXEL_INVALID_MESSAGE; + } + + if (dynamixel_reader_status_get_payload_size(&pr) != length) { + return DYNAMIXEL_INVALID_MESSAGE; + } + + memcpy(data, dynamixel_reader_status_get_payload(&pr), length); + return DYNAMIXEL_OK; +} + +int dynamixel_read8(dynamixel_t *device, dynamixel_addr_t reg, uint8_t *value) +{ + return dynamixel_read(device, reg, value, 1); +} + +int dynamixel_read16(dynamixel_t *device, dynamixel_addr_t reg, uint16_t *value) +{ + return dynamixel_read(device, reg, (uint8_t*)value, 2); +} diff --git a/drivers/dynamixel/include/dynamixel_crc.h b/drivers/dynamixel/include/dynamixel_crc.h new file mode 100644 index 000000000000..603d0e165497 --- /dev/null +++ b/drivers/dynamixel/include/dynamixel_crc.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_dynamixel + * + * @{ + * + * @file + * @brief Interface definition for Dynamixel crc + * + * @author Loïc Dauphin + */ + +#ifndef DYNAMIXEL_CRC_H +#define DYNAMIXEL_CRC_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +uint16_t dynamixel_crc_update(uint16_t crc_accum, const uint8_t *buffer, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/dynamixel/include/dynamixel_protocol.h b/drivers/dynamixel/include/dynamixel_protocol.h new file mode 100644 index 000000000000..221032d3a056 --- /dev/null +++ b/drivers/dynamixel/include/dynamixel_protocol.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_dynamixel + * + * @{ + * + * @file + * @brief Dynamixel protocol definitions + * + * @author Loïc Dauphin + */ + +#ifndef DYNAMIXEL_PROTOCOL_H +#define DYNAMIXEL_PROTOCOL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DXL_HEADER ((uint8_t[]){0xFF,0xFF,0xFD}) + +typedef enum { + XL320_B_9600 = 0, /**< XL320 available baudrate : 9600 */ + XL320_B_57600 = 1, /**< XL320 available baudrate : 57600 */ + XL320_B_115200 = 2, /**< XL320 available baudrate : 115200 */ + XL320_B_1000000 = 3, /**< XL320 available baudrate : 1000000 */ +} xl320_baudrate_t; + +typedef enum { + XL320_VERSION = 2, /**< Information on the version of firmware [R] */ + XL320_ID = 3, /**< ID of Dynamixel [RW] (default=1 ; min=0 ; max=252) */ + XL320_BAUD_RATE = 4, /**< Baud Rate of Dynamixel [RW] (default=3 ; min=0 ; max=3) */ + XL320_RETURN_DELAY_TIME = 5, /**< Return Delay Time [RW] (default=250 ; min=0 ; max=254) */ + XL320_CONTROL_MODE = 11, /**< Control Mode [RW] (default=2 ; min=1 ; max=2) */ + XL320_LIMIT_TEMPERATURE = 12, /**< Internal Limit Temperature [RW] (default=65 ; min=0 ; max=150) */ + XL320_LOWER_LIMIT_VOLTAGE = 13, /**< Lowest Limit Voltage [RW] (default=60 ; min=50 ; max=250) */ + XL320_UPPER_LIMIT_VOLTAGE = 14, /**< Upper Limit Voltage [RW] (default=90 ; min=50 ; max=250) */ + XL320_RETURN_LEVEL = 17, /**< Return Level [RW] (default=2 ; min=0 ; max=2) */ + XL320_ALARM_SHUTDOWN = 18, /**< Shutdown for Alarm [RW] (default=3 ; min=0 ; max=7) */ + XL320_TORQUE_ENABLE = 24, /**< Torque On/Off [RW] (default=0 ; min=0 ; max=1) */ + XL320_LED = 25, /**< LED On/Off [RW] (default=0 ; min=0 ; max=7) */ + XL320_D_GAIN = 27, /**< D Gain [RW] (default=0 ; min=0 ; max=254) */ + XL320_I_GAIN = 28, /**< I Gain [RW] (default=0 ; min=0 ; max=254) */ + XL320_P_GAIN = 29, /**< P Gain [RW] (default=32 ; min=0 ; max=254) */ + XL320_PRESENT_VOLTAGE = 45, /**< Current Voltage [R] */ + XL320_PRESENT_TEMPERATURE = 46, /**< Present temperature [R] */ + XL320_REGISTERED_INST = 47, /**< Registered Instruction [R] (default=0) */ + XL320_MOVING = 49, /**< Moving [R] (default=0) */ + XL320_ERROR = 50, /**< Hardware error status [R] (default=0) */ +} xl320_register8_t; + +typedef enum { + XL320_MODEL_NUMBER = 0, /**< Model number [R] (default=350) */ + XL320_CW_ANGLE_LIMIT = 6, /**< clockwise Angle Limit [RW] (default=0 ; min=0 ; max=1023) */ + XL320_CCW_ANGLE_LIMIT = 8, /**< counterclockwise Angle Limit [RW] (default=1023 ; min=0 ; max=1023) */ + XL320_MAX_TORQUE = 15, /**< Lowest byte of Max. Torque [RW] (default=1023 ; min=0 ; max=1023) */ + XL320_GOAL_POSITION = 30, /**< Goal Position [RW] (min=0 ; max=1023) */ + XL320_GOAL_VELOCITY = 32, /**< Goal Speed [RW] (min=0 ; max=2047) */ + XL320_GOAL_TORQUE = 35, /**< Goal Torque [RW] (min=0 ; max=1023) */ + XL320_PRESENT_POSITION = 37, /**< Current Position [R] */ + XL320_PRESENT_SPEED = 39, /**< Current Speed [R] */ + XL320_PRESENT_LOAD = 41, /**< Current Load [R] */ + XL320_PUNCH = 51, /**< Punch [RW] (default=32 ; min=0 ; max=1023) */ +} xl320_register16_t; + +typedef enum { + DXL_INST_PING = 0x01, /**< checks if ID is associated to a Device */ + DXL_INST_READ = 0x02, /**< read data from the Device */ + DXL_INST_WRITE = 0x03, /**< write data on the Device */ + DXL_INST_REG_WRITE = 0x04, /**< registers the write instruction to a standby status */ + DXL_INST_ACTION = 0x05, /**< executes the write instruction previously registered */ + DXL_INST_FACTORY_RESET = 0x06, /**< resets the Control Table to its initial factory default settings */ + DXL_INST_REBOOT = 0x08, /**< reboot the Device */ + DXL_INST_STATUS = 0x55, /**< Return Instruction for the Instruction Packet */ + DXL_INST_SYNC_READ = 0x82, /**< (Multiple devices) read data with same Address and length at once */ + DXL_INST_SYNC_WRITE = 0x83, /**< (Multiple devices) write data on the same Address and length at once */ + DXL_INST_BULK_READ = 0x92, /**< (Multiple devices) read data from different Addresses and lengths at once */ + DXL_INST_BULK_WRITE = 0x93, /**< (Multiple devices) write data on different Addresses and lengths at once */ +} dynamixel_intruction_t; + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/dynamixel/include/dynamixel_reader.h b/drivers/dynamixel/include/dynamixel_reader.h new file mode 100644 index 000000000000..7569df7157b1 --- /dev/null +++ b/drivers/dynamixel/include/dynamixel_reader.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_dynamixel + * + * @{ + * + * @file + * @brief Interface definition for Dynamixel packet reader + * + * @author Loïc Dauphin + */ + +#ifndef DYNAMIXEL_READER_H +#define DYNAMIXEL_READER_H + +#include +#include + +#include "dynamixel_protocol.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define DXL_PING_SIZE (10) +#define DXL_STATUS_SIZE(len) (11+len) +#define DXL_READ_SIZE (14) +#define DXL_WRITE_SIZE(len) (12+len) + +/** + * @brief Dynamixel packet reader struct + */ +typedef struct { + const uint8_t *buffer; /**< data buffer */ + size_t size; /**< data buffer's size */ +} dynamixel_reader_t; + +/** + * @brief Initialize the Dynamixel packet reader + * + * @param[out] reader the packet reader + * @param[in] buffer the buffer used to store data + * @param[in] size the size of the buffer + */ +static inline void dynamixel_reader_init(dynamixel_reader_t *reader, const uint8_t *buffer, size_t size) +{ + reader->buffer = buffer; + reader->size = size; +} + + +/** + * @brief Check if the packet is valid + * + * @param[in] reader the packet reader + * + * @return true if the packet is valid + * @return false otherwise + */ +bool dynamixel_reader_is_valid(const dynamixel_reader_t *reader); + +/** + * @brief Get the packet's device id + * + * @param[in] reader the packet reader + * + * @return the packet's device id + */ +static inline uint8_t dynamixel_reader_get_id(const dynamixel_reader_t *reader) +{ + return reader->buffer[4]; +} + +/** + * @brief Get the packet's instruction code + * + * @param[in] reader the packet reader + * + * @return the packet's instruction code + */ +static inline uint8_t dynamixel_reader_get_instr(const dynamixel_reader_t *reader) +{ + return reader->buffer[7]; +} + +/** + * @brief Get the packet's length field + * + * @param[in] reader the packet reader + * + * @return the packet's length field + */ +static inline uint16_t dynamixel_reader_get_length(const dynamixel_reader_t *reader) +{ + return + (((uint16_t)reader->buffer[5]) & 0xFF) | + ((((uint16_t)reader->buffer[6]) & 0xFF) << 8); +} + +/** + * @brief Get the packet's crc + * + * @param[in] reader the packet reader + * + * @return the packet's length field + */ +static inline uint16_t dynamixel_reader_get_crc(const dynamixel_reader_t *reader) +{ + return + (((uint16_t)reader->buffer[reader->size - 2]) & 0xFF) | + ((((uint16_t)reader->buffer[reader->size - 1]) & 0xFF) << 8); +} + +/** + * @brief Get the packet's payload (response) + * + * @param[in] reader the packet reader + * + * @return the addess of the begining of the payload + */ +static inline const uint8_t *dynamixel_reader_status_get_payload(const dynamixel_reader_t *reader) +{ + return &reader->buffer[9]; +} + +/** + * @brief Get the packet's payload size (response) + * + * @param[in] reader the packet reader + * + * @return the size of the payload + */ +static inline size_t dynamixel_reader_status_get_payload_size(const dynamixel_reader_t *reader) +{ + return dynamixel_reader_get_length(reader) - 4; +} + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/dynamixel/include/dynamixel_writer.h b/drivers/dynamixel/include/dynamixel_writer.h new file mode 100644 index 000000000000..7f0f33968248 --- /dev/null +++ b/drivers/dynamixel/include/dynamixel_writer.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_dynamixel + * + * @{ + * + * @file + * @brief Interface definition for Dynamixel packet writer + * + * @author Loïc Dauphin + */ + +#ifndef DYNAMIXEL_WRITER_H +#define DYNAMIXEL_WRITER_H + +#include "dynamixel_protocol.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Dynamixel packet writer struct + */ +typedef struct { + uint8_t *buffer; /**< data buffer */ + size_t size; /**< packet's size */ + size_t limit; /**< data buffer's size */ +} dynamixel_writer_t; + +/** + * @brief Initialize the Dynamixel packet writer + * + * @param[out] writer the packet writer + * @param[in] buffer the buffer used to store data + * @param[in] limit the size of the buffer (= maximum packet size) + */ +void dynamixel_writer_init(dynamixel_writer_t *writer, uint8_t *buffer, size_t limit); + +/** + * @brief Get the data buffer to send + * + * @param[out] writer the packet writer + * + * @return the begining address of the buffer + */ +const uint8_t *dynamixel_writer_get_data(const dynamixel_writer_t *writer); + +/** + * @brief Get the data buffer's size to send + * + * @param[out] writer the packet writer + * + * @return the buffer's size + */ +size_t dynamixel_writer_get_size(const dynamixel_writer_t *writer); + +/** + * @brief Build a PING packet + * + * @param[out] writer the packet writer + * @param[in] id the destination's id + */ +void dynamixel_writer_ping_make(dynamixel_writer_t *writer, uint8_t id); + +/** + * @brief Build a WRITE packet + * + * @param[out] writer the packet writer + * @param[in] id the destination's id + * @param[in] reg the register to write in + * @param[in] buffer the data buffer to write + * @param[in] size the data buffer's size + */ +void dynamixel_writer_write_make(dynamixel_writer_t *writer, uint8_t id, uint16_t reg, const uint8_t *buffer, size_t size); + +/** + * @brief Build a READ packet + * + * @param[out] writer the packet writer + * @param[in] id the destination's id + * @param[in] reg the register to read + * @param[in] size the size to read + */ +void dynamixel_writer_read_make(dynamixel_writer_t *writer, uint8_t id, uint16_t reg, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/dynamixel/reader.c b/drivers/dynamixel/reader.c new file mode 100644 index 000000000000..5c4ee8171c6b --- /dev/null +++ b/drivers/dynamixel/reader.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_dynamixel + * @{ + * + * @file + * @brief Dynamixel messages reader + * + * @author Loïc Dauphin + * + * @} + */ + +#include "dynamixel_reader.h" +#include "dynamixel_crc.h" + +static inline bool dynamixel_reader_check_minsize(const dynamixel_reader_t *reader) +{ + return 10 <= reader->size; +} + +static inline bool dynamixel_reader_check_start(const dynamixel_reader_t *reader) +{ + return + reader->buffer[0] == DXL_HEADER[0] && + reader->buffer[1] == DXL_HEADER[1] && + reader->buffer[2] == DXL_HEADER[2]; +} + +static inline bool dynamixel_reader_check_size(const dynamixel_reader_t *reader) +{ + return reader->size == (size_t)(dynamixel_reader_get_length(reader) + 7); +} + +static inline bool dynamixel_reader_check_sum(const dynamixel_reader_t *reader) +{ + return dynamixel_crc_update(0, reader->buffer, reader->size - 2) == dynamixel_reader_get_crc(reader); +} + +bool dynamixel_reader_is_valid(const dynamixel_reader_t *reader) +{ + return + dynamixel_reader_check_minsize(reader) && + dynamixel_reader_check_start(reader) && + dynamixel_reader_check_size(reader) && + dynamixel_reader_check_sum(reader); +} diff --git a/drivers/dynamixel/writer.c b/drivers/dynamixel/writer.c new file mode 100644 index 000000000000..05f21752e540 --- /dev/null +++ b/drivers/dynamixel/writer.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup drivers_dynamixel + * @{ + * + * @file + * @brief Dynamixel messages writer + * + * @author Loïc Dauphin + * + * @} + */ + +#include "dynamixel_writer.h" +#include "dynamixel_crc.h" + +#include + +#define LOW(v) (v & 0xFF) +#define HIGH(v) ((v >> 8) & 0xFF) + +void dynamixel_writer_init(dynamixel_writer_t *writer, uint8_t *buffer, size_t limit) +{ + writer->buffer = buffer; + writer->size = 0; + writer->limit = limit; +} + +const uint8_t *dynamixel_writer_get_data(const dynamixel_writer_t *writer) +{ + return (const uint8_t*)writer->buffer; +} + +size_t dynamixel_writer_get_size(const dynamixel_writer_t *writer) +{ + return writer->size; +} + +void dynamixel_writer_ping_make(dynamixel_writer_t *writer, uint8_t id) +{ + const size_t len = 3; + if (len + 7 <= writer->limit) { + writer->size = len + 7; + + writer->buffer[0] = DXL_HEADER[0]; + writer->buffer[1] = DXL_HEADER[1]; + writer->buffer[2] = DXL_HEADER[2]; + writer->buffer[3] = 0x00; /* reserved */ + writer->buffer[4] = id; + writer->buffer[5] = LOW(len); + writer->buffer[6] = HIGH(len); + writer->buffer[7] = DXL_INST_PING; + + uint16_t crc = dynamixel_crc_update(0, writer->buffer, 8); + + writer->buffer[8] = LOW(crc); + writer->buffer[9] = HIGH(crc); + } + else { + writer->size = 0; + } +} + +void dynamixel_writer_write_make(dynamixel_writer_t *writer, uint8_t id, uint16_t reg, const uint8_t *buffer, size_t size) +{ + const size_t len = 5 + size; + if (len + 7 <= writer->limit) { + writer->size = len + 7; + + writer->buffer[0] = DXL_HEADER[0]; + writer->buffer[1] = DXL_HEADER[1]; + writer->buffer[2] = DXL_HEADER[2]; + writer->buffer[3] = 0x00; /* reserved */ + writer->buffer[4] = id; + writer->buffer[5] = LOW(len); + writer->buffer[6] = HIGH(len); + writer->buffer[7] = DXL_INST_WRITE; + + writer->buffer[8] = LOW(reg); + writer->buffer[9] = HIGH(reg); + + memcpy(&writer->buffer[10], buffer, size); + + uint16_t crc = dynamixel_crc_update(0, writer->buffer, len + 5); + + writer->buffer[writer->size - 2] = LOW(crc); + writer->buffer[writer->size - 1] = HIGH(crc); + } + else { + writer->size = 0; + } +} + +void dynamixel_writer_read_make(dynamixel_writer_t *writer, uint8_t id, uint16_t reg, size_t size) +{ + const size_t len = 7; + if (len + 7 <= writer->limit) { + writer->size = len + 7; + + writer->buffer[0] = DXL_HEADER[0]; + writer->buffer[1] = DXL_HEADER[1]; + writer->buffer[2] = DXL_HEADER[2]; + writer->buffer[3] = 0x00; /* reserved */ + writer->buffer[4] = id; + writer->buffer[5] = LOW(len); + writer->buffer[6] = HIGH(len); + writer->buffer[7] = DXL_INST_READ; + + writer->buffer[8] = LOW(reg); + writer->buffer[9] = HIGH(reg); + writer->buffer[10] = LOW(size); + writer->buffer[11] = HIGH(size); + + uint16_t crc = dynamixel_crc_update(0, writer->buffer, 12); + + writer->buffer[12] = LOW(crc); + writer->buffer[13] = HIGH(crc); + } + else { + writer->size = 0; + } +} diff --git a/drivers/include/dynamixel.h b/drivers/include/dynamixel.h new file mode 100644 index 000000000000..fc8108e70b23 --- /dev/null +++ b/drivers/include/dynamixel.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2017 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @defgroup drivers_dynamixel Dynamixel driver + * @ingroup drivers_actuators + * + * This module contains drivers for any device using dynamixel's servomotors communication bus. + * The bus is mainly used for servomotors, but a device can be anything : sensors, other actuators. + * + * @{ + * + * @file + * @brief Interface definition for Dynamixel devices driver + * + * @author Loïc Dauphin + */ + +#ifndef DYNAMIXEL_H +#define DYNAMIXEL_H + +#include +#include + +#include "dynamixel_protocol.h" +#include "uart_half_duplex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uint8_t dynamixel_id_t; /**< device id type */ +typedef uint16_t dynamixel_addr_t; /**< register address type */ + +/** + * @brief Descriptor struct for a dynamixel device + */ +typedef struct { + uart_half_duplex_t *stream; /**< the stream used */ + dynamixel_id_t id; /**< the device address */ +} dynamixel_t; + +/** + * @brief Possible dynamixel return values + */ +enum { + DYNAMIXEL_OK, /**< Success */ + DYNAMIXEL_TIMEOUT, /**< No response from the device */ + DYNAMIXEL_BUFFER_TOO_SMALL, /**< Buffer is too small for the message */ + DYNAMIXEL_INVALID_MESSAGE, /**< Invalid message received */ +}; + +/** + * @brief Send a PING message to a device + * + * @param[in] stream the stream + * @param[in] id the device address + * + * @return DYNAMIXEL_OK if a device answered + * @return DYNAMIXEL_TIMEOUT if the device did not answer + * @return DYNAMIXEL_BUFFER_TOO_SMALL if buffer is too small for the message + * @return DYNAMIXEL_INVALID_MESSAGE if an invalid message was received + */ +int dynamixel_ping(uart_half_duplex_t *stream, dynamixel_id_t id); + +/** + * @brief Initialize a Dynamixel device + * + * @param[out] device the Dynamixel device + * @param[in] stream the stream + * @param[in] id the device address + */ +void dynamixel_init(dynamixel_t *device, uart_half_duplex_t *stream, dynamixel_id_t id); + +/** + * @brief Write to a device 8bits register + * + * @param[in] device the Dynamixel device + * @param[in] reg the register to write + * @param[in] value the value to write + * + * @return DYNAMIXEL_OK on success + * @return DYNAMIXEL_TIMEOUT if the device did not answer + * @return DYNAMIXEL_BUFFER_TOO_SMALL if buffer is too small for the message + * @return DYNAMIXEL_INVALID_MESSAGE if an invalid message was received + */ +int dynamixel_write8(dynamixel_t *device, dynamixel_addr_t reg, uint8_t value); + +/** + * @brief Write to a device 16bits register + * + * @param[in] device the Dynamixel device + * @param[in] reg the register to write + * @param[in] value the value to write + * + * @return DYNAMIXEL_OK on success + * @return DYNAMIXEL_TIMEOUT if the device did not answer + * @return DYNAMIXEL_BUFFER_TOO_SMALL if buffer is too small for the message + * @return DYNAMIXEL_INVALID_MESSAGE if an invalid message was received + */ +int dynamixel_write16(dynamixel_t *device, dynamixel_addr_t reg, uint16_t value); + +/** + * @brief Write to a device address + * + * @param[in] device the Dynamixel device + * @param[in] reg the address to start write + * @param[in] data the data to write + * @param[in] length the data length + * + * @return DYNAMIXEL_OK on success + * @return DYNAMIXEL_TIMEOUT if the device did not answer + * @return DYNAMIXEL_BUFFER_TOO_SMALL if buffer is too small for the message + * @return DYNAMIXEL_INVALID_MESSAGE if an invalid message was received + */ +int dynamixel_write(dynamixel_t *device, dynamixel_addr_t reg, const uint8_t *data, size_t length); + +/** + * @brief Read from a device 8bits register + * + * @param[in] device the Dynamixel device + * @param[in] reg the register to read + * @param[out] value the value to read + * + * @return DYNAMIXEL_OK on success + * @return DYNAMIXEL_TIMEOUT if the device did not answer + * @return DYNAMIXEL_BUFFER_TOO_SMALL if buffer is too small for the message + * @return DYNAMIXEL_INVALID_MESSAGE if an invalid message was received + */ +int dynamixel_read8(dynamixel_t *device, dynamixel_addr_t reg, uint8_t *value); + +/** + * @brief Read from a device 16bits register + * + * @param[in] device the Dynamixel device + * @param[in] reg the register to read + * @param[out] value the value to read + * + * @return DYNAMIXEL_OK on success + * @return DYNAMIXEL_TIMEOUT if the device did not answer + * @return DYNAMIXEL_BUFFER_TOO_SMALL if buffer is too small for the message + * @return DYNAMIXEL_INVALID_MESSAGE if an invalid message was received + */ +int dynamixel_read16(dynamixel_t *device, dynamixel_addr_t reg, uint16_t *value); + +/** + * @brief Read from a device address + * + * @param[in] device the Dynamixel device + * @param[in] reg the address to start read + * @param[out] data the data buffer to fill + * @param[in] length the data length + * + * @return DYNAMIXEL_OK on success + * @return DYNAMIXEL_TIMEOUT if the device did not answer + * @return DYNAMIXEL_BUFFER_TOO_SMALL if buffer is too small for the message + * @return DYNAMIXEL_INVALID_MESSAGE if an invalid message was received + */ +int dynamixel_read(dynamixel_t *device, dynamixel_addr_t reg, uint8_t *data, size_t length); + +#ifdef __cplusplus +} +#endif + +#endif +/** @} */ diff --git a/drivers/uart_half_duplex/include/uart_half_duplex.h b/drivers/uart_half_duplex/include/uart_half_duplex.h index 7e694d75f527..3b791d345bbe 100644 --- a/drivers/uart_half_duplex/include/uart_half_duplex.h +++ b/drivers/uart_half_duplex/include/uart_half_duplex.h @@ -39,12 +39,13 @@ extern "C" { /** * @brief half-duplex UART direction management method type */ -typedef int uart_half_duplex_dir_t; - -#define UART_HALF_DUPLEX_DIR_NONE (0) /**< Don't manage direction */ +typedef struct { + void (*init)(uart_t); /**< function initializing direction management method */ + void (*enable_tx)(uart_t); /**< function enabling TX */ + void (*disable_tx)(uart_t); /**< function disabling TX */ +} uart_half_duplex_dir_t; -#define UART_HALF_DUPLEX_DIR_PIN_SET(pin) (((pin + 1) << 1)) /**< pin set enables TX */ -#define UART_HALF_DUPLEX_DIR_PIN_CLEAR(pin) (((pin + 1) << 1) | 1) /**< pin clear enables TX */ +#define UART_HALF_DUPLEX_DIR_NONE { NULL, NULL, NULL } /**< Don't manage direction */ /** * @brief Configuration for half-duplex UART diff --git a/drivers/uart_half_duplex/uart_half_duplex.c b/drivers/uart_half_duplex/uart_half_duplex.c index 7147899b77ac..c1b1a33ae891 100644 --- a/drivers/uart_half_duplex/uart_half_duplex.c +++ b/drivers/uart_half_duplex/uart_half_duplex.c @@ -30,31 +30,17 @@ #define IS_PIN(dir) (dir >= UART_HALF_DUPLEX_DIR_PIN_SET(0)) #define GET_PIN(dir) ((dir >> 1) - 1) -static inline void _enable_tx(uart_half_duplex_dir_t dir) +static inline void _enable_tx(uart_half_duplex_t *dev) { - if (IS_PIN(dir)) { - if (IS_SET(dir)) { - gpio_set(GET_PIN(dir)); - return; - } - if (IS_CLEAR(dir)) { - gpio_clear(GET_PIN(dir)); - return; - } + if (dev->params.dir.enable_tx) { + dev->params.dir.enable_tx(dev->params.uart); } } -static inline void _disable_tx(uart_half_duplex_dir_t dir) +static inline void _disable_tx(uart_half_duplex_t *dev) { - if (IS_PIN(dir)) { - if (IS_SET(dir)) { - gpio_clear(GET_PIN(dir)); - return; - } - if (IS_CLEAR(dir)) { - gpio_set(GET_PIN(dir)); - return; - } + if (dev->params.dir.disable_tx) { + dev->params.dir.disable_tx(dev->params.uart); } } @@ -77,13 +63,13 @@ int uart_half_duplex_init(uart_half_duplex_t *dev, uint8_t *buffer, size_t buffe dev->params = *params; dev->timeout_us = UART_HALF_DUPLEX_DEFAULT_TIMEOUT_US; - if (IS_PIN(dev->params.dir)) { - gpio_init(GET_PIN(dev->params.dir), GPIO_OUT); + if (dev->params.dir.init) { + dev->params.dir.init(dev->params.uart); } int ret = uart_init(dev->params.uart, dev->params.baudrate, _rx_cb, dev); - _disable_tx(dev->params.dir); + _disable_tx(dev); uart_half_duplex_set_rx(dev); return ret; @@ -91,9 +77,9 @@ int uart_half_duplex_init(uart_half_duplex_t *dev, uint8_t *buffer, size_t buffe size_t uart_half_duplex_send(uart_half_duplex_t *dev, size_t size) { - _enable_tx(dev->params.dir); + _enable_tx(dev); uart_write(dev->params.uart, dev->buffer, size); - _disable_tx(dev->params.dir); + _disable_tx(dev); return size; } diff --git a/pkg/spiffs/Makefile b/pkg/spiffs/Makefile new file mode 100644 index 000000000000..d40ea98d327b --- /dev/null +++ b/pkg/spiffs/Makefile @@ -0,0 +1,19 @@ +PKG_NAME=spiffs +PKG_URL=https://github.com/pellepl/spiffs.git +PKG_VERSION=39937743fbbec4b82308ee08332bf9180408d23b +PKG_BUILDDIR ?= $(PKGDIRBASE)/$(PKG_NAME) + +CFLAGS += -std=c11 + +.PHONY: all + +all: git-download + @mkdir -p "$(PKG_BUILDDIR)/riotbuild" + @cp $(PKG_BUILDDIR)/src/*.c $(PKG_BUILDDIR)/src/*.h $(PKG_BUILDDIR)/riotbuild + + @echo 'MODULE:=spiffs' > $(PKG_BUILDDIR)/riotbuild/Makefile + @echo 'include $$(RIOTBASE)/Makefile.base' >> $(PKG_BUILDDIR)/riotbuild/Makefile + + "$(MAKE)" -C $(PKG_BUILDDIR)/riotbuild + +include $(RIOTBASE)/pkg/pkg.mk diff --git a/pkg/spiffs/Makefile.include b/pkg/spiffs/Makefile.include new file mode 100644 index 000000000000..975c3ddc2f9b --- /dev/null +++ b/pkg/spiffs/Makefile.include @@ -0,0 +1,6 @@ +INCLUDES += -I$(RIOTPKG)/spiffs/include +INCLUDES += -I$(PKGDIRBASE)/spiffs/riotbuild/ + +ifneq (,$(filter spiffs_fs,$(USEMODULE))) + DIRS += $(RIOTBASE)/pkg/spiffs/fs +endif diff --git a/pkg/spiffs/fs/Makefile b/pkg/spiffs/fs/Makefile new file mode 100644 index 000000000000..6557bca99e73 --- /dev/null +++ b/pkg/spiffs/fs/Makefile @@ -0,0 +1,3 @@ +MODULE := spiffs_fs + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/spiffs/fs/spiffs_fs.c b/pkg/spiffs/fs/spiffs_fs.c new file mode 100644 index 000000000000..220edd47b5d0 --- /dev/null +++ b/pkg/spiffs/fs/spiffs_fs.c @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup fs + * @{ + * + * @file + * @brief SPIFFS integration with vfs + * + * @author Vincent Dupont + * + * @} + */ + + +#include +#include +#include + +#include "fs/spiffs_fs.h" + +#include "kernel_defines.h" + +#define ENABLE_DEBUG (0) +#include + +static int spiffs_err_to_errno(s32_t err); + +#if SPIFFS_HAL_CALLBACK_EXTRA == 1 +static int32_t _dev_read(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst) +{ + mtd_dev_t *dev = (mtd_dev_t *)fs->user_data; + + DEBUG("spiffs: read: from addr 0x%" PRIx32 " size 0x%" PRIx32 "\n", addr, size); + + if (mtd_read(dev, dst, addr, size) > 0) { + return 0; + } + else { + return -EIO; + } +} + +static int32_t _dev_write(struct spiffs_t *fs, u32_t addr, u32_t size, const u8_t *src) +{ + mtd_dev_t *dev = (mtd_dev_t *)fs->user_data; + + DEBUG("spiffs: write: from addr 0x%" PRIx32 " size 0x%" PRIx32 "\n", addr, size); + + if (mtd_write(dev, src, addr, size) > 0) { + return 0; + } + else { + return -EIO; + } +} + +static int32_t _dev_erase(struct spiffs_t *fs, u32_t addr, u32_t size) +{ + mtd_dev_t *dev = (mtd_dev_t *)fs->user_data; + + DEBUG("spiffs: erase: from addr 0x%" PRIx32" size 0x%" PRIx32 "\n", addr, size); + + return mtd_erase(dev, addr, size); +} +#else +#ifndef SPIFFS_MTD_DEV +#error "SPIFFS needs SPIFFS_HAL_CALLBACK_EXTRA or SPIFFS_MTD_DEV" +#endif +static int32_t _dev_read(u32_t addr, u32_t size, u8_t *dst) +{ + DEBUG("spiffs: read: from addr 0x%" PRIx32 " size 0x%" PRIx32 "\n", addr, size); + + if (mtd_read(SPIFFS_MTD_DEV, dst, addr, size) > 0) { + return 0; + } + else { + return -EIO; + } +} + +static int32_t _dev_write(u32_t addr, u32_t size, const u8_t *src) +{ + DEBUG("spiffs: write: from addr 0x%" PRIx32 " size 0x%" PRIx32 "\n", addr, size); + + if (mtd_write(SPIFFS_MTD_DEV, src, addr, size) > 0) { + return 0; + } + else { + return -EIO; + } +} + +static int32_t _dev_erase(u32_t addr, u32_t size) +{ + DEBUG("spiffs: erase: from addr 0x%" PRIx32" size 0x%" PRIx32 "\n", addr, size); + + return mtd_erase(SPIFFS_MTD_DEV, addr, size); +} +#endif + +void spiffs_lock(struct spiffs_t *fs) +{ + spiffs_desc_t *fs_desc = container_of(fs, spiffs_desc_t, fs); + + DEBUG("spiffs: lock: fs_desc %p\n", (void*)fs_desc); + mutex_lock(&fs_desc->lock); +} +void spiffs_unlock(struct spiffs_t *fs) +{ + spiffs_desc_t *fs_desc = container_of(fs, spiffs_desc_t, fs); + + DEBUG("spiffs: unlock: fs_desc %p\n", (void*)fs_desc); + mutex_unlock(&fs_desc->lock); +} + +static int _mount(vfs_mount_t *mountp) +{ + spiffs_desc_t *fs_desc = mountp->private_data; +#if SPIFFS_HAL_CALLBACK_EXTRA == 1 + mtd_dev_t *dev = fs_desc->dev; + fs_desc->fs.user_data = dev; +#else + mtd_dev_t *dev = SPIFFS_MTD_DEV; +#endif + + DEBUG("spiffs: mount: private_data = %p\n", mountp->private_data); + + fs_desc->config.hal_read_f = _dev_read; + fs_desc->config.hal_write_f = _dev_write; + fs_desc->config.hal_erase_f = _dev_erase; + +#if SPIFFS_SINGLETON == 0 + DEBUG("spiffs: mount: mtd page_size=%" PRIu32 ", pages_per_sector=%" PRIu32 + ", sector_count=%" PRIu32 "\n", dev->page_size, dev->pages_per_sector, dev->sector_count); + fs_desc->config.phys_size = dev->page_size * dev->pages_per_sector * dev->sector_count; + fs_desc->config.log_block_size = dev->page_size * dev->pages_per_sector; + fs_desc->config.log_page_size = dev->page_size; + fs_desc->config.phys_addr = 0; + fs_desc->config.phys_erase_block = dev->page_size * dev->pages_per_sector; +#endif + + mtd_init(dev); + + s32_t ret = SPIFFS_mount(&fs_desc->fs, + &fs_desc->config, + fs_desc->work, + fs_desc->fd_space, + SPIFFS_FS_FD_SPACE_SIZE, +#if SPIFFS_CACHE == 1 + fs_desc->cache, + SPIFFS_FS_CACHE_SIZE, +#else + NULL, + 0, +#endif + NULL); + + if (ret != 0) { + DEBUG("spiffs: mount: ret %" PRId32 "\n", ret); + switch (ret) { + case SPIFFS_ERR_NOT_A_FS: + DEBUG("spiffs: mount: formatting fs\n"); + ret = SPIFFS_format(&fs_desc->fs); + DEBUG("spiffs: mount: format ret %" PRId32 "\n", ret); + if (ret < 0) { + return spiffs_err_to_errno(ret); + } + ret = SPIFFS_mount(&fs_desc->fs, + &fs_desc->config, + fs_desc->work, + fs_desc->fd_space, + SPIFFS_FS_FD_SPACE_SIZE, +#if SPIFFS_CACHE == 1 + fs_desc->cache, + SPIFFS_FS_CACHE_SIZE, +#else + NULL, + 0, +#endif + NULL); + DEBUG("spiffs: mount: ret %" PRId32 "\n", ret); + break; + } + } + + return spiffs_err_to_errno(ret); +} + +static int _umount(vfs_mount_t *mountp) +{ + spiffs_desc_t *fs_desc = mountp->private_data; + + SPIFFS_unmount(&fs_desc->fs); + + return 0; +} + +static int _unlink(vfs_mount_t *mountp, const char *name) +{ + spiffs_desc_t *fs_desc = mountp->private_data; + + return spiffs_err_to_errno(SPIFFS_remove(&fs_desc->fs, name)); +} + +static int _rename(vfs_mount_t *mountp, const char *from_path, const char *to_path) +{ + spiffs_desc_t *fs_desc = mountp->private_data; + + return spiffs_err_to_errno(SPIFFS_rename(&fs_desc->fs, from_path, to_path)); +} + +static int _open(vfs_file_t *filp, const char *name, int flags, mode_t mode, const char *abs_path) +{ + spiffs_desc_t *fs_desc = filp->mp->private_data; + (void) abs_path; + DEBUG("spiffs: open: private_data = %p\n", filp->mp->private_data); + + spiffs_flags s_flags = 0; + if ((flags & O_ACCMODE) == O_RDONLY) { + s_flags |= SPIFFS_O_RDONLY; + } + if ((flags & O_APPEND) == O_APPEND) { + s_flags |= SPIFFS_O_APPEND; + } + if ((flags & O_TRUNC) == O_TRUNC) { + s_flags |= SPIFFS_O_TRUNC; + } + if ((flags & O_CREAT) == O_CREAT) { + s_flags |= SPIFFS_O_CREAT; + } + if ((flags & O_ACCMODE) == O_WRONLY) { + s_flags |= SPIFFS_O_WRONLY; + } + if ((flags & O_ACCMODE) == O_RDWR) { + s_flags |= SPIFFS_O_RDWR; + } +#ifdef __O_DIRECT + if ((flags & __O_DIRECT) == __O_DIRECT) { + s_flags |= SPIFFS_O_DIRECT; + } +#endif + if ((flags & O_EXCL) == O_EXCL) { + s_flags |= SPIFFS_O_EXCL; + } + + DEBUG("spiffs: open: %s (abs_path: %s), flags: 0x%x, mode: %d\n", name, abs_path, (int) s_flags, (int) mode); + + s32_t ret = SPIFFS_open(&fs_desc->fs, name, s_flags, mode); + if (ret >= 0) { + filp->private_data.value = ret; + return ret; + } + else { + return spiffs_err_to_errno(ret); + } +} + +static int _close(vfs_file_t *filp) +{ + spiffs_desc_t *fs_desc = filp->mp->private_data; + + return spiffs_err_to_errno(SPIFFS_close(&fs_desc->fs, filp->private_data.value)); +} + +static ssize_t _write(vfs_file_t *filp, const void *src, size_t nbytes) +{ + spiffs_desc_t *fs_desc = filp->mp->private_data; + + return spiffs_err_to_errno(SPIFFS_write(&fs_desc->fs, filp->private_data.value, src, nbytes)); +} + +static ssize_t _read(vfs_file_t *filp, void *dest, size_t nbytes) +{ + spiffs_desc_t *fs_desc = filp->mp->private_data; + + return spiffs_err_to_errno(SPIFFS_read(&fs_desc->fs, filp->private_data.value, dest, nbytes)); +} + +static off_t _lseek(vfs_file_t *filp, off_t off, int whence) +{ + spiffs_desc_t *fs_desc = filp->mp->private_data; + + int s_whence = 0; + if (whence == SEEK_SET) { + s_whence = SPIFFS_SEEK_SET; + } + else if (whence == SEEK_CUR) { + s_whence = SPIFFS_SEEK_CUR; + } + else if (whence == SEEK_END) { + s_whence = SPIFFS_SEEK_END; + } + + return spiffs_err_to_errno(SPIFFS_lseek(&fs_desc->fs, filp->private_data.value, off, s_whence)); +} + +static int _fstat(vfs_file_t *filp, struct stat *buf) +{ + spiffs_desc_t *fs_desc = filp->mp->private_data; + spiffs_stat stat; + s32_t ret; + + memset(buf, 0, sizeof(*buf)); + + ret = SPIFFS_fstat(&fs_desc->fs, filp->private_data.value, &stat); + + if (ret < 0) { + return ret; + } + /* stat.name; */ + buf->st_ino = stat.obj_id; + /* stat.pix; */ + buf->st_size = stat.size; + /* stat.type;*/ + buf->st_mode = S_IFREG; + + return spiffs_err_to_errno(ret); +} + +static int _opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path) +{ + spiffs_desc_t *fs_desc = dirp->mp->private_data; + spiffs_DIR *d = (spiffs_DIR *)&dirp->private_data.buffer[0]; + (void) abs_path; + + spiffs_DIR *res = SPIFFS_opendir(&fs_desc->fs, dirname, d); + if (res == NULL) { + return -ENOENT; + } + + return 0; +} + +static int _readdir(vfs_DIR *dirp, vfs_dirent_t *entry) +{ + spiffs_DIR *d = (spiffs_DIR *)&dirp->private_data.buffer[0]; + struct spiffs_dirent e; + struct spiffs_dirent *ret; + + ret = SPIFFS_readdir(d, &e); + if (ret == NULL) { + s32_t err = SPIFFS_errno(d->fs); + if (err != SPIFFS_OK && err > SPIFFS_ERR_INTERNAL) { + DEBUG("spiffs: readdir: err=%" PRId32 "\n", err); + return -EIO; + } + } + + if (ret) { + entry->d_ino = e.obj_id; + strncpy(entry->d_name, (char*) e.name, VFS_NAME_MAX); + return 1; + } + else { + return 0; + } +} + +static int _closedir(vfs_DIR *dirp) +{ + spiffs_DIR *d = (spiffs_DIR *)&dirp->private_data.buffer[0]; + + return spiffs_err_to_errno(SPIFFS_closedir(d)); +} + +static int spiffs_err_to_errno (s32_t err) +{ + if (err >= 0) { + return (int) err; + } + + DEBUG("spiffs: error=%" PRId32 "\n", err); + + switch (err) { + case SPIFFS_OK: + return 0; + case SPIFFS_ERR_NOT_MOUNTED: + return -EINVAL; + case SPIFFS_ERR_FULL: + return -ENOSPC; + case SPIFFS_ERR_NOT_FOUND: + return -ENOENT; + case SPIFFS_ERR_END_OF_OBJECT: + return 0; + case SPIFFS_ERR_DELETED: + return -ENOENT; + case SPIFFS_ERR_MOUNTED: + return -EBUSY; + case SPIFFS_ERR_ERASE_FAIL: + return -EIO; + case SPIFFS_ERR_MAGIC_NOT_POSSIBLE: + return -ENOSPC; + case SPIFFS_ERR_NO_DELETED_BLOCKS: + return 0; + case SPIFFS_ERR_FILE_EXISTS: + return -EEXIST; + case SPIFFS_ERR_NOT_A_FILE: + return -ENOENT; + case SPIFFS_ERR_RO_NOT_IMPL: + return -EROFS; + case SPIFFS_ERR_RO_ABORTED_OPERATION: + return -SPIFFS_ERR_RO_ABORTED_OPERATION; + case SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS: + return -ENOSPC; + case SPIFFS_ERR_PROBE_NOT_A_FS: + return -ENODEV; + case SPIFFS_ERR_NAME_TOO_LONG: + return -ENAMETOOLONG; + case SPIFFS_ERR_NOT_FINALIZED: + return -ENODEV; + case SPIFFS_ERR_NOT_INDEX: + return -ENODEV; + case SPIFFS_ERR_OUT_OF_FILE_DESCS: + return -ENFILE; + case SPIFFS_ERR_FILE_CLOSED: + return -ENOENT; + case SPIFFS_ERR_FILE_DELETED: + return -ENOENT; + case SPIFFS_ERR_BAD_DESCRIPTOR: + return -EBADF; + case SPIFFS_ERR_IS_INDEX: + return -ENOENT; + case SPIFFS_ERR_IS_FREE: + return -ENOENT; + case SPIFFS_ERR_INDEX_SPAN_MISMATCH: + return -EIO; + case SPIFFS_ERR_DATA_SPAN_MISMATCH: + return -EIO; + case SPIFFS_ERR_INDEX_REF_FREE: + return -EIO; + case SPIFFS_ERR_INDEX_REF_LU: + return -EIO; + case SPIFFS_ERR_INDEX_REF_INVALID: + return -EIO; + case SPIFFS_ERR_INDEX_FREE: + return -EIO; + case SPIFFS_ERR_INDEX_LU: + return -EIO; + case SPIFFS_ERR_INDEX_INVALID: + return -EIO; + case SPIFFS_ERR_NOT_WRITABLE: + return -EACCES; + case SPIFFS_ERR_NOT_READABLE: + return -EACCES; + case SPIFFS_ERR_CONFLICTING_NAME: + return -EEXIST; + case SPIFFS_ERR_NOT_CONFIGURED: + return -ENODEV; + case SPIFFS_ERR_NOT_A_FS: + return -ENODEV; + } + + return (int) err; +} + +static const vfs_file_system_ops_t spiffs_fs_ops = { + .mount = _mount, + .umount = _umount, + .unlink = _unlink, + .rename = _rename, +}; + +static const vfs_file_ops_t spiffs_file_ops = { + .open = _open, + .close = _close, + .read = _read, + .write = _write, + .lseek = _lseek, + .fstat = _fstat, +}; + +static const vfs_dir_ops_t spiffs_dir_ops = { + .opendir = _opendir, + .readdir = _readdir, + .closedir = _closedir, +}; + +const vfs_file_system_t spiffs_file_system = { + .fs_op = &spiffs_fs_ops, + .f_op = &spiffs_file_ops, + .d_op = &spiffs_dir_ops, +}; diff --git a/pkg/spiffs/include/spiffs_config.h b/pkg/spiffs/include/spiffs_config.h new file mode 100644 index 000000000000..534d0fb48786 --- /dev/null +++ b/pkg/spiffs/include/spiffs_config.h @@ -0,0 +1,301 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Peter Andersson (pelleplutt1976gmail.com) + * + * 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. + */ + +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "board.h" + +// ----------- 8< ------------ +// Following includes are for the linux test build of spiffs +// These may/should/must be removed/altered/replaced in your target +#include +#include +#include +#include +#include +#include + +// ----------- >8 ------------ + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) //DEBUG(__VA_ARGS__) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) //DEBUG(__VA_ARGS__) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) //DEBUG(__VA_ARGS__) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) //DEBUG(__VA_ARGS__) +#endif + +// Enable/disable API functions to determine exact number of bytes +// for filedescriptor and cache buffers. Once decided for a configuration, +// this can be disabled to reduce flash. +#ifndef SPIFFS_BUFFER_HELP +#define SPIFFS_BUFFER_HELP 0 +#endif + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifndef SPIFFS_CACHE +#define SPIFFS_CACHE 1 +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifndef SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR 1 +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifndef SPIFFS_CACHE_STATS +#define SPIFFS_CACHE_STATS 1 +#endif +#endif + +// Always check header of each accessed page to ensure consistent state. +// If enabled it will increase number of reads, will increase flash. +#ifndef SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK 1 +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#ifndef SPIFFS_GC_MAX_RUNS +#define SPIFFS_GC_MAX_RUNS 5 +#endif + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifndef SPIFFS_GC_STATS +#define SPIFFS_GC_STATS 1 +#endif + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// To have a fair wear-leveling, the erase age is also included in score, +// whose factor normally is the most positive. +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// Garbage collecting heuristics - weight used for deleted pages. +#ifndef SPIFFS_GC_HEUR_W_DELET +#define SPIFFS_GC_HEUR_W_DELET (5) +#endif +// Garbage collecting heuristics - weight used for used pages. +#ifndef SPIFFS_GC_HEUR_W_USED +#define SPIFFS_GC_HEUR_W_USED (-1) +#endif +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) +#endif + +// Object name maximum length. Note that this length include the +// zero-termination character, meaning maximum string of characters +// can at most be SPIFFS_OBJ_NAME_LEN - 1. +#ifndef SPIFFS_OBJ_NAME_LEN +#define SPIFFS_OBJ_NAME_LEN (32) +#endif + +// Size of buffer allocated on stack used when copying data. +// Lower value generates more read/writes. No meaning having it bigger +// than logical page size. +#ifndef SPIFFS_COPY_BUFFER_STACK +#define SPIFFS_COPY_BUFFER_STACK (64) +#endif + +// Enable this to have an identifiable spiffs filesystem. This will look for +// a magic in all sectors to determine if this is a valid spiffs system or +// not on mount point. If not, SPIFFS_format must be called prior to mounting +// again. +#ifndef SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC (1) +#endif + +#if SPIFFS_USE_MAGIC +// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is +// enabled, the magic will also be dependent on the length of the filesystem. +// For example, a filesystem configured and formatted for 4 megabytes will not +// be accepted for mounting with a configuration defining the filesystem as 2 +// megabytes. +#ifndef SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH (0) +#endif +#endif + +// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level +// These should be defined on a multithreaded system + +struct spiffs_t; +void spiffs_lock(struct spiffs_t *fs); +void spiffs_unlock(struct spiffs_t *fs); + +// define this to enter a mutex if you're running on a multithreaded system +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) spiffs_lock(fs) +#endif +// define this to exit a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) spiffs_unlock(fs) +#endif + +// Enable if only one spiffs instance with constant configuration will exist +// on the target. This will reduce calculations, flash and memory accesses. +// Parts of configuration must be defined below instead of at time of mount. +#ifndef SPIFFS_SINGLETON +#define SPIFFS_SINGLETON 0 +#endif + +#if SPIFFS_SINGLETON +// Instead of giving parameters in config struct, singleton build must +// give parameters in defines below. +#ifndef SPIFFS_CFG_PHYS_SZ +#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*1024*2) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (65536) +#endif +#ifndef SPIFFS_CFG_PHYS_ADDR +#define SPIFFS_CFG_PHYS_ADDR(ignore) (0) +#endif +#ifndef SPIFFS_CFG_LOG_PAGE_SZ +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#endif +#ifndef SPIFFS_CFG_LOG_BLOCK_SZ +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536) +#endif +#endif + +// Enable this if your target needs aligned data for index tables +#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 0 +#endif + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 1 +#endif + +// Enable this if you want to add an integer offset to all file handles +// (spiffs_file). This is useful if running multiple instances of spiffs on +// same target, in order to recognise to what spiffs instance a file handle +// belongs. +// NB: This adds config field fh_ix_offset in the configuration struct when +// mounting, which must be defined. +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 0 +#endif + +// Enable this to compile a read only version of spiffs. +// This will reduce binary size of spiffs. All code comprising modification +// of the file system will not be compiled. Some config will be ignored. +// HAL functions for erasing and writing to spi-flash may be null. Cache +// can be disabled for even further binary size reduction (and ram savings). +// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. +// If the file system cannot be mounted due to aborted erase operation and +// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be +// returned. +// Might be useful for e.g. bootloaders and such. +#ifndef SPIFFS_READ_ONLY +#define SPIFFS_READ_ONLY 0 +#endif + +// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function +// in the api. This function will visualize all filesystem using given printf +// function. +#ifndef SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 0 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) DEBUG(__VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#ifndef SPIFFS_TEST_VIS_FREE_STR +#define SPIFFS_TEST_VIS_FREE_STR "_" +#endif +// spiffs_printf argument for a deleted page +#ifndef SPIFFS_TEST_VIS_DELE_STR +#define SPIFFS_TEST_VIS_DELE_STR "/" +#endif +// spiffs_printf argument for an index page for given object id +#ifndef SPIFFS_TEST_VIS_INDX_STR +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +#endif +// spiffs_printf argument for a data page for given object id +#ifndef SPIFFS_TEST_VIS_DATA_STR +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif +#endif + +// Types depending on configuration such as the amount of flash bytes +// given to spiffs file system in total (spiffs_file_system_size), +// the logical block size (log_block_size), and the logical page size +// (log_page_size) + +// Block index type. Make sure the size of this type can hold +// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size +typedef uint16_t spiffs_block_ix; +// Page index type. Make sure the size of this type can hold +// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size +typedef uint16_t spiffs_page_ix; +// Object id type - most significant bit is reserved for index flag. Make sure the +// size of this type can hold the highest object id on a full system, +// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 +typedef uint16_t spiffs_obj_id; +// Object span index type. Make sure the size of this type can +// hold the largest possible span index on the system - +// i.e. (spiffs_file_system_size / log_page_size) - 1 +typedef uint16_t spiffs_span_ix; + +typedef uint8_t u8_t; +typedef uint32_t u32_t; +typedef int32_t s32_t; +typedef uint16_t u16_t; +typedef int16_t s16_t; + +#ifdef __cplusplus +} +#endif + +#endif /* SPIFFS_CONFIG_H_ */ diff --git a/pkg/spiffs/patches/0001-Use-const-pointer-in-write-functions.patch b/pkg/spiffs/patches/0001-Use-const-pointer-in-write-functions.patch new file mode 100644 index 000000000000..565faf9d6ffd --- /dev/null +++ b/pkg/spiffs/patches/0001-Use-const-pointer-in-write-functions.patch @@ -0,0 +1,48 @@ +From 95c4806835cf9b049c5af1a42fd49ed7de82947b Mon Sep 17 00:00:00 2001 +From: Vincent Dupont +Date: Mon, 11 Jul 2016 19:57:31 +0200 +Subject: [PATCH 1/2] Use const pointer in write functions + +--- + src/spiffs.h | 2 +- + src/spiffs_hydrogen.c | 4 ++-- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/spiffs.h b/src/spiffs.h +index bea90b3..dfe967d 100644 +--- a/src/spiffs.h ++++ b/src/spiffs.h +@@ -459,7 +459,7 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); + * @param len how much to write + * @returns number of bytes written, or -1 if error + */ +-s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); ++s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, const void *buf, s32_t len); + + /** + * Moves the read/write file offset. Resulting offset is returned or negative if error. +diff --git a/src/spiffs_hydrogen.c b/src/spiffs_hydrogen.c +index 1cf64ff..8eb3919 100644 +--- a/src/spiffs_hydrogen.c ++++ b/src/spiffs_hydrogen.c +@@ -411,7 +411,7 @@ s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + } + + #if !SPIFFS_READ_ONLY +-static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { ++static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, const void *buf, u32_t offset, s32_t len) { + (void)fs; + s32_t res = SPIFFS_OK; + s32_t remaining = len; +@@ -434,7 +434,7 @@ static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offs + } + #endif // !SPIFFS_READ_ONLY + +-s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { ++s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, const void *buf, s32_t len) { + #if SPIFFS_READ_ONLY + (void)fs; (void)fh; (void)buf; (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; +-- +2.9.3 + diff --git a/pkg/spiffs/patches/0002-Use-const-pointer-through-all-write-functions.patch b/pkg/spiffs/patches/0002-Use-const-pointer-through-all-write-functions.patch new file mode 100644 index 000000000000..d5bc913e5e5d --- /dev/null +++ b/pkg/spiffs/patches/0002-Use-const-pointer-through-all-write-functions.patch @@ -0,0 +1,236 @@ +From 3c753304a7366514c86abfba436103b2f37edf15 Mon Sep 17 00:00:00 2001 +From: Vincent Dupont +Date: Thu, 8 Sep 2016 14:38:18 +0200 +Subject: [PATCH 2/2] Use const pointer through all write functions + +HAL API has been updated to use const pointer when writing +--- + src/spiffs.h | 4 ++-- + src/spiffs_cache.c | 2 +- + src/spiffs_hydrogen.c | 18 ++++++++++-------- + src/spiffs_nucleus.c | 12 ++++++------ + src/spiffs_nucleus.h | 8 ++++---- + 5 files changed, 23 insertions(+), 21 deletions(-) + +diff --git a/src/spiffs.h b/src/spiffs.h +index dfe967d..8a42868 100644 +--- a/src/spiffs.h ++++ b/src/spiffs.h +@@ -81,7 +81,7 @@ struct spiffs_t; + /* spi read call function type */ + typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); + /* spi write call function type */ +-typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); ++typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, const u8_t *src); + /* spi erase call function type */ + typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); + +@@ -90,7 +90,7 @@ typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); + /* spi read call function type */ + typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); + /* spi write call function type */ +-typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); ++typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, const u8_t *src); + /* spi erase call function type */ + typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); + #endif // SPIFFS_HAL_CALLBACK_EXTRA +diff --git a/src/spiffs_cache.c b/src/spiffs_cache.c +index b508ad5..65f8cd2 100644 +--- a/src/spiffs_cache.c ++++ b/src/spiffs_cache.c +@@ -183,7 +183,7 @@ s32_t spiffs_phys_wr( + spiffs_file fh, + u32_t addr, + u32_t len, +- u8_t *src) { ++ const u8_t *src) { + (void)fh; + spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + spiffs_cache *cache = spiffs_get_cache(fs); +diff --git a/src/spiffs_hydrogen.c b/src/spiffs_hydrogen.c +index 8eb3919..b2c3d92 100644 +--- a/src/spiffs_hydrogen.c ++++ b/src/spiffs_hydrogen.c +@@ -417,7 +417,7 @@ static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, const void *buf, u32_ + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN((s32_t)(fd->size - offset), len); +- res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); ++ res = spiffs_object_modify(fd, offset, buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + u8_t *buf_8 = (u8_t *)buf; +@@ -426,7 +426,7 @@ static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, const void *buf, u32_ + offset += m_len; + } + if (remaining > 0) { +- res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); ++ res = spiffs_object_append(fd, offset, buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; +@@ -499,7 +499,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, const void *buf, s32_t len) { + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->offset, fd->cache_page->size); + spiffs_cache_fd_release(fs, fd->cache_page); +- SPIFFS_API_CHECK_RES_UNLOCK(fs, res); ++ SPIFFS_API_CHECK_RES(fs, res); + } else { + // writing within cache + alloc_cpage = 0; +@@ -530,7 +530,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, const void *buf, s32_t len) { + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); +- SPIFFS_API_CHECK_RES_UNLOCK(fs, res); ++ SPIFFS_API_CHECK_RES(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; +@@ -553,7 +553,7 @@ s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, const void *buf, s32_t len) { + #endif + + res = spiffs_hydro_write(fs, fd, buf, offset, len); +- SPIFFS_API_CHECK_RES_UNLOCK(fs, res); ++ SPIFFS_API_CHECK_RES(fs, res); + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); +@@ -571,7 +571,7 @@ s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); +- SPIFFS_API_CHECK_RES_UNLOCK(fs, res); ++ SPIFFS_API_CHECK_RES(fs, res); + + #if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +@@ -917,7 +917,7 @@ static s32_t spiffs_read_dir_v( + if (res != SPIFFS_OK) return res; + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && +- (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == ++ (objix_hdr.p_hdr.flags& (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + struct spiffs_dirent *e = (struct spiffs_dirent*)user_var_p; + e->obj_id = obj_id; +@@ -927,6 +927,7 @@ static s32_t spiffs_read_dir_v( + e->pix = pix; + return SPIFFS_OK; + } ++ + return SPIFFS_VIS_COUNTINUE; + } + +@@ -1305,10 +1306,11 @@ s32_t SPIFFS_vis(spiffs *fs) { + spiffs_printf("free_blocks: %i\n", fs->free_blocks); + spiffs_printf("page_alloc: %i\n", fs->stats_p_allocated); + spiffs_printf("page_delet: %i\n", fs->stats_p_deleted); +- SPIFFS_UNLOCK(fs); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: %i of %i\n", used, total); ++ ++ SPIFFS_UNLOCK(fs); + return res; + } + #endif +diff --git a/src/spiffs_nucleus.c b/src/spiffs_nucleus.c +index 35fe0d4..625d036 100644 +--- a/src/spiffs_nucleus.c ++++ b/src/spiffs_nucleus.c +@@ -73,7 +73,7 @@ s32_t spiffs_phys_wr( + spiffs *fs, + u32_t addr, + u32_t len, +- u8_t *src) { ++ const u8_t *src) { + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } + +@@ -142,7 +142,7 @@ s32_t spiffs_obj_lu_find_entry_visitor( + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // wrap initial +- if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { ++ if (cur_entry >= (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + cur_entry = 0; + cur_block++; + cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); +@@ -467,7 +467,7 @@ s32_t spiffs_obj_lu_find_free( + SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); + if (res == SPIFFS_OK) { + fs->free_cursor_block_ix = *block_ix; +- fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; ++ fs->free_cursor_obj_lu_entry = *lu_entry; + if (*lu_entry == 0) { + fs->free_blocks--; + } +@@ -754,7 +754,7 @@ s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, +- u8_t *data, ++ const u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, +@@ -1155,7 +1155,7 @@ s32_t spiffs_object_open_by_page( + #if !SPIFFS_READ_ONLY + // Append to object + // keep current object index (header) page in fs->work buffer +-s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { ++s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, const u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; +@@ -1402,7 +1402,7 @@ s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + #if !SPIFFS_READ_ONLY + // Modify object + // keep current object index (header) page in fs->work buffer +-s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { ++s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, const u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; +diff --git a/src/spiffs_nucleus.h b/src/spiffs_nucleus.h +index 0fa1cb3..2e6afa1 100644 +--- a/src/spiffs_nucleus.h ++++ b/src/spiffs_nucleus.h +@@ -532,7 +532,7 @@ s32_t spiffs_phys_wr( + #endif + u32_t addr, + u32_t len, +- u8_t *src); ++ const u8_t *src); + + s32_t spiffs_phys_cpy( + spiffs *fs, +@@ -610,7 +610,7 @@ s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, +- u8_t *data, ++ const u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, +@@ -684,13 +684,13 @@ s32_t spiffs_object_open_by_page( + s32_t spiffs_object_append( + spiffs_fd *fd, + u32_t offset, +- u8_t *data, ++ const u8_t *data, + u32_t len); + + s32_t spiffs_object_modify( + spiffs_fd *fd, + u32_t offset, +- u8_t *data, ++ const u8_t *data, + u32_t len); + + s32_t spiffs_object_read( +-- +2.9.3 + diff --git a/sys/include/fs/spiffs_fs.h b/sys/include/fs/spiffs_fs.h new file mode 100644 index 000000000000..5316914068c1 --- /dev/null +++ b/sys/include/fs/spiffs_fs.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup fs + * @defgroup spiffs SPIFFS integration + * @{ + * + * @file + * @brief SPIFFS integration with vfs + * + * @author Vincent Dupont + */ + +#ifndef SPIFFS_FS_H +#define SPIFFS_FS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "spiffs.h" +#include "spiffs_config.h" +#include "vfs.h" +#include "mtd.h" +#include "mutex.h" + +/** Size of the buffer needed for directory */ +#define SPIFFS_DIR_SIZE (12) + +#if (VFS_DIR_BUFFER_SIZE < SPIFFS_DIR_SIZE) +#error "VFS_DIR_BUFFER_SIZE too small" +#endif + +/** + * @name SPIFFS config constants + * @{ + */ +#ifndef SPIFFS_FS_CACHE_SIZE +#if SPIFFS_CACHE +#define SPIFFS_FS_CACHE_SIZE (512) +#else +#define SPIFFS_FS_CACHE_SIZE (0) +#endif /* SPIFFS_CACHE */ +#endif /* SPIFFS_FS_CACHE_SIZE */ +#ifndef SPIFFS_FS_WORK_SIZE +#define SPIFFS_FS_WORK_SIZE (512) +#endif +#ifndef SPIFFS_FS_FD_SPACE_SIZE +#define SPIFFS_FS_FD_SPACE_SIZE (125) +#endif +/** @} */ + +/** + * This contains everything needed to run an instance of SPIFFS + */ +typedef struct spiffs_desc { + spiffs fs; /**< The SPIFFS struct */ + uint8_t work[SPIFFS_FS_WORK_SIZE]; /**< SPIFFS work buffer */ + uint8_t fd_space[SPIFFS_FS_FD_SPACE_SIZE]; /**< SPIFFS file descriptor cache */ +#if (SPIFFS_CACHE == 1) || defined(DOXYGEN) + uint8_t cache[SPIFFS_FS_CACHE_SIZE]; /**< SPIFFS cache */ +#endif + spiffs_config config; /**< SPIFFS config, filled at mount time depending + * on the underlying mtdi_dev_t @p dev */ + mutex_t lock; /**< A lock for SPIFFS internal use */ +#if (SPIFFS_HAL_CALLBACK_EXTRA == 1) || defined(DOXYGEN) + mtd_dev_t *dev; /**< The underlying mtd device, must be set by user */ +#endif +} spiffs_desc_t; + +/** The SPIFFS vfs driver, a pointer to a spiffs_desc_t must be provided as vfs_mountp::private_data */ +extern const vfs_file_system_t spiffs_file_system; + +#ifdef __cplusplus +} +#endif + +#endif /* SPIFFS_FS_H */ + +/** @} */ diff --git a/tests/driver_dynamixel/Makefile b/tests/driver_dynamixel/Makefile new file mode 100644 index 000000000000..297a31cd85dc --- /dev/null +++ b/tests/driver_dynamixel/Makefile @@ -0,0 +1,13 @@ +APPLICATION = driver_dynamixel +include ../Makefile.tests_common + +# chronos : USART_1 undeclared +BOARD_BLACKLIST += chronos + +# mips-malta : undefined reference to uart_write +BOARD_BLACKLIST += mips-malta + +USEMODULE += dynamixel +USEMODULE += shell + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_dynamixel/main.c b/tests/driver_dynamixel/main.c new file mode 100644 index 000000000000..521430ac9917 --- /dev/null +++ b/tests/driver_dynamixel/main.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2017 Inira + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +#include "dynamixel.h" +#include "shell.h" +#include "shell_commands.h" +#include "uart_stdio.h" +#include "board.h" +#include "periph/gpio.h" + +#include +#include + +#define ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) + +typedef struct { + const char *name; + int addr; +} reg_name_addr_t; + +static const reg_name_addr_t regs8[] = { + { "VERSION", XL320_VERSION }, + { "ID", XL320_ID }, + { "BAUD_RATE", XL320_BAUD_RATE }, + { "RETURN_DELAY_TIME", XL320_RETURN_DELAY_TIME }, + { "CONTROL_MODE", XL320_CONTROL_MODE }, + { "LIMIT_TEMPERATURE", XL320_LIMIT_TEMPERATURE }, + { "LOWER_LIMIT_VOLTAGE", XL320_LOWER_LIMIT_VOLTAGE }, + { "UPPER_LIMIT_VOLTAGE", XL320_UPPER_LIMIT_VOLTAGE }, + { "RETURN_LEVEL", XL320_RETURN_LEVEL }, + { "ALARM_SHUTDOWN", XL320_ALARM_SHUTDOWN }, + { "TORQUE_ENABLE", XL320_TORQUE_ENABLE }, + { "LED", XL320_LED }, + { "D_GAIN", XL320_D_GAIN }, + { "I_GAIN", XL320_I_GAIN }, + { "P_GAIN", XL320_P_GAIN }, + { "PRESENT_VOLTAGE", XL320_PRESENT_VOLTAGE }, + { "PRESENT_TEMPERATURE", XL320_PRESENT_TEMPERATURE }, + { "REGISTERED_INST", XL320_REGISTERED_INST }, + { "MOVING", XL320_MOVING }, + { "ERROR", XL320_ERROR }, +}; + +static const reg_name_addr_t regs16[] = { + { "MODEL_NUMBER", XL320_MODEL_NUMBER }, + { "CW_ANGLE_LIMIT", XL320_CW_ANGLE_LIMIT }, + { "CCW_ANGLE_LIMIT", XL320_CCW_ANGLE_LIMIT }, + { "MAX_TORQUE", XL320_MAX_TORQUE }, + { "GOAL_POSITION", XL320_GOAL_POSITION }, + { "GOAL_VELOCITY", XL320_GOAL_VELOCITY }, + { "GOAL_TORQUE", XL320_GOAL_TORQUE }, + { "PRESENT_POSITION", XL320_PRESENT_POSITION }, + { "PRESENT_SPEED", XL320_PRESENT_SPEED }, + { "PRESENT_LOAD", XL320_PRESENT_LOAD }, + { "PUNCH", XL320_PUNCH }, +}; + +static const int32_t baudrates[] = { + 1000000L, + 115200L, + 57600L, + 9600L, +}; + +static uint8_t dynamixel_buffer[128]; +static uart_half_duplex_t stream; + +#ifdef DXL_DIR_PIN +static void dir_init(uart_t uart) { + gpio_init(DXL_DIR_PIN, GPIO_OUT); +} + +static void dir_enable_tx(uart_t uart) { + gpio_set(DXL_DIR_PIN); +} + +static void dir_disable_tx(uart_t uart) { + gpio_clear(DXL_DIR_PIN); +} +#else +#define dir_init NULL +#define dir_enable_tx NULL +#define dir_disable_tx NULL +#endif + +static int parse_uart(char *arg) +{ + unsigned uart = (unsigned)atoi(arg); + if (uart >= UART_NUMOF) { + printf("Error: Invalid UART_DEV device specified (%u).\n", uart); + return -1; + } + else if (UART_DEV(uart) == UART_STDIO_DEV) { + printf("Error: The selected UART_DEV(%u) is used for the shell!\n", uart); + return -2; + } + return uart; +} + +static int32_t parse_baud(char *arg) +{ + int32_t baud = (int32_t)atoi(arg); + + for (size_t i = 0 ; i < ARRAY_LEN(baudrates) ; i++) { + if (baud == baudrates[i]) { + return baud; + } + } + + printf("Error: Invalid baudrate (%s)\n", arg); + return -1; +} + +static int parse_dev(char *arg) +{ + int dev = (int)atoi(arg); + if (dev < 0 || 254 < dev) { + printf("Error: Invalid device id (%s)\n", arg); + return -1; + } + return dev; +} + +static void parse_reg(char *arg, int *reg8, int *reg16) +{ + *reg8 = -1; + *reg16 = -1; + + for (size_t i = 0 ; i < ARRAY_LEN(regs8) ; i++) { + if (strcmp(arg, regs8[i].name) == 0) { + *reg8 = regs8[i].addr; + return; + } + } + + for (size_t i = 0 ; i < ARRAY_LEN(regs16) ; i++) { + if (strcmp(arg, regs16[i].name) == 0) { + *reg16 = regs16[i].addr; + return; + } + } + + printf("Error: Invalid register (%s)\n", arg); +} + +void print_registers(void) { + puts("available 8bits registers :"); + for (size_t i = 0 ; i < ARRAY_LEN(regs8) ; i++) { + printf("\t%s\n", regs8[i].name); + } + + puts("available 16bits registers :"); + for (size_t i = 0 ; i < ARRAY_LEN(regs16) ; i++) { + printf("\t%s\n", regs16[i].name); + } +} + +static int cmd_init(int argc, char **argv) { + int uart = -1; + int baud = -1; + uint32_t timeout = -1; + + if (argc != 3 && argc != 4) { + printf("usage; %s []\n", argv[0]); + puts("available baudrates :"); + for (size_t i = 0 ; i < ARRAY_LEN(baudrates) ; i++) { + printf("\t%ld\n", (long int)baudrates[i]); + } + return 1; + } + /* parse parameters */ + uart = parse_uart(argv[1]); + if (uart < 0) { + return -1; + } + + baud = parse_baud(argv[2]); + if (baud < 0) { + return -1; + } + + if (argc == 4) { + timeout = (uint32_t)atol(argv[3]); + if (timeout == 0) { + printf("Error : Invalid timeout (%s)", argv[3]); + return -1; + } + } + + /* init */ + uart_half_duplex_params_t params = { + .uart = uart, + .baudrate = baud, + .dir = { dir_init, dir_enable_tx, dir_disable_tx }, + }; + + int ret = uart_half_duplex_init(&stream, dynamixel_buffer, ARRAY_LEN(dynamixel_buffer), ¶ms); + + if (argc == 4) { + stream.timeout_us = timeout; + } + + if (ret == UART_HALF_DUPLEX_NODEV) { + puts("Error: invalid UART device given"); + return -1; + } + if (ret == UART_HALF_DUPLEX_NOBAUD) { + puts("Error: given baudrate is not applicable"); + return -1; + } + if (ret == UART_HALF_DUPLEX_INTERR) { + puts("Error: internal error"); + return -1; + } + if (ret == UART_HALF_DUPLEX_NOMODE) { + puts("Error: given mode is not applicable"); + return -1; + } + if (ret == UART_HALF_DUPLEX_NOBUFF) { + puts("Error: invalid buffer given"); + return -1; + } + + printf("Successfully initialized Dynamixel TTL bus UART_DEV(%i)\n", uart); + return 0; +} + +static int cmd_ping(int argc, char **argv) { + int id = -1; + + if (argc != 2) { + printf("usage; %s \n", argv[0]); + return 1; + } + /* parse parameters */ + id = parse_dev(argv[1]); + if (id < 0) { + return -1; + } + + /* ping */ + if (dynamixel_ping(&stream, id) == DYNAMIXEL_OK) { + printf("Device %i responded\n", id); + } + else { + printf("No response from %i\n", id); + } + return 0; +} + +static int cmd_scan(int argc, char **argv) { + int min = -1; + int max = -1; + + if (argc == 3) { + min = atoi(argv[1]); + max = atoi(argv[2]); + if (min < 0) { + return -1; + } + if (max > 254) { + return -1; + } + if (max < min) { + return -1; + } + } + else if (argc == 1) { + min = 0; + max = 254; + } + else { + printf("usage; %s [ ]\n", argv[0]); + return 1; + } + + /* ping */ + puts("Scanning..."); + for (int id = min ; id < max ; id++) { + if (dynamixel_ping(&stream, id) == DYNAMIXEL_OK) { + printf("Device %i available\n", id); + } + } + puts("End"); + return 0; +} + +static int cmd_read(int argc, char **argv) { + int id = -1; + int reg8 = -1; + int reg16 = -1; + + if (argc != 3) { + printf("usage; %s \n", argv[0]); + print_registers(); + return 1; + } + /* parse parameters */ + id = parse_dev(argv[1]); + if (id < 0) { + return -1; + } + + parse_reg(argv[2], ®8, ®16); + if (reg8 < 0 && reg16 < 0) { + return -1; + } + + /* read */ + dynamixel_t dev; + dynamixel_init(&dev, &stream, id); + if (reg8 >= 0) { + uint8_t val = 0; + int ret = dynamixel_read8(&dev, reg8, &val); + if (ret != DYNAMIXEL_OK) { + printf("Error[%i] : No response from %i\n", ret, id); + return -1; + } + printf("%i\n", (int)val); + } + else { + uint16_t val = 0; + int ret = dynamixel_read16(&dev, reg16, &val); + if (ret != DYNAMIXEL_OK) { + printf("Error[%i] : No response from %i\n", ret, id); + return -1; + } + printf("%i\n", (int)val); + } + return 0; +} + +static int cmd_write(int argc, char **argv) { + int id = -1; + int reg8 = -1; + int reg16 = -1; + + if (argc != 4) { + printf("usage; %s \n", argv[0]); + print_registers(); + return 1; + } + /* parse parameters */ + id = parse_dev(argv[1]); + if (id < 0) { + return -1; + } + + parse_reg(argv[2], ®8, ®16); + if (reg8 < 0 && reg16 < 0) { + return -1; + } + + int val = atoi(argv[3]); + if (val < 0) { + return -1; + } + + /* read */ + dynamixel_t dev; + dynamixel_init(&dev, &stream, id); + if (reg8 >= 0) { + int ret = dynamixel_write8(&dev, reg8, val); + if (ret != DYNAMIXEL_OK) { + printf("Error[%i] : No response from %i\n", ret, id); + return -1; + } + printf("Written %i at address %i\n", (int)val, reg8); + } + else { + int ret = dynamixel_write16(&dev, reg16, val); + if (ret != DYNAMIXEL_OK) { + printf("Error[%i] : No response from %i\n", ret, id); + return -1; + } + printf("Written %i at address %i\n", (int)val, reg16); + } + return 0; +} + +static const shell_command_t shell_commands[] = { + { "init", "Initialize a Dynamixel TTL bus with a given baudrate", cmd_init }, + { "ping", "Ping a Dynamixel device", cmd_ping }, + { "scan", "Find all Dynamixel devices between min_id and max_id", cmd_scan }, + { "read", "Read a Dynamixel device register", cmd_read }, + { "write", "Write in a Dynamixel device register", cmd_write }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("\nManual Dynamixel device driver test"); + puts("==================================="); + puts("This application is intended for testing Dynamixel TTL bus\n"); + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +} diff --git a/tests/driver_feetech/main.c b/tests/driver_feetech/main.c index 8eb6379096a4..b2df7c94529b 100644 --- a/tests/driver_feetech/main.c +++ b/tests/driver_feetech/main.c @@ -98,7 +98,7 @@ static int32_t parse_baud(char *arg) int32_t baud = (int32_t)atoi(arg); for (size_t i = 0 ; i < ARRAY_LEN(baudrates) ; i++) { - if(baud == baudrates[i]) { + if (baud == baudrates[i]) { return baud; } } @@ -123,14 +123,14 @@ static void parse_reg(char *arg, int *reg8, int *reg16) *reg16 = -1; for (size_t i = 0 ; i < ARRAY_LEN(regs8) ; i++) { - if(strcmp(arg, regs8[i].name) == 0) { + if (strcmp(arg, regs8[i].name) == 0) { *reg8 = regs8[i].addr; return; } } for (size_t i = 0 ; i < ARRAY_LEN(regs16) ; i++) { - if(strcmp(arg, regs16[i].name) == 0) { + if (strcmp(arg, regs16[i].name) == 0) { *reg16 = regs16[i].addr; return; } diff --git a/tests/periph_hwrng/main.c b/tests/periph_hwrng/main.c index 364f63f2bff1..c81b60be5093 100644 --- a/tests/periph_hwrng/main.c +++ b/tests/periph_hwrng/main.c @@ -31,7 +31,7 @@ int main(void) uint8_t buf[LIMIT]; puts("\nHWRNG peripheral driver test\n"); - printf("This test will print from 1 to %i random bytes about every" + printf("This test will print from 1 to %i random bytes about every " "second\n\n", LIMIT); puts("Initializing the HWRNG driver.\n"); diff --git a/tests/unittests/Makefile b/tests/unittests/Makefile index 309be48dfacc..4256c1328b4d 100644 --- a/tests/unittests/Makefile +++ b/tests/unittests/Makefile @@ -2,15 +2,17 @@ APPLICATION = unittests include ../Makefile.tests_common BOARD_INSUFFICIENT_MEMORY := airfy-beacon arduino-duemilanove arduino-mega2560 \ - arduino-uno arduino-zero calliope-mini cc2650stk \ - chronos ek-lm4f120xl limifrog-v1 maple-mini microbit \ - msb-430 msb-430h nrf51dongle nrf6310 nucleo32-f031 \ - nucleo32-f042 nucleo32-f303 nucleo32-l031 nucleo-f030 \ - nucleo-f070 nucleo-f072 nucleo-f091 nucleo-f103 nucleo-f302 \ - nucleo-f334 nucleo-f410 nucleo-l053 nucleo-l073 opencm904 \ - pba-d-01-kw2x pca10000 pca10005 remote-pa remote-reva \ - remote-revb saml21-xpro samr21-xpro seeeduino_arch-pro \ - slwstk6220a sodaq-autonomo spark-core stm32f0discovery \ + arduino-uno arduino-zero calliope-mini cc2538dk \ + cc2650stk chronos ek-lm4f120xl limifrog-v1 maple-mini \ + mbed_lpc1768 microbit msb-430 msb-430h nrf51dongle \ + nrf6310 nucleo32-f031 nucleo32-f042 nucleo32-f303 \ + nucleo32-l031 nucleo-f030 nucleo-f070 nucleo-f072 \ + nucleo-f091 nucleo-f103 nucleo-f302 nucleo-f334 \ + nucleo-f410 nucleo-l053 nucleo-l073 opencm904 openmote \ + openmote-cc2538 pba-d-01-kw2x pca10000 pca10005 \ + remote-pa remote-reva remote-revb saml21-xpro \ + samr21-xpro seeeduino_arch-pro slwstk6220a \ + sodaq-autonomo spark-core stm32f0discovery \ stm32f3discovery telosb waspmote-pro weio wsn430-v1_3b \ wsn430-v1_4 yunjia-nrf51822 z1 @@ -42,7 +44,7 @@ AVR_BOARDS := arduino-mega2560 waspmote-pro arduino-uno arduino-duemilanove DISABLE_TEST_FOR_AVR := tests-relic MSP430_BOARDS := chronos msb-430 msb-430h telosb wsn430-v1_3b wsn430-v1_4 z1 -DISABLE_TEST_FOR_MSP430 := tests-relic +DISABLE_TEST_FOR_MSP430 := tests-relic tests-spiffs ifneq (, $(filter $(ARM7_BOARDS), $(BOARD))) UNIT_TESTS := $(filter-out $(DISABLE_TEST_FOR_ARM7), $(UNIT_TESTS)) @@ -57,7 +59,7 @@ UNIT_TESTS := $(filter-out $(DISABLE_TEST_FOR_AVR), $(UNIT_TESTS)) endif ifneq (, $(filter $(MSP430_BOARDS), $(BOARD))) -UNIT_TESTS := $(filter-out $(DISABLE_TEST_FOR_AVR), $(UNIT_TESTS)) +UNIT_TESTS := $(filter-out $(DISABLE_TEST_FOR_MSP430), $(UNIT_TESTS)) endif DISABLE_MODULE += auto_init diff --git a/tests/unittests/tests-spiffs/Makefile b/tests/unittests/tests-spiffs/Makefile new file mode 100644 index 000000000000..48422e909a47 --- /dev/null +++ b/tests/unittests/tests-spiffs/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-spiffs/Makefile.include b/tests/unittests/tests-spiffs/Makefile.include new file mode 100644 index 000000000000..e6acb73e9a6d --- /dev/null +++ b/tests/unittests/tests-spiffs/Makefile.include @@ -0,0 +1 @@ +USEMODULE += spiffs diff --git a/tests/unittests/tests-spiffs/tests-spiffs.c b/tests/unittests/tests-spiffs/tests-spiffs.c new file mode 100644 index 000000000000..998ee8ee34a5 --- /dev/null +++ b/tests/unittests/tests-spiffs/tests-spiffs.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + */ +#include "fs/spiffs_fs.h" +#include "vfs.h" +#include "mtd.h" +#include "board.h" + +#include +#include + +#include "embUnit/embUnit.h" + +#include "tests-spiffs.h" + +/* Define MTD_0 in board.h to use the board mtd if any */ +#ifdef MTD_0 +#define _dev (MTD_0) +#else +/* Test mock object implementing a simple RAM-based mtd */ +#ifndef SECTOR_COUNT +#define SECTOR_COUNT 8 +#endif +#ifndef PAGE_PER_SECTOR +#define PAGE_PER_SECTOR 4 +#endif +#ifndef PAGE_SIZE +#define PAGE_SIZE 128 +#endif + +static uint8_t dummy_memory[PAGE_PER_SECTOR * PAGE_SIZE * SECTOR_COUNT]; + +static int _init(mtd_dev_t *dev) +{ + (void)dev; + + memset(dummy_memory, 0xff, sizeof(dummy_memory)); + return 0; +} + +static int _read(mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size) +{ + (void)dev; + + if (addr + size > sizeof(dummy_memory)) { + return -EOVERFLOW; + } + memcpy(buff, dummy_memory + addr, size); + + return size; +} + +static int _write(mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size) +{ + (void)dev; + + if (addr + size > sizeof(dummy_memory)) { + return -EOVERFLOW; + } + if (size > PAGE_SIZE) { + return -EOVERFLOW; + } + memcpy(dummy_memory + addr, buff, size); + + return size; +} + +static int _erase(mtd_dev_t *dev, uint32_t addr, uint32_t size) +{ + (void)dev; + + if (size % (PAGE_PER_SECTOR * PAGE_SIZE) != 0) { + return -EOVERFLOW; + } + if (addr % (PAGE_PER_SECTOR * PAGE_SIZE) != 0) { + return -EOVERFLOW; + } + if (addr + size > sizeof(dummy_memory)) { + return -EOVERFLOW; + } + memset(dummy_memory + addr, 0xff, size); + + return 0; +} + +static int _power(mtd_dev_t *dev, enum mtd_power_state power) +{ + (void)dev; + (void)power; + return 0; +} + +static const mtd_desc_t driver = { + .init = _init, + .read = _read, + .write = _write, + .erase = _erase, + .power = _power, +}; + +static mtd_dev_t dev = { + .driver = &driver, + .sector_count = SECTOR_COUNT, + .pages_per_sector = PAGE_PER_SECTOR, + .page_size = PAGE_SIZE, +}; + +static mtd_dev_t *_dev = (mtd_dev_t*) &dev; +#endif /* MTD_0 */ + +static struct spiffs_desc spiffs_desc = { + .lock = MUTEX_INIT, +}; + +static vfs_mount_t _test_spiffs_mount = { + .fs = &spiffs_file_system, + .mount_point = "/test-spiffs", + .private_data = &spiffs_desc, +}; + +static void test_spiffs_setup(void) +{ +#if SPIFFS_HAL_CALLBACK_EXTRA == 1 + spiffs_desc.dev = _dev; +#endif + vfs_mount(&_test_spiffs_mount); +} + +static void test_spiffs_teardown(void) +{ + vfs_unlink("/test-spiffs/test.txt"); + vfs_unlink("/test-spiffs/test0.txt"); + vfs_unlink("/test-spiffs/test1.txt"); + vfs_unlink("/test-spiffs/a/test2.txt"); + vfs_umount(&_test_spiffs_mount); +} + +static void tests_spiffs_mount_umount(void) +{ + int res; + res = vfs_umount(&_test_spiffs_mount); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_mount(&_test_spiffs_mount); + TEST_ASSERT_EQUAL_INT(0, res); +} + +static void tests_spiffs_open_close(void) +{ + int res; + res = vfs_open("/test-spiffs/test.txt", O_CREAT | O_RDWR, 0); + TEST_ASSERT(res >= 0); + + res = vfs_close(res); + TEST_ASSERT_EQUAL_INT(0, res); +} + +static void tests_spiffs_write(void) +{ + const char buf[] = "TESTSTRING"; + char r_buf[2 * sizeof(buf)]; + + int res; + int fd = vfs_open("/test-spiffs/test.txt", O_CREAT | O_RDWR, 0); + TEST_ASSERT(fd >= 0); + + res = vfs_write(fd, buf, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(sizeof(buf), res); + + res = vfs_lseek(fd, 0, SEEK_SET); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_read(fd, r_buf, sizeof(r_buf)); + TEST_ASSERT_EQUAL_INT(sizeof(buf), res); + TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]); + + res = vfs_close(fd); + TEST_ASSERT_EQUAL_INT(0, res); + + fd = vfs_open("/test-spiffs/test.txt", O_RDONLY, 0); + TEST_ASSERT(fd >= 0); + + res = vfs_read(fd, r_buf, sizeof(r_buf)); + TEST_ASSERT_EQUAL_INT(sizeof(buf), res); + TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]); + + res = vfs_close(fd); + TEST_ASSERT_EQUAL_INT(0, res); +} + +static void tests_spiffs_unlink(void) +{ + const char buf[] = "TESTSTRING"; + + int res; + int fd = vfs_open("/test-spiffs/test.txt", O_CREAT | O_RDWR, 0); + TEST_ASSERT(fd >= 0); + + res = vfs_write(fd, buf, sizeof(buf)); + TEST_ASSERT_EQUAL_INT(sizeof(buf), res); + + res = vfs_close(fd); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_unlink("/test-spiffs/test.txt"); + TEST_ASSERT_EQUAL_INT(0, res); +} + +static void tests_spiffs_readdir(void) +{ + const char buf0[] = "TESTSTRING"; + const char buf1[] = "TESTTESTSTRING"; + const char buf2[] = "TESTSTRINGSTRING"; + + int res; + int fd0 = vfs_open("/test-spiffs/test0.txt", O_CREAT | O_RDWR, 0); + TEST_ASSERT(fd0 >= 0); + + int fd1 = vfs_open("/test-spiffs/test1.txt", O_CREAT | O_RDWR, 0); + TEST_ASSERT(fd1 >= 0); + + int fd2 = vfs_open("/test-spiffs/a/test2.txt", O_CREAT | O_RDWR, 0); + TEST_ASSERT(fd2 >= 0); + + res = vfs_write(fd0, buf0, sizeof(buf0)); + TEST_ASSERT_EQUAL_INT(sizeof(buf0), res); + + res = vfs_write(fd1, buf1, sizeof(buf1)); + TEST_ASSERT_EQUAL_INT(sizeof(buf1), res); + + res = vfs_write(fd2, buf2, sizeof(buf2)); + TEST_ASSERT_EQUAL_INT(sizeof(buf2), res); + + res = vfs_close(fd0); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_close(fd1); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_close(fd2); + TEST_ASSERT_EQUAL_INT(0, res); + + vfs_DIR dirp; + res = vfs_opendir(&dirp, "/test-spiffs"); + + vfs_dirent_t entry; + int nb_files = 0; + do { + res = vfs_readdir(&dirp, &entry); + if (res == 1 && (strcmp("/test0.txt", &(entry.d_name[0])) == 0 || + strcmp("/test1.txt", &(entry.d_name[0])) == 0 || + strcmp("/a/test2.txt", &(entry.d_name[0])) == 0)) { + nb_files++; + } + } while (res == 1); + + TEST_ASSERT_EQUAL_INT(3, nb_files); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_closedir(&dirp); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_unlink("/test-spiffs/test0.txt"); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_unlink("/test-spiffs/test1.txt"); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_unlink("/test-spiffs/a/test2.txt"); + TEST_ASSERT_EQUAL_INT(0, res); +} + +static void tests_spiffs_rename(void) +{ + const char buf[] = "TESTSTRING"; + char r_buf[2 * sizeof(buf)]; + + int res; + int fd = vfs_open("/test-spiffs/test.txt", O_CREAT | O_RDWR, 0); + TEST_ASSERT(fd >= 0); + + res = vfs_write(fd, buf, sizeof(buf)); + TEST_ASSERT(res == sizeof(buf)); + + res = vfs_lseek(fd, 0, SEEK_SET); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_read(fd, r_buf, sizeof(r_buf)); + TEST_ASSERT(res == sizeof(buf)); + TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]); + + res = vfs_close(fd); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_rename("/test-spiffs/test.txt", "/test-spiffs/test1.txt"); + TEST_ASSERT_EQUAL_INT(0, res); + + fd = vfs_open("/test-spiffs/test.txt", O_RDONLY, 0); + TEST_ASSERT(fd < 0); + + fd = vfs_open("/test-spiffs/test1.txt", O_RDONLY, 0); + TEST_ASSERT(fd >= 0); + + res = vfs_lseek(fd, 0, SEEK_SET); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_read(fd, r_buf, sizeof(r_buf)); + TEST_ASSERT(res == sizeof(buf)); + TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]); + + res = vfs_close(fd); + TEST_ASSERT_EQUAL_INT(0, res); + + res = vfs_unlink("/test-spiffs/test1.txt"); + TEST_ASSERT_EQUAL_INT(0, res); +} + +Test *tests_spiffs_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(tests_spiffs_mount_umount), + new_TestFixture(tests_spiffs_open_close), + new_TestFixture(tests_spiffs_write), + new_TestFixture(tests_spiffs_unlink), + new_TestFixture(tests_spiffs_readdir), + new_TestFixture(tests_spiffs_rename), + }; + + EMB_UNIT_TESTCALLER(spiffs_tests, test_spiffs_setup, test_spiffs_teardown, fixtures); + + return (Test *)&spiffs_tests; +} + +void tests_spiffs(void) +{ + TESTS_RUN(tests_spiffs_tests()); +} +/** @} */ diff --git a/tests/unittests/tests-spiffs/tests-spiffs.h b/tests/unittests/tests-spiffs/tests-spiffs.h new file mode 100644 index 000000000000..ae424f5cac8b --- /dev/null +++ b/tests/unittests/tests-spiffs/tests-spiffs.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 OTA keys S.A. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for SPIFFS + * + * @author Vincent Dupont + */ +#ifndef TESTS_SPIFFS_H +#define TESTS_SPIFFS_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_spiffs(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_SPIFFS_H */ +/** @} */