diff --git a/pkg/fatfs/Makefile b/pkg/fatfs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..55aec0d3b058527bb13a2128e1c080683fda0c38 --- /dev/null +++ b/pkg/fatfs/Makefile @@ -0,0 +1,13 @@ +PKG_NAME=fatfs +PKG_URL=https://github.com/MichelRottleuthner/FatFs_for_RIOT.git +PKG_VERSION=61fd6ae3815170bf7bf6121f33f1ef68c2b11599 +PKG_LICENSE=BSD-1-Clause +MODULE_MAKEFILE := $(CURDIR)/Makefile.fatfs + +.PHONY: all + +all: git-download + @cp $(MODULE_MAKEFILE) $(PKG_BUILDDIR)/Makefile + "$(MAKE)" -C $(PKG_BUILDDIR) + +include $(RIOTBASE)/pkg/pkg.mk diff --git a/pkg/fatfs/Makefile.dep b/pkg/fatfs/Makefile.dep new file mode 100644 index 0000000000000000000000000000000000000000..390a17cb59a7840c095a2ddddf62b14519dc142e --- /dev/null +++ b/pkg/fatfs/Makefile.dep @@ -0,0 +1,16 @@ +ifneq (,$(filter fatfs_diskio_sdcard_spi,$(USEMODULE))) + USEMODULE += sdcard_spi +endif + +ifneq (,$(filter fatfs,$(USEPKG))) + USEMODULE += fatfs_diskio_common +endif + +include $(RIOTBASE)/boards/$(BOARD)/Makefile.features + +#if periph_rtc is available use it. Otherwise use static timestamps +ifneq (, $(filter periph_rtc, $(FEATURES_PROVIDED))) + export CFLAGS+= -DFATFS_FFCONF_OPT_FS_NORTC=0 +else + export CFLAGS+= -DFATFS_FFCONF_OPT_FS_NORTC=1 +endif diff --git a/pkg/fatfs/Makefile.fatfs b/pkg/fatfs/Makefile.fatfs new file mode 100644 index 0000000000000000000000000000000000000000..3db85141a94bf486c9a05e5080b3dd83657d7e66 --- /dev/null +++ b/pkg/fatfs/Makefile.fatfs @@ -0,0 +1,3 @@ +MODULE=fatfs + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/fatfs/Makefile.include b/pkg/fatfs/Makefile.include new file mode 100644 index 0000000000000000000000000000000000000000..2b71833cd49ec8acd5cfcdbbdef607e0ed2f9e2c --- /dev/null +++ b/pkg/fatfs/Makefile.include @@ -0,0 +1,17 @@ +INCLUDES += -I$(PKGDIRBASE) +INCLUDES += -I$(RIOTPKG)/fatfs/fatfs_diskio/common/include +DIRS += $(PKGDIRBASE)/fatfs + +DIRS += $(RIOTBASE)/pkg/fatfs/fatfs_diskio/common + +ifneq (,$(filter fatfs_diskio_native,$(USEMODULE))) + DIRS += $(RIOTBASE)/pkg/fatfs/fatfs_diskio/native +endif + +ifneq (,$(filter fatfs_diskio_sdcard_spi,$(USEMODULE))) + DIRS += $(RIOTBASE)/pkg/fatfs/fatfs_diskio/sdcard_spi +endif + +ifeq ($(shell uname -s),Darwin) + CFLAGS += -Wno-empty-body +endif diff --git a/pkg/fatfs/fatfs_diskio/common/Makefile b/pkg/fatfs/fatfs_diskio/common/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..0eedefea551905ff295ef40b0e15075b5a664677 --- /dev/null +++ b/pkg/fatfs/fatfs_diskio/common/Makefile @@ -0,0 +1,3 @@ +MODULE = fatfs_diskio_common + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/fatfs/fatfs_diskio/common/fatfs_diskio_common.c b/pkg/fatfs/fatfs_diskio/common/fatfs_diskio_common.c new file mode 100644 index 0000000000000000000000000000000000000000..5e82a7d3d67b3022ca466315060712460362f57a --- /dev/null +++ b/pkg/fatfs/fatfs_diskio/common/fatfs_diskio_common.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * 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 sys_fatfs_diskio + * @{ + * + * @file + * @brief Implementation of common FatFs interface for native and sdcard_spi + * Based on low level disk I/O module example for FatFs by ChaN, 2016 + * + * @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * @} + */ + +#include "fatfs/diskio.h" /* FatFs lower layer API */ +#include "fatfs_diskio_common.h" +#include "time.h" +#include "stdint.h" + +#if FATFS_FFCONF_OPT_FS_NORTC == 0 +#include "periph/rtc.h" + +DWORD get_fattime(void) +{ + struct tm time; + + rtc_get_time(&time); + + /* bit 31:25 Year origin from 1980 (0..127) */ + uint8_t year = time.tm_year + RTC_YEAR_OFFSET - FATFS_YEAR_OFFSET; + uint8_t month = time.tm_mon + 1; /* bit 24:21 month (1..12) */ + uint8_t day_of_month = time.tm_mon + 1; /* bit 20:16 day (1..31) */ + uint8_t hour = time.tm_hour; /* bit 15:11 hour (0..23) */ + uint8_t minute = time.tm_min; /* bit 10:5 minute (0..59) */ + uint8_t second = (time.tm_sec / 2); /* bit 4:0 second/2 (0..29) */ + + return year << FATFS_DISKIO_FATTIME_YEAR_OFFS | + month << FATFS_DISKIO_FATTIME_MON_OFFS | + day_of_month << FATFS_DISKIO_FATTIME_DAY_OFFS | + hour << FATFS_DISKIO_FATTIME_HH_OFFS | + minute << FATFS_DISKIO_FATTIME_MM_OFFS | + second; +} +#endif diff --git a/pkg/fatfs/fatfs_diskio/common/include/fatfs_diskio_common.h b/pkg/fatfs/fatfs_diskio/common/include/fatfs_diskio_common.h new file mode 100644 index 0000000000000000000000000000000000000000..d019e0acddd18b0c37dfd3888530f45390810045 --- /dev/null +++ b/pkg/fatfs/fatfs_diskio/common/include/fatfs_diskio_common.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * 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 sys_fatfs_diskio + * @brief + * @{ + * + * @brief Common defines for fatfs low-level diskio defines + * @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + */ + +#ifndef FATFS_DISKIO_COMMON_H +#define FATFS_DISKIO_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "fatfs/diskio.h" /* FatFs lower layer API */ + +#define RTC_YEAR_OFFSET (1900) +#define FATFS_YEAR_OFFSET (1980) + +#define FIXED_BLOCK_SIZE (512) + +#define FATFS_DISKIO_DSTASTUS_OK (0) + +#define FATFS_DISKIO_FATTIME_YEAR_OFFS (25) +#define FATFS_DISKIO_FATTIME_MON_OFFS (21) +#define FATFS_DISKIO_FATTIME_DAY_OFFS (16) +#define FATFS_DISKIO_FATTIME_HH_OFFS (11) +#define FATFS_DISKIO_FATTIME_MM_OFFS (5) + +#ifdef __cplusplus +} +#endif + +#endif /* FATFS_DISKIO_COMMON_H */ +/** @} */ diff --git a/pkg/fatfs/fatfs_diskio/native/Makefile b/pkg/fatfs/fatfs_diskio/native/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f56b6f5f2f75c5513ddb3727aca5e6453b194b48 --- /dev/null +++ b/pkg/fatfs/fatfs_diskio/native/Makefile @@ -0,0 +1,7 @@ +MODULE = fatfs_diskio_native + +FATFS_DISKIO_NATIVE_DEFAULT_FILE ?= \"riot_fatfs_disk.img\" + +CFLAGS += -DFATFS_DISKIO_NATIVE_DEFAULT_FILE=$(FATFS_DISKIO_NATIVE_DEFAULT_FILE) + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/fatfs/fatfs_diskio/native/native_diskio.c b/pkg/fatfs/fatfs_diskio/native/native_diskio.c new file mode 100644 index 0000000000000000000000000000000000000000..a9ad3756801afa1b221663f2d76b9402d1ba35e0 --- /dev/null +++ b/pkg/fatfs/fatfs_diskio/native/native_diskio.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * 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 sys_fatfs_diskio + * @{ + * + * @file + * @brief Implementation of FatFs interface that makes use of a fatfs image + * file instead of hardware to allow FatFs usage/testing on native. + * Based on low level disk I/O module example for FatFs by ChaN, 2016 + * + * @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * @} + */ +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "fatfs/diskio.h" +#include "fatfs_diskio_common.h" +#include "fatfs/integer.h" +#include "periph/rtc.h" +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <errno.h> + +bool rtc_init_done = false; + +typedef struct { + const char *image_path; + FILE *fd; + bool opened; +} dummy_volume_t; + +static dummy_volume_t volume_files[] = { + { + .image_path = FATFS_DISKIO_NATIVE_DEFAULT_FILE, + .fd = NULL, + .opened = false + }, +}; + +static inline dummy_volume_t *get_volume_file(uint32_t idx) +{ + if (idx < sizeof(volume_files) / sizeof(dummy_volume_t)) { + return &volume_files[idx]; + } + else { + return NULL; + } +} + +/** + * @brief returns the status of the disk + * + * @param[in] pdrv drive number to identify the drive + * + * @return STA_NODISK if no disk exists with the given id + * @return 0 if disk is initialized + * @return STA_NOINIT if disk id exists, but disk isn't initialized + */ +DSTATUS disk_status(BYTE pdrv) +{ + dummy_volume_t *volume = get_volume_file(pdrv); + + if (volume == NULL) { + return STA_NODISK; + } + if (volume->opened) { + return FATFS_DISKIO_DSTASTUS_OK; + } + else { + return STA_NOINIT; + } +} + +/** + * @brief initializes the disk + * + * @param[in] pdrv drive number to identify the drive + * + * @return STA_NODISK if no disk exists with the given id + * @return 0 if disk was initialized sucessfully + * @return STA_NOINIT if disk id exists, but couldn't be initialized + */ +DSTATUS disk_initialize(BYTE pdrv) +{ + dummy_volume_t *volume = get_volume_file(pdrv); + DEBUG("disk_initialize: %d\n", pdrv); + if (volume == NULL) { + return STA_NODISK; + } + + if (volume->opened) { /* if volume is already opened close it first */ + fclose(volume->fd); + volume->opened = false; + } + + /* open file for r/w but don't create if it doesn't exist */ + FILE *fd = fopen(volume->image_path, "r+"); + DEBUG("fd: %p\n", (void *)fd); + if (fd == NULL) { + DEBUG("diskio_native.c: disk_initialize: fopen: " + "errno: 0x%08x\n", errno); + return STA_NOINIT; + } + else { + volume->fd = fd; + volume->opened = true; + return FATFS_DISKIO_DSTASTUS_OK; + } +} + +/** + * @brief reads sectors from disk + * + * @param[in] pdrv drive number to identify the drive + * @param[out] buff Data buffer to store read data + * @param[in] sector Start sector in LBA + * @param[in] count Number of sectors to read + * + * @return RES_OK if no error occurred + * @return RES_NOTRDY if data wasn't read completely + */ +DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count) +{ + dummy_volume_t *volume = get_volume_file(pdrv); + + if ((volume != NULL) && volume->opened) { + /* set read pointer to secor equivalent position */ + if (fseek(volume->fd, sector * FIXED_BLOCK_SIZE, SEEK_SET) == 0) { + if (fread(buff, FIXED_BLOCK_SIZE, count, volume->fd) == count) { + return RES_OK; + } + else { + DEBUG("diskio_native.c: disk_read: fread: " + "errno: 0x%08x\n", errno); + } + } + else { + DEBUG("diskio_native.c: disk_read: fseek: errno: 0x%08x\n", errno); + } + } + + return RES_NOTRDY; +} + +/** + * @brief writes sectors to disk + * + * @param[in] pdrv Physical drive nmuber to identify the drive + * @param[in] buff Data to be written + * @param[in] sector Start sector in LBA + * @param[in] count Number of sectors to write + * + * @return RES_OK if no error occurred + * @return RES_NOTRDY if data wasn't written completely + */ +DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) +{ + dummy_volume_t *volume = get_volume_file(pdrv); + + if ((volume != NULL) && volume->opened) { + /* set write pointer to secor equivalent position */ + if (fseek(volume->fd, sector * FIXED_BLOCK_SIZE, SEEK_SET) == 0) { + if (fwrite(buff, FIXED_BLOCK_SIZE, count, volume->fd) == count) { + if (fflush(volume->fd) == 0) { + return RES_OK; + } + else { + DEBUG("diskio_native.c: disk_write: fflush: " + "errno: 0x%08x\n", errno); + } + } + else { + DEBUG("diskio_native.c: disk_write: fwrite: " + "errno: 0x%08x\n", errno); + } + } + else { + DEBUG("diskio_native.c: disk_write: fseek: errno: 0x%08x\n", errno); + } + } + return RES_NOTRDY; +} + +/** + * @brief perform miscellaneous low-level control functions + * + * @param[in] pdrv Physical drive nmuber (0..) + * @param[in out] cmd Control code + * @param[in] sector Buffer to send/receive control data + * + * @return RES_OK if no error occurred + * @return RES_ERROR if an error occurred + * @return RES_PARERR if an error occurred + */ +DRESULT disk_ioctl( + BYTE pdrv, /* */ + BYTE cmd, /* */ + void *buff /* Buffer to send/receive control data */ + ) +{ + (void) pdrv; /* prevent warning about unused param */ + (void) buff; /* prevent warning about unused param */ + + switch (cmd) { + #if (_FS_READONLY == 0) + case CTRL_SYNC: + /* r/w is always finished within r/w-functions */ + return RES_OK; + #endif + + #if (_MAX_SS != _MIN_SS) + case GET_SECTOR_SIZE; + *buff = FIXED_BLOCK_SIZE; + return RES_OK; + #endif + + #if (_USE_MKFS == 1) + case GET_SECTOR_COUNT: + + dummy_volume_t *volume = get_volume_file(pdrv); + + if ((volume != NULL) && volume->opened) { + struct stat s; + if (stat(volume->image_path, &s) == 0) { + *(DWORD *)buff = s.st_size / FIXED_BLOCK_SIZE; + return RES_OK; + } + } + return RES_ERROR; + + case GET_BLOCK_SIZE: + *(DWORD *)buff = FIXED_BLOCK_SIZE; + return RES_OK; + #endif + + #if (_USE_TRIM == 1) + case CTRL_TRIM: + return RES_OK; + #endif + } + + return RES_PARERR; +} diff --git a/pkg/fatfs/fatfs_diskio/sdcard_spi/Makefile b/pkg/fatfs/fatfs_diskio/sdcard_spi/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..24f8401efa3dcd788f4a8d061a188a498ad0b2f6 --- /dev/null +++ b/pkg/fatfs/fatfs_diskio/sdcard_spi/Makefile @@ -0,0 +1,5 @@ +MODULE = fatfs_diskio_sdcard_spi + +USEMODULE += sdcard_spi + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/fatfs/fatfs_diskio/sdcard_spi/sdcard_spi_diskio.c b/pkg/fatfs/fatfs_diskio/sdcard_spi/sdcard_spi_diskio.c new file mode 100644 index 0000000000000000000000000000000000000000..0149ae0f1a175d930128b6daa6ca3e2f447213de --- /dev/null +++ b/pkg/fatfs/fatfs_diskio/sdcard_spi/sdcard_spi_diskio.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * 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 sys_fatfs_diskio + * @{ + * + * @file + * @brief Implementation of fatfs interface that supports sdcard_spi driver + * based on low level disk I/O module example for FatFs by ChaN, 2016 + * + * @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * @} + */ +#include "fatfs/diskio.h" /* FatFs lower layer API */ +#include "fatfs_diskio_common.h" +#include "fatfs/ffconf.h" +#include "fatfs/integer.h" +#include "sdcard_spi.h" +#include "sdcard_spi_internal.h" +#include "sdcard_spi_params.h" + +#include <stdio.h> +#include <time.h> +#include "periph_conf.h" +#include "periph/rtc.h" +#include "xtimer.h" +#include "debug.h" + +#define NUM_OF_SD_CARDS (sizeof(sdcard_spi_params) / sizeof(sdcard_spi_params[0])) +extern sdcard_spi_t sdcard_spi_devs[NUM_OF_SD_CARDS]; + +static inline sdcard_spi_t *get_sd_card(int idx) +{ + if (idx < NUM_OF_SD_CARDS) { + return &(sdcard_spi_devs[idx]); + } + + return NULL; +} + +/** + * @brief returns the status of the disk + * + * @param[in] pdrv drive number to identify the drive + * + * @return STA_NODISK if no disk exists with the given id + * @return 0 if disk is initialized + * @return STA_NOINIT if disk id exists, but disk isn't initialized + */ +DSTATUS disk_status(BYTE pdrv) +{ + sdcard_spi_t *card = get_sd_card(pdrv); + + if (card == NULL) { + return STA_NODISK; + } + else if (card->init_done) { + return FATFS_DISKIO_DSTASTUS_OK; + } + + return STA_NOINIT; +} + +/** + * @brief initializes the disk + * + * @param[in] pdrv drive number to identify the drive + * + * @return STA_NODISK if no disk exists with the given id + * @return 0 if disk was initialized sucessfully + * @return STA_NOINIT if disk id exists, but couldn't be initialized + */ +DSTATUS disk_initialize(BYTE pdrv) +{ + sdcard_spi_t *card = get_sd_card(pdrv); + + if (card == NULL) { + return STA_NODISK; + } + else if (sdcard_spi_init(card, &sdcard_spi_params[pdrv]) == 0) { + return FATFS_DISKIO_DSTASTUS_OK; + } + + return STA_NOINIT; +} + +/** + * @brief reads sectors from disk + * + * @param[in] pdrv drive number to identify the drive + * @param[out] buff Data buffer to store read data + * @param[in] sector Start sector in LBA + * @param[in] count Number of sectors to read + * + * @return RES_OK if no error occurred + * @return RES_NOTRDY if data wasn't read completely + */ +DRESULT disk_read(BYTE pdrv, BYTE *buff, DWORD sector, UINT count) +{ + sdcard_spi_t *card = get_sd_card(pdrv); + + if ((card != NULL) && card->init_done) { + sd_rw_response_t state; + if (count != sdcard_spi_read_blocks(card, sector, (char *)buff, + SD_HC_BLOCK_SIZE, count, &state)) { + printf("[ERROR] disk_read: sdcard_spi_read_blocks: %d\n", state); + return RES_NOTRDY; + } + return RES_OK; + } + + return RES_NOTRDY; +} + +/** + * @brief writes sectors to disk + * + * @param[in] pdrv Physical drive nmuber to identify the drive + * @param[in] buff Data to be written + * @param[in] sector Start sector in LBA + * @param[in] count Number of sectors to write + * + * @return RES_OK if no error occurred + * @return RES_NOTRDY if data wasn't written completely + */ +DRESULT disk_write(BYTE pdrv, const BYTE *buff, DWORD sector, UINT count) +{ + sdcard_spi_t *card = get_sd_card(pdrv); + + if ((card != NULL) && card->init_done) { + sd_rw_response_t state; + if (count != sdcard_spi_write_blocks(card, sector, (char *)buff, + SD_HC_BLOCK_SIZE, count, &state)) { + printf("[ERROR] disk_write: sdcard_spi_write_blocks: %d\n", state); + return RES_NOTRDY; + } + return RES_OK; + } + + return RES_NOTRDY; +} + +/** + * @brief perform miscellaneous low-level control functions + * + * @param[in] pdrv Physical drive nmuber (0..) + * @param[in out] cmd Control code + * @param[in] sector Buffer to send/receive control data + * + * @return RES_OK if no error occurred + * @return RES_ERROR if an error occurred + * @return RES_PARERR if an error occurred + */ +DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) +{ + #if (_USE_MKFS == 1) + sdcard_spi_t *card = get_sd_card(pdrv); + #endif + + switch (cmd) { + #if (_FS_READONLY == 0) + case CTRL_SYNC: + /* r/w is always finished within r/w-functions of sdcard_spi */ + return RES_OK; + #endif + + #if (_USE_MKFS == 1) + case GET_SECTOR_COUNT: + if ((card != NULL) && card->init_done) { + *(DWORD *)buff = sdcard_spi_get_sector_count(card); + return RES_OK; + } + else { + return RES_ERROR; + } + + case GET_BLOCK_SIZE: + if ((card != NULL) && card->init_done) { + /* erase block size in unit of sector */ + *(DWORD *)buff = sdcard_spi_get_au_size(card) / SD_HC_BLOCK_SIZE; + return RES_OK; + } + *(DWORD *)buff = 0; + return RES_ERROR; + #endif + + #if (_MAX_SS != _MIN_SS) + case GET_SECTOR_SIZE: + *buff = SD_HC_BLOCK_SIZE; + return RES_OK; + #endif + + #if (_USE_TRIM == 1) + case CTRL_TRIM: + return RES_OK; + #endif + } + + return RES_PARERR; +} diff --git a/sys/Makefile b/sys/Makefile index 143b65e2b177853e53f0be4426b6699666510172..6e431596f7f756cab98590caf627e5b4d2ce5f6b 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -82,23 +82,18 @@ endif ifneq (,$(filter tcp,$(USEMODULE))) DIRS += net/transport_layer/tcp endif - ifneq (,$(filter hamming256,$(USEMODULE))) DIRS += ecc/hamming256 endif - ifneq (,$(filter uhcpc,$(USEMODULE))) DIRS += net/application_layer/uhcp endif - ifneq (,$(filter gnrc_uhcpc,$(USEMODULE))) DIRS += net/gnrc/application_layer/uhcpc endif - ifneq (,$(filter sntp,$(USEMODULE))) DIRS += net/application_layer/sntp endif - ifneq (,$(filter netopt,$(USEMODULE))) DIRS += net/crosslayer/netopt endif @@ -117,11 +112,9 @@ endif ifneq (,$(filter sock_dns,$(USEMODULE))) DIRS += net/application_layer/dns endif - ifneq (,$(filter constfs,$(USEMODULE))) DIRS += fs/constfs endif - ifneq (,$(filter devfs,$(USEMODULE))) DIRS += fs/devfs endif diff --git a/tests/pkg_fatfs/Makefile b/tests/pkg_fatfs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..a984a73d84557793a14fc739f52405d15fc6d55e --- /dev/null +++ b/tests/pkg_fatfs/Makefile @@ -0,0 +1,33 @@ +APPLICATION = pkg_fatfs +include ../Makefile.tests_common + +USEMODULE += shell + +BOARD ?= native + +# whitelist can be removed when the problem described in #6063 was resolved +# this list is composed of boards that support spi + native +BOARD_WHITELIST := native airfy-beacon arduino-due arduino-duemilanove arduino-mega2560 \ + arduino-uno arduino-zero avsextrem cc2538dk fox frdm-k64f iotlab-a8-m3 \ + iotlab-m3 limifrog-v1 maple-mini msb-430 msb-430h msba2 msbiot mulle \ + nrf52840dk nrf52dk nrf6310 nucleo-f072 nucleo-f091 nucleo-f103 \ + nucleo-f302 nucleo-f303 nucleo-f334 nucleo-f401 nucleo-f410 nucleo-f411 \ + nucleo-f446 nucleo-l053 nucleo-l073 nucleo-l1 nucleo-l476 nucleo144-f207 \ + nucleo144-f303 nucleo144-f413 nucleo144-f429 nucleo144-f446 nucleo32-f031 \ + nucleo32-f042 nucleo32-f303 nucleo32-l031 nucleo32-l432 openmote-cc2538 \ + pba-d-01-kw2x pca10005 remote-pa remote-reva remote-revb samd21-xpro \ + saml21-xpro samr21-xpro sodaq-autonomo spark-core stm32f0discovery \ + stm32f3discovery stm32f4discovery telosb udoo waspmote-pro weio \ + wsn430-v1_3b wsn430-v1_4 yunjia-nrf51822 z1 + +ifeq ($(BOARD),native) + USEMODULE += fatfs_diskio_native + FATFS_DISKIO_NATIVE_DEFAULT_FILE ?= \"riot_fatfs_disk.img\" +else + USEMODULE += fatfs_diskio_sdcard_spi + USEMODULE += auto_init_storage +endif + +USEPKG += fatfs + +include $(RIOTBASE)/Makefile.include diff --git a/tests/pkg_fatfs/README.md b/tests/pkg_fatfs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..92e8f2ac23910b130e7d1bfa410586c304a9530e --- /dev/null +++ b/tests/pkg_fatfs/README.md @@ -0,0 +1,27 @@ +Using FatFs on native +======================================= +To use this test on native you need a FAT image file. The following commands can be used to create such an image and mount it afterwards so you can add files to your virtual disk that can later be accessed from RIOT. + +1. create an enpty file with a size of 128MB +`dd if=/dev/zero of=riot_fatfs_disk.img bs=1M count=128` + +2. create a FAT file system within the file +`mkfs.fat riot_fatfs_disk.img` + +3. create a mount point which you can use later to add files with your file browser +`sudo mkdir -p /media/riot_fatfs_disk` + +4. give all needed rights for that mountpoint to your user +`sudo chown <your_username> /media/riot_fatfs_disk/` + +5. mount the image -> the disk should now be accessible from any program +`sudo mount -o loop,umask=000 riot_fatfs_disk.img /media/riot_fatfs_disk` + +6. When you are done -> unmount the disk before you use it under RIOT +`sudo umount /media/riot_fatfs_disk` + +#####NOTE: +You shouldn't leave the image mounted while you use it in RIOT, the abstraction layer between FatFs and the image file mimics a dumb block device +(i.e. behaves much like the devices that are actually meant to be used with FAT) That implies it doesn't show any modifications in RIOT that you perform on your OS and the other way round. So always remember to mount/unmount correctly or your FS will probably get damaged. + +To tell RIOT where your image file is located you can use the image_path entry in the volume_files array in fatfs_diskio_native/diskio.c. \ No newline at end of file diff --git a/tests/pkg_fatfs/main.c b/tests/pkg_fatfs/main.c new file mode 100644 index 0000000000000000000000000000000000000000..57a25b85e6cb3515bd33789cdfbd6e15fa98f406 --- /dev/null +++ b/tests/pkg_fatfs/main.c @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * 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 tests + * @{ + * + * @file + * @brief Test application for the fatfs package. + * + * @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de> + * + * @} + */ + +#if FATFS_FFCONF_OPT_FS_NORTC == 0 +#include "periph/rtc.h" +#endif +#include "fatfs_diskio_common.h" +#include "fatfs/ff.h" +#include "shell.h" +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <stdbool.h> + +#define TEST_FATFS_READ_BUFFER_SIZE 64 +#define TEST_FATFS_MAX_LBL_SIZE 64 +#define TEST_FATFS_MAX_VOL_STR_LEN 8 +#define TEST_FATFS_FIXED_SECTOR_SIZE 512 +#define TEST_FATFS_FATENT_OFFSET 2 +#define TEST_FATFS_SHIFT_B_TO_GIB 30 +#define TEST_FATFS_SHIFT_B_TO_MIB 20 +#define TEST_FATFS_RTC_MON_OFFSET 1 +#define TEST_FATFS_RTC_YEAR 2000 +#define TEST_FATFS_RTC_MON 1 +#define TEST_FATFS_RTC_DAY 1 +#define TEST_FATFS_RTC_H 0 +#define TEST_FATFS_RTC_M 0 +#define TEST_FATFS_RTC_S 0 +#define IEC_KIBI 1024 +#define SI_KILO 1000 + +FATFS fat_fs; /* FatFs work area needed for each volume */ + +static int _mount(int argc, char **argv) +{ + int vol_idx; + + if (argc != 2) { + printf("usage: %s <volume_idx>\n", argv[0]); + return -1; + } + + vol_idx = (int)atoi(argv[1]); + + char volume_str[TEST_FATFS_MAX_VOL_STR_LEN]; + sprintf(volume_str, "%d:/", vol_idx); + + puts("mounting file system image..."); + + /* "0:/" points to the root dir of drive 0 */ + FRESULT mountresu = f_mount(&fat_fs, volume_str, 1); + TCHAR label[TEST_FATFS_MAX_LBL_SIZE]; + + if (mountresu == FR_OK) { + puts("[OK]"); + if (f_getlabel("", label, NULL) == FR_OK) { + printf("Volume name: %s\n", label); + } + + FATFS *fs; + DWORD fre_clust; + + /* Get volume information and free clusters of selected drive */ + if (f_getfree(volume_str, &fre_clust, &fs) != FR_OK) { + puts("wasn't able to get volume size info!"); + } + else { + + #if _MAX_SS == _MIN_SS + uint16_t sector_size = TEST_FATFS_FIXED_SECTOR_SIZE; + #else + uint16_t sector_size = fs->ssize; + #endif + + uint64_t total_bytes = (fs->n_fatent - TEST_FATFS_FATENT_OFFSET) * fs->csize; + total_bytes *= sector_size; + uint64_t free_bytes = fre_clust * fs->csize; + free_bytes *= sector_size; + + uint32_t to_gib_i = total_bytes >> TEST_FATFS_SHIFT_B_TO_GIB; + uint32_t to_gib_f = ((((total_bytes >> TEST_FATFS_SHIFT_B_TO_MIB) - to_gib_i * IEC_KIBI) + * SI_KILO) / IEC_KIBI); + + uint32_t fr_gib_i = free_bytes >> TEST_FATFS_SHIFT_B_TO_GIB; + uint32_t fr_gib_f = ((((free_bytes >> TEST_FATFS_SHIFT_B_TO_MIB) - fr_gib_i * IEC_KIBI) + * SI_KILO) / IEC_KIBI); + + printf("%" PRIu32 ",%03" PRIu32 " GiB of %" PRIu32 ",%03" PRIu32 + " GiB available\n", fr_gib_i, fr_gib_f, to_gib_i, to_gib_f); + } + } + else { + puts("[FAILED]"); + switch (mountresu) { + case FR_NO_FILESYSTEM: + puts("no filesystem -> you need to format the card to FAT"); + break; + case FR_DISK_ERR: + puts("error in the low-level disk driver!"); + break; + default: + printf("error %d -> see ff.h of fatfs package for " + "further details\n", mountresu); + } + return -1; + } + return 0; +} + +static int _touch(int argc, char **argv) +{ + FIL fd; + + if (argc != 2) { + printf("usage: %s <filename>\n", argv[0]); + return -1; + } + + FRESULT open_resu = f_open(&fd, argv[1], FA_WRITE | FA_CREATE_ALWAYS); + if (open_resu == FR_OK) { + FRESULT close_resu = f_close(&fd); + if (close_resu == FR_OK) { + puts("[OK]"); + return 0; + } + + printf("[FAILED] (f_close error %d)\n", close_resu); + return -2; + } + + printf("[FAILED] (f_open error %d)\n", open_resu); + return -3; +} + +static int _read(int argc, char **argv) +{ + FIL fd; + int resu = 0; + + if ((argc < 2) || (argc > 3)) { + printf("usage: %s <filename> [<len>]\n", argv[0]); + return -1; + } + + FRESULT open_resu = f_open(&fd, argv[1], FA_READ | FA_OPEN_EXISTING); + if (open_resu == FR_OK) { + UINT read_chunk; + uint32_t len = ((argc == 3) ? (uint32_t)atoi(argv[2]) : f_size(&fd)); + + char buffer[TEST_FATFS_READ_BUFFER_SIZE]; + + for (uint32_t read = 0; read < len; read += read_chunk) { + uint32_t to_read = len - read; + + if (to_read > sizeof(buffer)) { + to_read = sizeof(buffer); + } + + FRESULT lseek_resu = f_lseek(&fd, read); + + if (lseek_resu != FR_OK) { + printf("[FAILED] f_lseek error %d\n", lseek_resu); + resu = -3; + break; + } + + FRESULT read_resu = f_read(&fd, buffer, to_read, &read_chunk); + + if (read_resu != FR_OK) { + printf("[FAILED] (f_read error %d)\n", read_resu); + resu = -4; + break; + } + + for (uint32_t i = 0; i < read_chunk; i++) { + printf("%c", buffer[i]); + } + } + puts(""); + + FRESULT close_resu = f_close(&fd); + + if (close_resu == FR_OK) { + puts("[OK]"); + resu = 0; + } + else { + printf("[FAILED] (f_close error %d)\n", open_resu); + resu = -5; + } + + } + else { + printf("[FAILED] (f_open error %d)\n", open_resu); + resu = -2; + } + + return resu; +} + +static int _write(int argc, char **argv) +{ + FIL fd; + UINT bw; + + if (argc != 3) { + printf("usage: %s <filename> <string>\n", argv[0]); + return -1; + } + + uint32_t len = strlen(argv[2]); + FRESULT open_resu = f_open(&fd, argv[1], FA_WRITE | FA_OPEN_APPEND); + + if (open_resu == FR_OK) { + printf("writing %" PRId32 " bytes to %s ...", len, argv[1]); + FRESULT write_resu = f_write(&fd, argv[2], len, &bw); + + if ((write_resu != FR_OK) || (bw < len)) { + printf("[FAILED] (f_write error %d)\n", write_resu); + return -2; + } + else { + FRESULT close_resu = f_close(&fd); + + if (close_resu == FR_OK) { + puts("[OK]"); + return 0; + } + + printf("[FAILED] (f_close error %d)\n", open_resu); + return -3; + } + } + + printf("[FAILED] (f_open error %d)\n", open_resu); + return -1; +} + +static int _ls(int argc, char **argv) +{ + char *path; + FRESULT res; + DIR dir; + static FILINFO fno; + + if (argc == 2) { + path = argv[1]; + } + else { + path = "/"; + } + + res = f_opendir(&dir, path);/* Open the directory */ + + if (res == FR_OK) { + while (true) { + res = f_readdir(&dir, &fno); /* Read a directory item */ + + if ((res != FR_OK) || fno.fname[0] == 0) { + break; /* Break on error or end of dir */ + } + + if (fno.fattrib & AM_DIR) { /* if this element is a directory */ + printf("%s%s/\n", path, fno.fname); + } + else { + printf("%s/%s\n", path, fno.fname); + } + } + + f_closedir(&dir); + return 0; + } + + printf("[FAILED] error %d\n", res); + return -1; +} + +static int _mkfs(int argc, char **argv) +{ + int vol_idx; + BYTE opt; + + if (argc == 3) { + vol_idx = (int)atoi(argv[1]); + + if (strcmp(argv[2], "fat") == 0) { + opt = FM_FAT; + } + else if (strcmp(argv[2], "fat32") == 0) { + opt = FM_FAT32; + } + else if (strcmp(argv[2], "exfat") == 0) { + opt = FM_EXFAT; + } + else { + opt = FM_ANY; + } + } + else { + printf("usage: %s <volume_idx> <fat|fat32|exfat|any>\n", argv[0]); + return -1; + } + + char volume_str[TEST_FATFS_MAX_VOL_STR_LEN]; + sprintf(volume_str, "%d:/", vol_idx); + BYTE work[_MAX_SS]; + + puts("formatting media..."); + + /* au = 0: use default allocation unit size depending on volume size */ + FRESULT mkfs_resu = f_mkfs(volume_str, opt, 0, work, sizeof(work)); + + if (mkfs_resu == FR_OK) { + puts("[OK]"); + return 0; + } + + printf("[FAILED] error %d\n", mkfs_resu); + return -1; +} + +static const shell_command_t shell_commands[] = { + { "mount", "mount file system", _mount }, + { "mkfs", "format volume", _mkfs }, + { "touch", "create file", _touch }, + { "read", "print file content to console", _read }, + { "write", "append string to file", _write }, + { "ls", "list files", _ls }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + #if FATFS_FFCONF_OPT_FS_NORTC == 0 + /* the rtc is used in diskio.c for timestamps of files */ + puts("Initializing the RTC driver"); + rtc_poweron(); + rtc_init(); + + struct tm time; + time.tm_year = TEST_FATFS_RTC_YEAR - RTC_YEAR_OFFSET; /* years are counted from 1900 */ + time.tm_mon = TEST_FATFS_RTC_MON; /* 0 = January, 11 = December */ + time.tm_mday = TEST_FATFS_RTC_DAY; + time.tm_hour = TEST_FATFS_RTC_H; + time.tm_min = TEST_FATFS_RTC_M; + time.tm_sec = TEST_FATFS_RTC_S; + + printf("Setting RTC to %04d-%02d-%02d %02d:%02d:%02d\n", + time.tm_year + RTC_YEAR_OFFSET, + time.tm_mon + TEST_FATFS_RTC_MON_OFFSET, + time.tm_mday, + time.tm_hour, + time.tm_min, + time.tm_sec); + rtc_set_time(&time); + #endif + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + return 0; +}