Compare commits

...

7 Commits

Author SHA1 Message Date
Aaron Nyholm
5d2632359b Merge branch 'flash-parsers' into 'main'
bsps/shared/dev/nor: Add SFDP and CFI parsers

See merge request rtems/rtos/rtems!823
2025-11-14 12:40:41 +11:00
Sepehr Ganji
bff18c05b9 libfs/fatfs: Add FatFS with tests
This adds FatFS wrapper layer to RTEMS
including tests as part of GSoC 2025.
2025-11-10 16:09:07 -07:00
Kinsey Moore
e3592620d7 cpukit/flashdev: Return error for missing callbacks
When a callback does not exist, the Flashdev API should return error
where possible instead of success. When the API returns success for
missing callbacks, the Flashdev API client code may end up using
uninitialized variables as if they were filled by the API.

Closes #5391
2025-11-10 12:29:32 -06:00
Kinsey Moore
54de3ac479 bsps/shared/xqspi_flash.c: Add support for sector information 2025-11-10 12:29:31 -06:00
Aaron Nyholm
71f4ff6316 bsps/xilinx-zynq: Added CFI support to QSPI driver 2025-11-02 22:31:18 -06:00
Aaron Nyholm
fabfef6c24 bsps/shared/dev/nor: Added support for multiple CFI regions
CFI parser now returns largest block size when multiple regions
are present allowing for block erase to be used with returned size.
2025-11-02 22:31:18 -06:00
Kinsey Moore
f77bb9762a bsps/shared/dev/nor: Add SFDP and CFI parsers 2025-11-02 22:31:18 -06:00
36 changed files with 4100 additions and 24 deletions

View File

@@ -31,6 +31,7 @@
#include <stdio.h>
#include <stdint.h>
#include <dev/nor/config-parser.h>
#include <dev/spi/zynq-qspi-flash.h>
#include <dev/spi/zynq-qspi-flash-defs.h>
#include <dev/spi/jedec_flash.h>
@@ -750,6 +751,8 @@ zqspi_error zqspi_readid(zqspiflash *driver, uint32_t *jedec_id)
zqspi_error fe;
uint8_t value = 0;
int index = 0;
bool use_cfi = true;
size_t cfi_raw_len = 0;
if (driver->jedec_id != 0) {
if (jedec_id != NULL) {
@@ -759,9 +762,9 @@ zqspi_error zqspi_readid(zqspiflash *driver, uint32_t *jedec_id)
}
zqspi_transfer_buffer_clear(&driver->buf);
zqspi_transfer_buffer_set_length(&driver->buf, 1 + 3);
zqspi_transfer_buffer_set_length(&driver->buf, 1 + 4);
zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_READ_ID);
zqspi_transfer_buffer_fill(&driver->buf, 0x00, 3);
zqspi_transfer_buffer_fill(&driver->buf, 0x00, 4);
zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_RX_TRANS);
zqspi_transfer_buffer_set_command_len(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE);
@@ -776,19 +779,58 @@ zqspi_error zqspi_readid(zqspiflash *driver, uint32_t *jedec_id)
zqspi_transfer_buffer_get8(&driver->buf, &value);
driver->jedec_id |= value;
zqspi_transfer_buffer_get8(&driver->buf, &value);
cfi_raw_len = value + 4;
if (jedec_id != NULL) {
*jedec_id = driver->jedec_id;
}
while (flash_dev_table[index].jedec_id != driver->jedec_id) {
if (flash_dev_table[index].jedec_id == 0) {
return ZQSPI_FLASH_INVALID_DEVICE;
while (flash_dev_table[index].jedec_id != 0) {
if (flash_dev_table[index].jedec_id == driver->jedec_id) {
use_cfi = false;
driver->flash_size = flash_dev_table[index].flash_size;
driver->flash_erase_sector_size = flash_dev_table[index].sec_size;
driver->flash_page_size = flash_dev_table[index].page_size;
break;
}
index++;
}
driver->flash_size = flash_dev_table[index].flash_size;
driver->flash_erase_sector_size = flash_dev_table[index].sec_size;
driver->flash_page_size = flash_dev_table[index].page_size;
if (use_cfi) {
rtems_status_code sc;
NOR_Config_Data nor_config;
uint8_t cfi_data[256];
if (cfi_raw_len > sizeof(cfi_data)) {
return ZQSPI_FLASH_INVALID_DEVICE;
}
zqspi_transfer_buffer_clear(&driver->buf);
zqspi_transfer_buffer_set_length(&driver->buf, 1 + cfi_raw_len);
zqspi_transfer_buffer_set8(&driver->buf, ZQSPI_FLASH_READ_ID);
zqspi_transfer_buffer_fill(&driver->buf, 0x00, cfi_raw_len);
zqspi_transfer_buffer_set_dir(&driver->buf, ZQSPI_FLASH_RX_TRANS);
zqspi_transfer_buffer_set_command_len(&driver->buf, ZQSPI_FLASH_COMMAND_SIZE);
fe = zqspi_transfer(&driver->buf, &driver->initialised);
if (fe != ZQSPI_FLASH_NO_ERROR)
return fe;
fe = zqspi_transfer_buffer_copy_out(&driver->buf, cfi_data, cfi_raw_len);
if (fe != ZQSPI_FLASH_NO_ERROR)
return fe;
sc = CFI_parse_from_buffer(cfi_data, cfi_raw_len, &nor_config);
if (sc != RTEMS_SUCCESSFUL) {
return ZQSPI_FLASH_INVALID_DEVICE;
}
driver->flash_size = nor_config.DeviceSize;
driver->flash_erase_sector_size = nor_config.SectorSize;
driver->flash_page_size = nor_config.PageSize;
}
#if ZQSPI_FLASH_FAST_READ
driver->flash_read_dummies = 1;
#endif

View File

@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2023 On-Line Applications Research Corporation (OAR)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <rtems/rtems/status.h>
#include <stddef.h>
#include <stdint.h>
/**
* This struct holds the information parsed from the Common Flash Memory
* Information (CFI) read from a flash chip.
*/
typedef struct NOR_Config_Data {
/* JEDEC ID */
uint32_t jedec_id;
/* Sector size in bytes */
uint32_t SectorSize;
/* Number of sectors */
uint32_t NumSectors;
/* Page size in bytes. This will default to 256 if unavailable. */
uint16_t PageSize;
/* This is the size of the flash device */
uint64_t DeviceSize;
/*
* Alternative Sector size in bytes. This will be 0 if the alternative sector
* size is unsupported or unavailable.
*/
uint32_t AltSectorSize;
/* The command byte to erase sectors in the alternative size */
uint8_t AltSectorCmd;
/* Number of sectors for the alternative sector size */
uint32_t NumAltSectors;
} NOR_Config_Data;
/**
*
* This function parses the provided buffer of CFI data into a NOR_Config_Data
* structure.
*
* @param cfi_raw is a buffer containing CFI data.
* @param cfi_raw_len is the length of the data in cfi_raw.
* @param data is a pointer to a NOR_Config_Data struct to be filled with data
* about the flash chip.
*
* @return RTEMS_SUCCESSFUL if successful.
*/
rtems_status_code CFI_parse_from_buffer(
uint8_t *cfi_raw,
size_t cfi_raw_len,
NOR_Config_Data *data
);
/**
*
* This function parses the provided buffer of SFDP data into a NOR_Config_Data
* structure.
*
* @param sfdp_raw is a buffer containing SFDP data.
* @param sfdp_raw_len is the length of the data in sfdp_raw.
* @param data is a pointer to a NOR_Config_Data struct to be filled with data
* about the flash chip.
*
* @return RTEMS_SUCCESSFUL if successful.
*/
rtems_status_code SFDP_parse_from_buffer(
uint8_t *sfdp_raw,
size_t sfdp_raw_len,
NOR_Config_Data *data
);
/**
* @brief Acquire data from the NOR chip.
*
* @param[in] context The context data used to retrieve bytes.
* @param[in] offset The offset to read from the flash begin in bytes.
* @param[in] length The size of the buffer in bytes.
*
* @retval NULL on failure.
* @retval pointer to the requested data on success.
*/
typedef uint8_t *(*NOR_Config_Resource_Acquire)(
void *context,
uint32_t offset,
size_t length
);
/**
* @brief Release data acquired from the NOR chip.
*
* @param[in] context The context data used to retrieve bytes.
* @param[in] data The data previously acquired.
*/
typedef void (*NOR_Config_Resource_Release)(
void *context,
uint8_t *data
);
typedef struct NOR_Config_Accessor {
/* The context provided to the acquire and release functions */
void *context;
/* The function used to acquire a block of data */
NOR_Config_Resource_Acquire acquire;
/* The function used to release a block of data */
NOR_Config_Resource_Release release;
} NOR_Config_Accessor;
/**
*
* This function parses a SFDP configuration space into a NOR_Config_Data
* structure.
*
* @param accessor is the accessor for the SFDP configuration space.
* @param data is a pointer to a NOR_Config_Data struct to be filled with data
* about the flash chip.
*
* @return RTEMS_SUCCESSFUL if successful.
*/
rtems_status_code SFDP_parse(
NOR_Config_Accessor *accessor,
NOR_Config_Data *data
);

View File

@@ -0,0 +1,412 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (C) 2023 On-Line Applications Research Corporation (OAR)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <dev/nor/config-parser.h>
#include <string.h>
static int read_config_byte(uint8_t *config_raw, size_t config_raw_len, int datalen, int desired_byte, uint8_t *read_byte)
{
/* access outside buffer */
if (desired_byte >= config_raw_len) {
return 1;
}
if (datalen != 0) {
/* access past self-declared limit */
if (desired_byte >= datalen) {
return 1;
}
}
*read_byte = config_raw[desired_byte];
return 0;
}
static int verify_config_string(uint8_t *config_raw, size_t config_raw_len, int datalen, int startbyte, char *config_string)
{
uint8_t bufbyte;
int endbyte = strlen(config_string) + startbyte;
for (;startbyte < endbyte; startbyte++, config_string++) {
if (read_config_byte(config_raw, config_raw_len, datalen, startbyte, &bufbyte)) {
return 1;
}
if (bufbyte != *config_string) {
return 1;
}
}
return 0;
}
/* Read two consecutive bytes as a little endian short */
static int read_config_short(uint8_t *config_raw, size_t config_raw_len, int datalen, int startbyte, uint16_t *config_short)
{
uint8_t bufbyte;
if (read_config_byte(config_raw, config_raw_len, datalen, startbyte + 1, &bufbyte)) {
return 1;
}
*config_short = bufbyte;
*config_short <<= 8;
if (read_config_byte(config_raw, config_raw_len, datalen, startbyte, &bufbyte)) {
return 1;
}
*config_short |= bufbyte;
return 0;
}
/* Read two consecutive shorts as an int */
static int read_config_int(uint8_t *config_raw, size_t config_raw_len, int datalen, int startbyte, uint32_t *config_int)
{
uint16_t bufshort;
if (read_config_short(config_raw, config_raw_len, datalen, startbyte + 2, &bufshort)) {
return 1;
}
*config_int = bufshort;
*config_int <<= 16;
if (read_config_short(config_raw, config_raw_len, datalen, startbyte, &bufshort)) {
return 1;
}
*config_int |= bufshort;
return 0;
}
rtems_status_code CFI_parse_from_buffer(
uint8_t *cfi_raw,
size_t cfi_raw_len,
NOR_Config_Data *data
)
{
memset(data, 0, sizeof(NOR_Config_Data));
if (cfi_raw_len < 4 || cfi_raw_len > 512) {
return RTEMS_INVALID_SIZE;
}
data->jedec_id = ((cfi_raw[0] << 16) | (cfi_raw[1] << 8) | cfi_raw[2]);
int datalen = cfi_raw[3];
if (datalen != 0) {
/*
* datalen as read is remaining valid bytes after 0x3 (the 4th byte), so
* account for those bytes since this is used as an absolute index limit
* */
datalen += 4;
}
/* verify query string ("QRY") exists */
if (verify_config_string(cfi_raw, cfi_raw_len, datalen, 0x10, "QRY")) {
return RTEMS_INVALID_NAME;
}
/* get device geometry */
uint8_t bufbyte;
if (read_config_byte(cfi_raw, cfi_raw_len, datalen, 0x27, &bufbyte)) {
return RTEMS_INVALID_ADDRESS;
}
data->DeviceSize = 1UL << bufbyte;
uint16_t page_power;
if (read_config_short(cfi_raw, cfi_raw_len, datalen, 0x2a, &page_power)) {
return RTEMS_INVALID_ADDRESS;
}
data->PageSize = 1UL << page_power;
if (read_config_byte(cfi_raw, cfi_raw_len, datalen, 0x2c, &bufbyte)) {
return RTEMS_INVALID_ADDRESS;
}
/* Get largest block */
uint8_t num_regions = bufbyte;
data->SectorSize = 0;
for (int region = 0; region < num_regions; ++region) {
uint16_t num_sectors_sub;
if (read_config_short(cfi_raw, cfi_raw_len, datalen, 0x2d + (region * 4),
&num_sectors_sub)) {
return RTEMS_INVALID_ADDRESS;
}
uint16_t sector_base;
if (read_config_short(cfi_raw, cfi_raw_len, datalen, 0x2f + (region * 4),
&sector_base)) {
return RTEMS_INVALID_ADDRESS;
}
if (data->SectorSize < (sector_base * 256UL)) {
data->NumSectors = num_sectors_sub + 1;
data->SectorSize = sector_base * 256UL;
}
}
if (num_regions == 1) {
/* Device size for at least s25fl512s is off by 1, calculate with sectors */
data->DeviceSize = data->NumSectors * data->SectorSize;
} else {
data->NumSectors = data->DeviceSize / data->SectorSize;
}
return RTEMS_SUCCESSFUL;
}
static int parse_sfdp_jedec_table(
uint8_t *tdata,
size_t tlen,
int version,
NOR_Config_Data *data
)
{
/* locals to be applied once parsing is guaranteed successful */
NOR_Config_Data local = {0,};
local.PageSize = 256;
int datalen = 0;
/* Read raw size from second dword */
uint32_t raw_size;
int raw_size_offset = 4;
if (read_config_int(tdata, tlen, datalen, raw_size_offset, &raw_size)) {
return -1;
}
if (raw_size & 0x80000000UL) {
raw_size &= 0x7FFFFFFFUL;
local.DeviceSize = 1ULL << raw_size;
} else {
local.DeviceSize = raw_size + 1;
}
/* Device size is specified in bits, convert to bytes */
local.DeviceSize /= 8;
/* Parse sector information from dwords 8 and 9 */
int sector_erase_dword_offset = 7;
int sector_erase_offset = 4 * sector_erase_dword_offset;
uint8_t bufbyte;
for (int sect_erase_index = 0; sect_erase_index < 4; sect_erase_index++) {
int sector_info_offset = sector_erase_offset + 2 * sect_erase_index;
if (read_config_byte(tdata, tlen, datalen, sector_info_offset, &bufbyte)) {
return -1;
}
if (bufbyte == 0) {
/* empty sector descriptor */
continue;
}
int sector_size = 1UL << bufbyte;
if (read_config_byte(tdata, tlen, datalen, sector_info_offset + 1, &bufbyte)) {
return -1;
}
if (bufbyte == 0xD8) {
/* This is the 'normal' sector size */
local.SectorSize = sector_size;
local.NumSectors = (uint32_t)(local.DeviceSize/local.SectorSize);
} else {
/* store the alternate sector size/command */
local.AltSectorSize = sector_size;
local.AltSectorCmd = bufbyte;
local.NumAltSectors = (uint32_t)(local.DeviceSize/local.AltSectorSize);
}
}
if (version >= 5) {
int page_info_dword_offset = 10;
int page_info_offset = 4 * page_info_dword_offset;
if (read_config_byte(tdata, tlen, datalen, page_info_offset, &bufbyte)) {
return -1;
}
bufbyte >>= 4;
bufbyte &= 0xF;
local.PageSize = 1U << bufbyte;
}
/* Apply local parse data now that parsing has succeeded */
*data = local;
return 0;
}
static int parse_sfdp_basic_header(
NOR_Config_Accessor *accessor,
uint8_t *header_base,
NOR_Config_Data *data
)
{
int datalen = 0;
uint8_t bufbyte;
int hlen = 8;
/* check minor version */
if (read_config_byte(header_base, hlen, datalen, 0x1, &bufbyte)) {
return -1;
}
int version = bufbyte;
/* get table length in 4-byte words */
if (read_config_byte(header_base, hlen, datalen, 0x3, &bufbyte)) {
return -1;
}
int tlen = bufbyte;
tlen *= 4;
/* read table offset */
uint32_t toffset = 0;
if (read_config_int(header_base, hlen, datalen, 0x4, &toffset)) {
return -1;
}
/* mask off the top byte */
toffset &= 0xFFFFFFUL;
uint8_t *table_data = accessor->acquire(accessor->context, toffset, tlen);
if (table_data == NULL) {
return RTEMS_INVALID_SIZE;
}
if (parse_sfdp_jedec_table(table_data, tlen, version, data)) {
accessor->release(accessor->context, table_data);
table_data = NULL;
return -1;
}
accessor->release(accessor->context, table_data);
table_data = NULL;
return 0;
}
rtems_status_code SFDP_parse(
NOR_Config_Accessor *accessor,
NOR_Config_Data *data
)
{
int datalen = 0;
memset(data, 0, sizeof(NOR_Config_Data));
size_t header_len = 8;
uint8_t *header_data = accessor->acquire(accessor->context, 0, header_len);
if (header_data == NULL) {
return RTEMS_INVALID_SIZE;
}
/* verify SFDP ID string ("SFDP") exists */
if (verify_config_string(header_data, header_len, datalen, 0x0, "SFDP")) {
return RTEMS_INVALID_NAME;
}
/* check major version */
uint8_t bufbyte;
if (read_config_byte(header_data, header_len, datalen, 0x05, &bufbyte)) {
return RTEMS_INVALID_ADDRESS;
}
if (bufbyte != 1) {
return RTEMS_TOO_MANY;
}
/* get count of parameter headers */
if (read_config_byte(header_data, header_len, datalen, 0x06, &bufbyte)) {
return RTEMS_INVALID_ADDRESS;
}
int pheaders_count = bufbyte + 1;
accessor->release(accessor->context, header_data);
header_data = NULL;
/* get parameter table descriptors */
size_t ptd_len = 8;
size_t total_ptd_len = ptd_len * pheaders_count;
uint8_t *ptable_desc = accessor->acquire(accessor->context, header_len, total_ptd_len);
if (ptable_desc == NULL) {
return RTEMS_INVALID_SIZE;
}
int pheader_index;
int param_version_seen = -1;
for (pheader_index = 0; pheader_index < pheaders_count; pheader_index++) {
int ptable_offset = pheader_index * ptd_len;
uint8_t *header_base = ptable_desc + ptable_offset;
/* only parse JEDEC SFDP Basic SPI Flash Parameter */
if (read_config_byte(header_base, ptd_len, datalen, 0x0, &bufbyte)) {
accessor->release(accessor->context, ptable_desc);
ptable_desc = NULL;
return -1;
}
if (bufbyte != 0) {
continue;
}
/* check minor version */
if (read_config_byte(header_base, ptd_len, datalen, 0x1, &bufbyte)) {
accessor->release(accessor->context, ptable_desc);
ptable_desc = NULL;
return -1;
}
int version = bufbyte;
if (version > param_version_seen) {
/* only parse if interested in this version */
if (parse_sfdp_basic_header(accessor, header_base, data)) {
/* parse failed, continue */
continue;
}
param_version_seen = version;
}
}
accessor->release(accessor->context, ptable_desc);
ptable_desc = NULL;
if (param_version_seen == -1) {
return RTEMS_INVALID_ADDRESS;
}
return RTEMS_SUCCESSFUL;
}
struct buffer_context {
uint8_t *data;
size_t length;
};
static uint8_t *buffer_acquire(
void *arg,
uint32_t offset,
size_t length
)
{
struct buffer_context *context = arg;
if (offset + length > context->length) return NULL;
return context->data + offset;
}
static void buffer_release(
void *arg,
uint8_t *data
)
{
(void) arg;
(void) data;
/* Do nothing */
}
rtems_status_code SFDP_parse_from_buffer(
uint8_t *sfdp_raw,
size_t sfdp_raw_len,
NOR_Config_Data *data
)
{
struct buffer_context context = {sfdp_raw, sfdp_raw_len};
NOR_Config_Accessor accessor;
accessor.context = &context;
accessor.acquire = buffer_acquire;
accessor.release = buffer_release;
return SFDP_parse(&accessor, data);
}

View File

@@ -103,6 +103,28 @@ static int xqspi_page_count(
return 0;
}
static int xqspi_sector_count(
rtems_flashdev *flash,
int *sector_count
)
{
*sector_count = QspiPsu_NOR_Get_Device_Size(flash->driver) /
QspiPsu_NOR_Get_Sector_Size(flash->driver);
return 0;
}
static int xqspi_sector_info_by_off(
rtems_flashdev *flash,
off_t search_offset,
off_t *sector_offset,
size_t *sector_size
)
{
*sector_size = QspiPsu_NOR_Get_Sector_Size(flash->driver);
*sector_offset = search_offset - (search_offset%((off_t)(*sector_size)));
return 0;
}
static int xqspi_write_block_size(
rtems_flashdev *flash,
size_t *write_block_size
@@ -181,6 +203,8 @@ rtems_flashdev* xqspi_flash_init(XQspiPsu *xQspiDev)
flash->page_info_by_index = &xqspi_page_info_by_index;
flash->page_count = &xqspi_page_count;
flash->write_block_size = &xqspi_write_block_size;
flash->sector_count = &xqspi_sector_count;
flash->sector_info_by_offset = &xqspi_sector_info_by_off;
flash->region_table = ftable;
return flash;

View File

@@ -106,7 +106,7 @@ static uint32_t rtems_flashdev_ioctl_jedec_id(
rtems_flashdev *flash
);
static uint32_t rtems_flashdev_ioctl_flash_type(
static int rtems_flashdev_ioctl_flash_type(
rtems_flashdev *flash,
void *arg
);
@@ -728,7 +728,7 @@ static int rtems_flashdev_ioctl_erase(
int status;
if ( flash->erase == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
}
erase_args_1 = (rtems_flashdev_region *) arg;
@@ -884,14 +884,14 @@ static uint32_t rtems_flashdev_ioctl_jedec_id( rtems_flashdev *flash )
}
}
static uint32_t rtems_flashdev_ioctl_flash_type(
static int rtems_flashdev_ioctl_flash_type(
rtems_flashdev *flash,
void *arg
)
{
rtems_flashdev_flash_type *type = (rtems_flashdev_flash_type*)arg;
if ( flash->flash_type == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->flash_type )( flash, type );
}
@@ -908,7 +908,7 @@ static int rtems_flashdev_ioctl_pageinfo_offset(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->page_info_by_offset == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
page_info = (rtems_flashdev_ioctl_page_info *) arg;
return ( *flash->page_info_by_offset )( flash,
@@ -927,7 +927,7 @@ static int rtems_flashdev_ioctl_pageinfo_index( rtems_flashdev *flash,
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->page_info_by_index == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
page_info = (rtems_flashdev_ioctl_page_info *) arg;
return ( *flash->page_info_by_index )( flash,
@@ -943,7 +943,7 @@ static int rtems_flashdev_ioctl_page_count( rtems_flashdev *flash, void *arg )
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->page_count == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->page_count )( flash, ( (int *) arg ) );
}
@@ -958,7 +958,7 @@ static int rtems_flashdev_ioctl_write_block_size(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->write_block_size == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->write_block_size )( flash, ( (size_t *) arg ) );
}
@@ -975,7 +975,7 @@ static int rtems_flashdev_ioctl_sectorinfo_offset(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->sector_info_by_offset == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
sector_info = (rtems_flashdev_ioctl_sector_info *) arg;
return ( *flash->sector_info_by_offset )( flash,
@@ -991,7 +991,7 @@ static int rtems_flashdev_ioctl_sector_count( rtems_flashdev *flash, void *arg )
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->sector_count == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->sector_count )( flash, ( (int *) arg ) );
}
@@ -1042,7 +1042,7 @@ static int rtems_flashdev_ioctl_oob_bytes_per_page(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->oob_bytes_per_page == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->oob_bytes_per_page )( flash, ( (size_t *) arg ) );
}
@@ -1055,7 +1055,7 @@ static int rtems_flashdev_ioctl_oob_read( rtems_flashdev *flash, void *arg )
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->oob_read == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
rw_info = (rtems_flashdev_ioctl_oob_rw_info *) arg;
return ( *flash->oob_read )(
@@ -1154,7 +1154,7 @@ static int rtems_flashdev_ioctl_oob_write( rtems_flashdev *flash, void *arg )
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->oob_write == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
rw_info = (rtems_flashdev_ioctl_oob_rw_info *) arg;
return ( *flash->oob_write )(
@@ -1193,7 +1193,7 @@ static int rtems_flashdev_ioctl_sector_mark_bad(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->sector_mark_bad == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
return ( *flash->sector_mark_bad)( flash, *(uintptr_t *) arg );
}
@@ -1229,7 +1229,7 @@ static int rtems_flashdev_ioctl_sectorhealth(
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( flash->sector_health == NULL ) {
return 0;
rtems_set_errno_and_return_minus_one( ENOSYS );
} else {
sector_health = arg;
return ( *flash->sector_health)( flash, sector_health->location, &sector_health->sector_bad );

View File

@@ -48,6 +48,7 @@
#ifdef CONFIGURE_FILESYSTEM_ALL
#define CONFIGURE_FILESYSTEM_DOSFS
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_FILESYSTEM_FTPFS
#define CONFIGURE_FILESYSTEM_IMFS
#define CONFIGURE_FILESYSTEM_JFFS2
@@ -108,6 +109,10 @@
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_DOSFS"
#endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_FATFS"
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS
#error "CONFIGURE_APPLICATION_DISABLE_FILESYSTEM cannot be used together with CONFIGURE_FILESYSTEM_FTPFS"
#endif
@@ -139,6 +144,10 @@
#include <rtems/dosfs.h>
#endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
#include <rtems/fatfs.h>
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS
#include <rtems/ftpfs.h>
#endif
@@ -297,6 +306,9 @@ const rtems_filesystem_table_t rtems_filesystem_table[] = {
#ifdef CONFIGURE_FILESYSTEM_DOSFS
{ RTEMS_FILESYSTEM_TYPE_DOSFS, rtems_dosfs_initialize },
#endif
#ifdef CONFIGURE_FILESYSTEM_FATFS
{ RTEMS_FILESYSTEM_TYPE_FATFS, rtems_fatfs_initialize },
#endif
#ifdef CONFIGURE_FILESYSTEM_FTPFS
{ RTEMS_FILESYSTEM_TYPE_FTPFS, rtems_ftpfs_initialize },
#endif

View File

@@ -0,0 +1,60 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FatFS Filesystem Public API
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifndef _RTEMS_FATFS_H
#define _RTEMS_FATFS_H
#include <rtems.h>
#include <rtems/libio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup FATFS
*
* @{
*/
/**
* @brief RTEMS FatFS filesystem mount options.
*/
typedef struct {
/** Currently no options defined */
int reserved;
} rtems_fatfs_mount_options;
/**
* @brief Initialize FatFS filesystem.
*
* This function is the mount handler for the FatFS filesystem. It is called
* by the mount() system call when the filesystem type is
* RTEMS_FILESYSTEM_TYPE_FATFS.
*
* @param[in] mt_entry The mount table entry.
* @param[in] data Mount options (rtems_fatfs_mount_options or NULL).
*
* @return 0 on success, -1 on error with errno set.
*/
int rtems_fatfs_initialize(
rtems_filesystem_mount_table_entry_t *mt_entry,
const void *data
);
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* _RTEMS_FATFS_H */

View File

@@ -1649,6 +1649,7 @@ extern int rtems_mkdir(const char *path, mode_t mode);
#define RTEMS_FILESYSTEM_TYPE_TFTPFS "tftpfs"
#define RTEMS_FILESYSTEM_TYPE_NFS "nfs"
#define RTEMS_FILESYSTEM_TYPE_DOSFS "dosfs"
#define RTEMS_FILESYSTEM_TYPE_FATFS "fatfs"
#define RTEMS_FILESYSTEM_TYPE_RFS "rfs"
#define RTEMS_FILESYSTEM_TYPE_JFFS2 "jffs2"

View File

@@ -0,0 +1,222 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS Disk I/O Interface for FatFs
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/bdbuf.h>
#include <rtems/libio_.h>
#include "ff.h"
#include "diskio.h"
#include "rtems-fatfs.h"
static fatfs_volume_t volumes[ FF_VOLUMES ];
/*
* Compiler will change name to rtems_fatfs_disk_status
*/
DSTATUS disk_status( BYTE pdrv )
{
if ( pdrv >= FF_VOLUMES ) {
return STA_NOINIT;
}
if ( !volumes[ pdrv ].initialized ) {
return STA_NOINIT;
}
return 0;
}
/*
* Compiler will change name to rtems_fatfs_disk_initialize
*/
DSTATUS disk_initialize( BYTE pdrv )
{
if ( pdrv >= FF_VOLUMES ) {
return STA_NOINIT;
}
bool is_initialized = volumes[ pdrv ].initialized;
return is_initialized ? 0 : STA_NOINIT;
}
/*
* Compiler will change name to rtems_fatfs_disk_read
*/
DRESULT disk_read( BYTE pdrv, BYTE *buff, LBA_t sector, UINT count )
{
rtems_status_code sc;
rtems_bdbuf_buffer *bd;
uint32_t block_size;
if ( disk_status( pdrv ) != 0 ) {
return RES_NOTRDY;
}
rtems_disk_device *dd = volumes[ pdrv ].dd;
block_size = dd->block_size;
for ( size_t i = 0; i < count; i++ ) {
sc = rtems_bdbuf_read( dd, sector + i, &bd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
memcpy( buff + ( i * block_size ), bd->buffer, block_size );
sc = rtems_bdbuf_release( bd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
}
return RES_OK;
}
/*
* Compiler will change name to rtems_fatfs_disk_write
*/
DRESULT disk_write( BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count )
{
rtems_status_code sc;
rtems_bdbuf_buffer *bd;
uint32_t block_size;
if ( disk_status( pdrv ) != 0 ) {
return RES_NOTRDY;
}
rtems_disk_device *dd = volumes[ pdrv ].dd;
block_size = dd->block_size;
for ( size_t i = 0; i < count; i++ ) {
sc = rtems_bdbuf_get( dd, sector + i, &bd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
memcpy( bd->buffer, buff + ( i * block_size ), block_size );
sc = rtems_bdbuf_release_modified( bd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
}
return RES_OK;
}
/*
* Compiler will change name to rtems_fatfs_disk_ioctl
*/
DRESULT disk_ioctl( BYTE pdrv, BYTE cmd, void *buff )
{
if ( disk_status( pdrv ) != 0 ) {
return RES_NOTRDY;
}
rtems_disk_device *dd = volumes[ pdrv ].dd;
switch ( cmd ) {
case CTRL_SYNC: {
rtems_status_code sc = rtems_bdbuf_syncdev( dd );
if ( sc != RTEMS_SUCCESSFUL ) {
return RES_ERROR;
}
} break;
case GET_SECTOR_COUNT:
*(rtems_blkdev_bnum *) buff = dd->size;
break;
case GET_SECTOR_SIZE:
*(uint32_t *) buff = dd->media_block_size;
break;
case GET_BLOCK_SIZE:
*(uint32_t *) buff = dd->block_size;
break;
case CTRL_TRIM:
break;
default:
return RES_PARERR;
}
return RES_OK;
}
int fatfs_diskio_register_device( uint8_t pdrv, const char *device_path )
{
struct stat stat_buf;
int fd;
rtems_disk_device *dd;
int rc;
if ( pdrv >= FF_VOLUMES ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( volumes[ pdrv ].initialized ) {
return 0;
}
fd = open( device_path, O_RDWR );
if ( fd < 0 ) {
return -1;
}
rc = fstat( fd, &stat_buf );
if ( rc != 0 ) {
close( fd );
return -1;
}
if ( !S_ISBLK( stat_buf.st_mode ) ) {
close( fd );
rtems_set_errno_and_return_minus_one( ENXIO );
}
rc = rtems_disk_fd_get_disk_device( fd, &dd );
if ( rc != 0 ) {
close( fd );
return -1;
}
volumes[ pdrv ].fd = fd;
volumes[ pdrv ].dd = dd;
volumes[ pdrv ].initialized = true;
return 0;
}
void fatfs_diskio_unregister_device( uint8_t pdrv )
{
if ( pdrv >= FF_VOLUMES || !volumes[ pdrv ].initialized ) {
return;
}
close( volumes[ pdrv ].fd );
volumes[ pdrv ].fd = -1;
volumes[ pdrv ].dd = NULL;
volumes[ pdrv ].initialized = false;
}

View File

@@ -0,0 +1,207 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FATFS directory operations
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/libio_.h>
#include "ff.h"
#include "rtems-fatfs.h"
#define DIR POSIX_DIR
#include <dirent.h>
#undef DIR
ssize_t rtems_fatfs_dir_read( rtems_libio_t *iop, void *buffer, size_t count )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
struct dirent *dp = (struct dirent *) buffer;
FILINFO fno;
FRESULT fr;
size_t bytes_transferred = 0;
if ( node == NULL || !node->is_directory ) {
rtems_set_errno_and_return_minus_one( ENOTDIR );
}
if ( count < sizeof( struct dirent ) ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
memset( &fno, 0, sizeof( fno ) );
fr = f_readdir( &node->handle.dir, &fno );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( fno.fname[ 0 ] == '\0' ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
memset( dp, 0, sizeof( struct dirent ) );
dp->d_ino = 1;
dp->d_off = iop->offset;
dp->d_reclen = sizeof( struct dirent );
size_t name_len = strlen( fno.fname );
if ( name_len >= sizeof( dp->d_name ) ) {
name_len = sizeof( dp->d_name ) - 1;
}
memcpy( dp->d_name, fno.fname, name_len );
dp->d_name[ name_len ] = '\0';
dp->d_namlen = name_len;
if ( fno.fattrib & AM_DIR ) {
dp->d_type = DT_DIR;
} else {
dp->d_type = DT_REG;
}
bytes_transferred = sizeof( struct dirent );
iop->offset += sizeof( struct dirent );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return (ssize_t) bytes_transferred;
}
int rtems_fatfs_dir_fstat(
const rtems_filesystem_location_info_t *loc,
struct stat *buf
)
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
if ( node == NULL ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
loc->mt_entry->fs_info;
rtems_fatfs_node_to_stat( node, buf, fs_info );
return 0;
}
int rtems_fatfs_opendir(
rtems_libio_t *iop,
const char *path,
int oflag,
mode_t mode
)
{
(void) path;
(void) oflag;
(void) mode;
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
iop->pathinfo.mt_entry->fs_info;
char full_path[ 256 ];
FRESULT fr;
int rc;
if ( node == NULL || !node->is_directory ) {
rtems_set_errno_and_return_minus_one( ENOTDIR );
}
if ( node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
rc = rtems_fatfs_resolve_node_path(
fs_info,
node,
full_path,
sizeof( full_path ),
iop->pathinfo.mt_entry
);
if ( rc != 0 ) {
return rc;
}
fr = f_opendir( &node->handle.dir, full_path );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
node->is_open = true;
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
int rtems_fatfs_closedir( rtems_libio_t *iop )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
if ( node == NULL || !node->is_directory ) {
rtems_set_errno_and_return_minus_one( ENOTDIR );
}
if ( !node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
fr = f_closedir( &node->handle.dir );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
node->is_open = false;
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
const rtems_filesystem_file_handlers_r rtems_fatfs_dir_handlers = {
.open_h = rtems_fatfs_opendir,
.close_h = rtems_fatfs_closedir,
.read_h = rtems_fatfs_dir_read,
.write_h = rtems_filesystem_default_write,
.ioctl_h = rtems_filesystem_default_ioctl,
.lseek_h = rtems_filesystem_default_lseek_directory,
.fstat_h = rtems_fatfs_dir_fstat,
.ftruncate_h = rtems_filesystem_default_ftruncate_directory,
.fsync_h = rtems_fatfs_file_fsync,
.fdatasync_h = rtems_fatfs_file_fsync,
.fcntl_h = rtems_filesystem_default_fcntl,
.kqfilter_h = rtems_filesystem_default_kqfilter,
.mmap_h = rtems_filesystem_default_mmap,
.poll_h = rtems_filesystem_default_poll,
.readv_h = rtems_filesystem_default_readv,
.writev_h = rtems_filesystem_default_writev
};

View File

@@ -0,0 +1,451 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FATFS file operations
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/libio.h>
#include <rtems/libio_.h>
#include "ff.h"
#include "rtems-fatfs.h"
ssize_t rtems_fatfs_file_read( rtems_libio_t *iop, void *buffer, size_t count )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
UINT bytes_read = 0;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
fr = f_lseek( &node->handle.file, (FSIZE_t) iop->offset );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
fr = f_read( &node->handle.file, buffer, (UINT) count, &bytes_read );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( bytes_read > 0 ) {
iop->offset += bytes_read;
}
return (ssize_t) bytes_read;
}
ssize_t rtems_fatfs_file_write(
rtems_libio_t *iop,
const void *buffer,
size_t count
)
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
UINT bytes_written = 0;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
if ( rtems_libio_iop_is_append( iop ) ) {
fr = f_lseek( &node->handle.file, f_size( &node->handle.file ) );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
iop->offset = f_tell( &node->handle.file );
} else {
FSIZE_t current_size = f_size( &node->handle.file );
FSIZE_t target_pos = (FSIZE_t) iop->offset;
if ( target_pos > current_size ) {
FSIZE_t gap_size = target_pos - current_size;
char zero_buf[ 512 ];
UINT bw;
memset( zero_buf, 0, sizeof( zero_buf ) );
fr = f_lseek( &node->handle.file, current_size );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
while ( gap_size > 0 && fr == FR_OK ) {
UINT to_write = gap_size > sizeof( zero_buf ) ? sizeof( zero_buf ) :
(UINT) gap_size;
fr = f_write( &node->handle.file, zero_buf, to_write, &bw );
if ( fr == FR_OK && bw == to_write ) {
gap_size -= to_write;
} else {
break;
}
}
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( gap_size > 0 ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = ENOSPC;
return -1;
}
} else {
fr = f_lseek( &node->handle.file, target_pos );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
}
}
fr = f_write( &node->handle.file, buffer, (UINT) count, &bytes_written );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( bytes_written > 0 ) {
iop->offset += bytes_written;
node->info.fsize = f_size( &node->handle.file );
time_t current_time = time( NULL );
node->posix_mtime = current_time;
node->posix_ctime = current_time;
node->posix_time_valid = true;
} else if ( count > 0 && bytes_written == 0 ) {
errno = ENOSPC;
return -1;
}
return (ssize_t) bytes_written;
}
off_t rtems_fatfs_file_lseek( rtems_libio_t *iop, off_t offset, int whence )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FSIZE_t new_pos;
off_t result;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
switch ( whence ) {
case SEEK_SET:
if ( offset < 0 ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
new_pos = (FSIZE_t) offset;
break;
case SEEK_CUR:
if ( offset > 0 && iop->offset > LLONG_MAX - offset ) {
rtems_set_errno_and_return_minus_one( EOVERFLOW );
}
result = iop->offset + offset;
if ( result < 0 ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
new_pos = (FSIZE_t) result;
break;
case SEEK_END:
if ( offset > 0 && (off_t) node->info.fsize > LLONG_MAX - offset ) {
rtems_set_errno_and_return_minus_one( EOVERFLOW );
}
result = (off_t) node->info.fsize + offset;
if ( result < 0 ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
new_pos = (FSIZE_t) result;
break;
default:
rtems_set_errno_and_return_minus_one( EINVAL );
}
iop->offset = (off_t) new_pos;
return iop->offset;
}
int rtems_fatfs_file_fstat(
const rtems_filesystem_location_info_t *loc,
struct stat *buf
)
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
if ( node == NULL ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
loc->mt_entry->fs_info;
rtems_fatfs_node_to_stat( node, buf, fs_info );
return 0;
}
int rtems_fatfs_file_ftruncate( rtems_libio_t *iop, off_t length )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
FSIZE_t old_size;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EBADF );
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
old_size = f_size( &node->handle.file );
if ( (FSIZE_t) length > old_size ) {
FSIZE_t current_pos = f_tell( &node->handle.file );
FSIZE_t gap_size = (FSIZE_t) length - old_size;
char zero_buf[ 512 ];
UINT bw;
memset( zero_buf, 0, sizeof( zero_buf ) );
fr = f_lseek( &node->handle.file, old_size );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
while ( gap_size > 0 && fr == FR_OK ) {
UINT to_write = gap_size > sizeof( zero_buf ) ? sizeof( zero_buf ) :
(UINT) gap_size;
fr = f_write( &node->handle.file, zero_buf, to_write, &bw );
if ( fr == FR_OK && bw == to_write ) {
gap_size -= to_write;
} else {
break;
}
}
if ( fr == FR_OK ) {
if ( gap_size > 0 ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = ENOSPC;
return -1;
}
fr = f_lseek( &node->handle.file, current_pos );
}
} else {
fr = f_lseek( &node->handle.file, (FSIZE_t) length );
if ( fr == FR_OK ) {
fr = f_truncate( &node->handle.file );
}
}
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
node->info.fsize = (FSIZE_t) length;
return 0;
}
int rtems_fatfs_file_fsync( rtems_libio_t *iop )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
if ( node == NULL || node->is_directory || !node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
fr = f_sync( &node->handle.file );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
return 0;
}
int rtems_fatfs_openfile(
rtems_libio_t *iop,
const char *path,
int oflag,
mode_t mode
)
{
(void) path;
(void) mode;
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
iop->pathinfo.mt_entry->fs_info;
char full_path[ 256 ];
FRESULT fr;
int rc;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EISDIR );
}
if ( node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
rc = rtems_fatfs_resolve_node_path(
fs_info,
node,
full_path,
sizeof( full_path ),
iop->pathinfo.mt_entry
);
if ( rc != 0 ) {
return rc;
}
BYTE fatfs_mode = FA_READ;
if ( oflag & O_WRONLY ) {
fatfs_mode = FA_WRITE;
} else if ( oflag & O_RDWR ) {
fatfs_mode = FA_READ | FA_WRITE;
}
if ( oflag & O_CREAT ) {
if ( oflag & O_WRONLY ) {
fatfs_mode = FA_WRITE | FA_OPEN_EXISTING;
} else if ( oflag & O_RDWR ) {
fatfs_mode = FA_READ | FA_WRITE | FA_OPEN_EXISTING;
} else {
fatfs_mode = FA_READ | FA_OPEN_EXISTING;
}
}
fr = f_open( &node->handle.file, full_path, fatfs_mode );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
if ( oflag & O_TRUNC ) {
if ( f_size( &node->handle.file ) == 0 ) {
UINT bw;
char dummy = 0;
fr = f_write( &node->handle.file, &dummy, 1, &bw );
if ( fr == FR_OK ) {
fr = f_truncate( &node->handle.file );
}
} else {
fr = f_truncate( &node->handle.file );
}
if ( fr != FR_OK ) {
f_close( &node->handle.file );
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
f_sync( &node->handle.file );
}
node->is_open = true;
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
int rtems_fatfs_closefile( rtems_libio_t *iop )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) iop->pathinfo.node_access;
FRESULT fr;
if ( node == NULL || node->is_directory ) {
rtems_set_errno_and_return_minus_one( EISDIR );
}
if ( !node->is_open ) {
return 0;
}
rtems_fatfs_lock( iop->pathinfo.mt_entry );
fr = f_close( &node->handle.file );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
node->is_open = false;
rtems_fatfs_unlock( iop->pathinfo.mt_entry );
return 0;
}
const rtems_filesystem_file_handlers_r rtems_fatfs_file_handlers = {
.open_h = rtems_fatfs_openfile,
.close_h = rtems_fatfs_closefile,
.read_h = rtems_fatfs_file_read,
.write_h = rtems_fatfs_file_write,
.ioctl_h = rtems_filesystem_default_ioctl,
.lseek_h = rtems_fatfs_file_lseek,
.fstat_h = rtems_fatfs_file_fstat,
.ftruncate_h = rtems_fatfs_file_ftruncate,
.fsync_h = rtems_fatfs_file_fsync,
.fdatasync_h = rtems_fatfs_file_fsync,
.fcntl_h = rtems_filesystem_default_fcntl,
.kqfilter_h = rtems_filesystem_default_kqfilter,
.mmap_h = rtems_filesystem_default_mmap,
.poll_h = rtems_filesystem_default_poll,
.readv_h = rtems_filesystem_default_readv,
.writev_h = rtems_filesystem_default_writev
};

View File

@@ -0,0 +1,837 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FATFS initialization
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
#include <time.h>
#include <unistd.h>
#include <rtems.h>
#include <rtems/bdbuf.h>
#include <rtems/diskdevs.h>
#include <rtems/fatfs.h>
#include <rtems/libio.h>
#include <rtems/libio_.h>
#include <rtems/score/chain.h>
#include <rtems/seterr.h>
#include "ff.h"
#include "rtems-fatfs.h"
static uint8_t next_drive_number = 0;
static int rtems_fatfs_build_full_path(
const rtems_fatfs_fs_info_t *fs_info,
const rtems_fatfs_node_t *parent_node,
const char *name,
size_t namelen,
char *path,
size_t path_size
)
{
size_t mount_len = strlen( fs_info->mount_path );
size_t total_len;
char *write_pos = path;
if ( parent_node == NULL ) {
total_len = mount_len + namelen;
if ( total_len >= path_size ) {
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
memcpy( write_pos, fs_info->mount_path, mount_len );
write_pos += mount_len;
memcpy( write_pos, name, namelen );
write_pos += namelen;
*write_pos = '\0';
} else {
size_t parent_len = strlen( parent_node->path );
bool need_slash = ( strcmp( parent_node->path, "/" ) != 0 );
total_len = mount_len + parent_len + ( need_slash ? 1 : 0 ) + namelen;
if ( total_len >= path_size ) {
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
memcpy( write_pos, fs_info->mount_path, mount_len );
write_pos += mount_len;
memcpy( write_pos, parent_node->path, parent_len );
write_pos += parent_len;
if ( need_slash ) {
*write_pos = '/';
write_pos++;
}
memcpy( write_pos, name, namelen );
write_pos += namelen;
*write_pos = '\0';
}
char *dotdot;
while ( ( dotdot = strstr( path, "/.." ) ) != NULL ) {
if ( dotdot[ 3 ] == '\0' || dotdot[ 3 ] == '/' ) {
char *start = dotdot - 1;
while ( start > path && *start != '/' ) {
start--;
}
if ( start >= path && *start == '/' ) {
char *end = dotdot + 3;
memmove( start, end, strlen( end ) + 1 );
if ( start == path && *start == '\0' ) {
strcpy( path, "/" );
}
} else {
break;
}
} else {
break;
}
}
size_t len = strlen( path );
if ( len > 0 && len <= 2 && path[ len - 1 ] == ':' ) {
strcat( path, "/" );
}
return 0;
}
static void rtems_fatfs_cache_posix_times(
rtems_fatfs_fs_info_t *fs_info,
const char *path,
time_t mtime,
time_t ctime
)
{
int index = fs_info->posix_cache_next;
size_t path_len = strlen( path );
if ( path_len >= sizeof( fs_info->posix_cache[ index ].path ) ) {
path_len = sizeof( fs_info->posix_cache[ index ].path ) - 1;
}
memcpy( fs_info->posix_cache[ index ].path, path, path_len );
fs_info->posix_cache[ index ].path[ path_len ] = '\0';
fs_info->posix_cache[ index ].mtime = mtime;
fs_info->posix_cache[ index ].ctime = ctime;
fs_info->posix_cache[ index ].valid = true;
fs_info->posix_cache_next = ( fs_info->posix_cache_next + 1 ) %
RTEMS_FATFS_POSIX_CACHE_SIZE;
}
static void rtems_fatfs_set_parent_timestamps(
const rtems_fatfs_node_t *parent_node
)
{
if ( parent_node != NULL && parent_node->is_directory ) {
rtems_fatfs_node_t *mutable_parent = (rtems_fatfs_node_t *) parent_node;
time_t current_time = time( NULL );
mutable_parent->posix_mtime = current_time;
mutable_parent->posix_ctime = current_time;
mutable_parent->posix_time_valid = true;
}
}
int rtems_fatfs_initialize(
rtems_filesystem_mount_table_entry_t *mt_entry,
const void *data
)
{
(void) data;
rtems_fatfs_fs_info_t *fs_info = NULL;
rtems_fatfs_node_t *root_node = NULL;
FRESULT fr;
char drive_path[ 8 ];
int rc = -1;
fs_info = calloc( 1, sizeof( *fs_info ) );
if ( fs_info == NULL ) {
rtems_set_errno_and_return_minus_one( ENOMEM );
}
root_node = calloc( 1, sizeof( *root_node ) );
if ( root_node == NULL ) {
free( fs_info );
rtems_set_errno_and_return_minus_one( ENOMEM );
}
rtems_recursive_mutex_init(
&fs_info->vol_mutex,
RTEMS_FILESYSTEM_TYPE_FATFS
);
fs_info->drive_number = next_drive_number++;
if ( next_drive_number >= FF_VOLUMES ) {
next_drive_number = 0;
}
rc = fatfs_diskio_register_device( fs_info->drive_number, mt_entry->dev );
if ( rc != 0 ) {
rtems_recursive_mutex_destroy( &fs_info->vol_mutex );
free( root_node );
free( fs_info );
return -1;
}
drive_path[ 0 ] = '0' + fs_info->drive_number;
drive_path[ 1 ] = ':';
drive_path[ 2 ] = '\0';
fr = f_mount( &fs_info->fatfs, drive_path, 1 );
if ( fr != FR_OK ) {
fatfs_diskio_unregister_device( fs_info->drive_number );
rtems_recursive_mutex_destroy( &fs_info->vol_mutex );
free( root_node );
free( fs_info );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
fs_info->file_handlers = &rtems_fatfs_file_handlers;
fs_info->dir_handlers = &rtems_fatfs_dir_handlers;
strncpy( fs_info->mount_path, drive_path, sizeof( fs_info->mount_path ) - 1 );
fs_info->mount_path[ sizeof( fs_info->mount_path ) - 1 ] = '\0';
strcpy( fs_info->current_dir, "/" );
memset( fs_info->posix_cache, 0, sizeof( fs_info->posix_cache ) );
fs_info->posix_cache_next = 0;
root_node->is_directory = true;
root_node->is_open = false;
root_node->is_root_node = true;
root_node->ref_count = 1;
strcpy( root_node->path, "/" );
memset( &root_node->info, 0, sizeof( root_node->info ) );
root_node->info.fattrib = AM_DIR;
root_node->info.fsize = 0;
root_node->info.fdate = 0;
root_node->info.ftime = 0;
strcpy( root_node->info.fname, "/" );
mt_entry->fs_info = fs_info;
mt_entry->ops = &rtems_fatfs_ops;
mt_entry->mt_fs_root->location.node_access = root_node;
mt_entry->mt_fs_root->location.handlers = fs_info->dir_handlers;
return 0;
}
void rtems_fatfs_fsunmount_me( rtems_filesystem_mount_table_entry_t *mt_entry )
{
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *) mt_entry->fs_info;
rtems_fatfs_node_t *root_node = NULL;
if ( fs_info != NULL ) {
if ( mt_entry->mt_fs_root != NULL ) {
root_node = (rtems_fatfs_node_t *)
mt_entry->mt_fs_root->location.node_access;
}
f_mount( NULL, fs_info->mount_path, 1 );
fatfs_diskio_unregister_device( fs_info->drive_number );
if ( root_node != NULL ) {
free( root_node );
}
rtems_recursive_mutex_destroy( &fs_info->vol_mutex );
free( fs_info );
}
}
static rtems_fatfs_node_t *rtems_fatfs_node_get( rtems_fatfs_node_t *node )
{
if ( node != NULL ) {
node->ref_count++;
}
return node;
}
static void rtems_fatfs_node_put( rtems_fatfs_node_t *node )
{
if ( node != NULL ) {
if ( node->is_root_node ) {
node->ref_count--;
return;
}
node->ref_count--;
if ( node->ref_count <= 0 ) {
if ( node->is_open ) {
if ( node->is_directory ) {
f_closedir( &node->handle.dir );
} else {
f_close( &node->handle.file );
}
}
free( node );
}
}
}
static int rtems_fatfs_find_name(
rtems_filesystem_location_info_t *parentloc,
const char *name,
size_t name_len
)
{
rtems_fatfs_fs_info_t *fs_info = parentloc->mt_entry->fs_info;
rtems_fatfs_node_t *parent_node = parentloc->node_access;
rtems_fatfs_node_t *new_node = NULL;
FRESULT fr;
FILINFO fno;
char fatfs_path[ 256 ];
int ret;
ret = rtems_fatfs_build_full_path(
fs_info,
parent_node,
name,
name_len,
fatfs_path,
sizeof( fatfs_path )
);
if ( ret != 0 ) {
return ret;
}
if ( strlen( fatfs_path ) == 3 && fatfs_path[ 1 ] == ':' &&
fatfs_path[ 2 ] == '/' ) {
rtems_filesystem_mount_table_entry_t *mt_entry = parentloc->mt_entry;
if ( mt_entry && mt_entry->mt_fs_root ) {
new_node = (rtems_fatfs_node_t *)
mt_entry->mt_fs_root->location.node_access;
if ( new_node ) {
rtems_fatfs_node_get( new_node );
rtems_fatfs_node_put( parent_node );
parentloc->node_access = new_node;
return 0;
}
}
rtems_set_errno_and_return_minus_one( ENOENT );
}
fr = f_stat( fatfs_path, &fno );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
new_node = malloc( sizeof( *new_node ) );
if ( new_node == NULL ) {
rtems_set_errno_and_return_minus_one( ENOMEM );
}
memset( new_node, 0, sizeof( *new_node ) );
new_node->info = fno;
new_node->is_directory = ( fno.fattrib & AM_DIR ) != 0;
new_node->is_open = false;
new_node->is_root_node = false;
new_node->ref_count = 1;
size_t total_len;
if ( strcmp( parent_node->path, "/" ) == 0 ) {
total_len = 1 + name_len; // "/" + name
if ( total_len >= sizeof( new_node->path ) ) {
free( new_node );
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
new_node->path[ 0 ] = '/';
memcpy( new_node->path + 1, name, name_len );
new_node->path[ total_len ] = '\0';
} else {
size_t parent_len = strlen( parent_node->path );
total_len = parent_len + 1 + name_len; // parent + "/" + name
if ( total_len >= sizeof( new_node->path ) ) {
free( new_node );
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
memcpy( new_node->path, parent_node->path, parent_len );
new_node->path[ parent_len ] = '/';
memcpy( new_node->path + parent_len + 1, name, name_len );
new_node->path[ total_len ] = '\0';
}
rtems_fatfs_node_put( parent_node );
parentloc->node_access = new_node;
return 0;
}
static void rtems_fatfs_set_handlers( rtems_filesystem_location_info_t *loc )
{
rtems_fatfs_fs_info_t *fs_info = loc->mt_entry->fs_info;
rtems_fatfs_node_t *node = loc->node_access;
if ( node->is_directory ) {
loc->handlers = fs_info->dir_handlers;
} else {
loc->handlers = fs_info->file_handlers;
}
}
static bool rtems_fatfs_eval_is_directory(
rtems_filesystem_eval_path_context_t *ctx,
void *arg
)
{
(void) arg;
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) ctx->currentloc.node_access;
return node != NULL && node->is_directory;
}
static rtems_filesystem_eval_path_generic_status rtems_fatfs_eval_token(
rtems_filesystem_eval_path_context_t *ctx,
void *arg,
const char *token,
size_t tokenlen
)
{
(void) arg;
rtems_filesystem_eval_path_generic_status
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
if ( rtems_filesystem_is_current_directory( token, tokenlen ) ) {
rtems_filesystem_eval_path_clear_token( ctx );
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
} else {
rtems_filesystem_location_info_t
*currentloc = rtems_filesystem_eval_path_get_currentloc( ctx );
int rc = rtems_fatfs_find_name( currentloc, token, tokenlen );
if ( rc == 0 ) {
rtems_filesystem_eval_path_clear_token( ctx );
rtems_fatfs_set_handlers( currentloc );
if ( rtems_filesystem_eval_path_has_path( ctx ) ) {
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *)
currentloc->node_access;
if ( node != NULL && !node->is_directory ) {
rtems_filesystem_eval_path_error( ctx, ENOTDIR );
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
} else {
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_CONTINUE;
}
} else {
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_DONE;
}
} else {
if ( errno == ENOENT ) {
status = RTEMS_FILESYSTEM_EVAL_PATH_GENERIC_NO_ENTRY;
} else {
rtems_filesystem_eval_path_error( ctx, errno );
}
}
}
return status;
}
static const rtems_filesystem_eval_path_generic_config rtems_fatfs_eval_config =
{ .is_directory = rtems_fatfs_eval_is_directory,
.eval_token = rtems_fatfs_eval_token };
void rtems_fatfs_eval_path( rtems_filesystem_eval_path_context_t *ctx )
{
rtems_filesystem_eval_path_generic( ctx, NULL, &rtems_fatfs_eval_config );
}
int rtems_fatfs_mknod(
const rtems_filesystem_location_info_t *parentloc,
const char *name,
size_t namelen,
mode_t mode,
dev_t dev
)
{
char full_path[ 256 ];
FRESULT fr;
FIL fil;
int rc;
(void) dev;
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
parentloc->mt_entry->fs_info;
rtems_fatfs_node_t *parent_node = (rtems_fatfs_node_t *)
parentloc->node_access;
rc = rtems_fatfs_build_full_path(
fs_info,
parent_node,
name,
namelen,
full_path,
sizeof( full_path )
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_lock( parentloc->mt_entry );
if ( S_ISDIR( mode ) ) {
fr = f_mkdir( full_path );
if ( fr == FR_OK ) {
rtems_fatfs_set_parent_timestamps( parent_node );
}
} else if ( S_ISREG( mode ) ) {
fr = f_open( &fil, full_path, FA_CREATE_NEW | FA_WRITE );
if ( fr == FR_OK ) {
f_close( &fil );
rtems_fatfs_set_parent_timestamps( parent_node );
}
} else {
rtems_fatfs_unlock( parentloc->mt_entry );
rtems_set_errno_and_return_minus_one( ENOTSUP );
}
rtems_fatfs_unlock( parentloc->mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
return 0;
}
int rtems_fatfs_rmnod(
const rtems_filesystem_location_info_t *parentloc,
const rtems_filesystem_location_info_t *loc
)
{
rtems_fatfs_fs_info_t *fs_info = NULL;
rtems_fatfs_node_t *node = NULL;
char full_path[ 256 ];
FRESULT fr;
int rc;
fs_info = (rtems_fatfs_fs_info_t *) parentloc->mt_entry->fs_info;
node = (rtems_fatfs_node_t *) loc->node_access;
if ( node == NULL ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
if ( node->is_open && !node->is_directory ) {
f_close( &node->handle.file );
node->is_open = false;
}
rc = rtems_fatfs_resolve_node_path(
fs_info,
node,
full_path,
sizeof( full_path ),
NULL
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_lock( parentloc->mt_entry );
fr = f_unlink( full_path );
rtems_fatfs_unlock( parentloc->mt_entry );
if ( fr != FR_OK ) {
if ( ( fr == FR_DENIED || fr == FR_INVALID_NAME ) && node->is_directory ) {
errno = ENOTEMPTY;
} else {
errno = rtems_fatfs_fresult_to_errno( fr );
}
return -1;
}
return 0;
}
bool rtems_fatfs_are_nodes_equal(
const rtems_filesystem_location_info_t *a,
const rtems_filesystem_location_info_t *b
)
{
rtems_fatfs_node_t *node_a = (rtems_fatfs_node_t *) a->node_access;
rtems_fatfs_node_t *node_b = (rtems_fatfs_node_t *) b->node_access;
if ( node_a == NULL || node_b == NULL ) {
return false;
}
return strcmp( node_a->path, node_b->path ) == 0;
}
int rtems_fatfs_clone_node( rtems_filesystem_location_info_t *loc )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
if ( node != NULL ) {
rtems_fatfs_node_get( node );
}
return 0;
}
void rtems_fatfs_free_node( const rtems_filesystem_location_info_t *loc )
{
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
if ( node != NULL ) {
rtems_fatfs_node_put( node );
}
}
int rtems_fatfs_rename(
const rtems_filesystem_location_info_t *old_parent_loc,
const rtems_filesystem_location_info_t *old_loc,
const rtems_filesystem_location_info_t *new_parent_loc,
const char *new_name,
size_t new_namelen
)
{
rtems_fatfs_fs_info_t *fs_info = NULL;
rtems_fatfs_node_t *old_node = NULL;
char old_path[ 256 ];
char new_path[ 256 ];
FRESULT fr;
int rc;
fs_info = (rtems_fatfs_fs_info_t *) old_parent_loc->mt_entry->fs_info;
old_node = (rtems_fatfs_node_t *) old_loc->node_access;
if ( old_node == NULL ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
rc = rtems_fatfs_resolve_node_path(
fs_info,
old_node,
old_path,
sizeof( old_path ),
NULL
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_fs_info_t *new_fs_info = (rtems_fatfs_fs_info_t *)
new_parent_loc->mt_entry->fs_info;
rtems_fatfs_node_t *new_parent_node = (rtems_fatfs_node_t *)
new_parent_loc->node_access;
rc = rtems_fatfs_build_full_path(
new_fs_info,
new_parent_node,
new_name,
new_namelen,
new_path,
sizeof( new_path )
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_lock( old_parent_loc->mt_entry );
fr = f_rename( old_path, new_path );
rtems_fatfs_unlock( old_parent_loc->mt_entry );
if ( fr != FR_OK ) {
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
return 0;
}
static void rtems_fatfs_update_fat_timestamp(
const char *path,
time_t timestamp
)
{
struct tm *tm_info = localtime( &timestamp );
if ( tm_info != NULL ) {
FILINFO fno;
memset( &fno, 0, sizeof( fno ) );
fno.fdate = ( ( tm_info->tm_year + 1900 - 1980 ) << 9 ) |
( ( tm_info->tm_mon + 1 ) << 5 ) | ( tm_info->tm_mday );
fno.ftime = ( tm_info->tm_hour << 11 ) | ( tm_info->tm_min << 5 ) |
( tm_info->tm_sec >> 1 );
f_utime( path, &fno );
}
}
int rtems_fatfs_utimens(
const rtems_filesystem_location_info_t *loc,
struct timespec times[ 2 ]
)
{
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *)
loc->mt_entry->fs_info;
rtems_fatfs_node_t *node = (rtems_fatfs_node_t *) loc->node_access;
char full_path[ 256 ];
int rc;
if ( node == NULL ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
time_t mtime = times[ 1 ].tv_sec;
time_t current_time = time( NULL );
rc = rtems_fatfs_resolve_node_path(
fs_info,
node,
full_path,
sizeof( full_path ),
NULL
);
if ( rc != 0 ) {
return rc;
}
rtems_fatfs_lock( loc->mt_entry );
rtems_fatfs_update_fat_timestamp( full_path, mtime );
rtems_fatfs_unlock( loc->mt_entry );
rtems_fatfs_cache_posix_times( fs_info, node->path, mtime, current_time );
node->posix_mtime = mtime;
node->posix_ctime = current_time;
node->posix_time_valid = true;
return 0;
}
/*
* Compiler will change name to rtems_fatfs_get_fattime
*/
DWORD get_fattime( void )
{
time_t current_time;
struct tm *tm_info;
DWORD fat_time = 0;
current_time = time( NULL );
tm_info = localtime( &current_time );
if ( tm_info != NULL ) {
fat_time = ( (DWORD) ( tm_info->tm_year + 1900 - 1980 ) << 25 ) |
( (DWORD) ( tm_info->tm_mon + 1 ) << 21 ) |
( (DWORD) tm_info->tm_mday << 16 ) |
( (DWORD) tm_info->tm_hour << 11 ) |
( (DWORD) tm_info->tm_min << 5 ) |
( (DWORD) tm_info->tm_sec >> 1 );
} else {
fat_time = ( 0 << 25 ) | ( 1 << 21 ) | ( 1 << 16 );
}
return fat_time;
}
int rtems_fatfs_statvfs(
const rtems_filesystem_location_info_t *root_loc,
struct statvfs *sb
)
{
rtems_fatfs_fs_info_t *fs_info = root_loc->mt_entry->fs_info;
FATFS *fs = &fs_info->fatfs;
FRESULT fr;
DWORD free_clusters;
FATFS *fatfs_ptr;
char drive_path[ 8 ];
if ( fs_info == NULL || sb == NULL ) {
rtems_set_errno_and_return_minus_one( EINVAL );
}
rtems_fatfs_lock( root_loc->mt_entry );
drive_path[ 0 ] = '0' + fs_info->drive_number;
drive_path[ 1 ] = ':';
drive_path[ 2 ] = '\0';
fr = f_getfree( drive_path, &free_clusters, &fatfs_ptr );
if ( fr != FR_OK ) {
rtems_fatfs_unlock( root_loc->mt_entry );
errno = rtems_fatfs_fresult_to_errno( fr );
return -1;
}
#if FF_MAX_SS != FF_MIN_SS
WORD sector_size = fs->ssize;
#else
WORD sector_size = FF_MAX_SS;
#endif
sb->f_bsize = sector_size;
sb->f_frsize = fs->csize * sector_size;
sb->f_blocks = fs->n_fatent - 2;
sb->f_bfree = free_clusters;
sb->f_bavail = free_clusters;
sb->f_files = 0;
sb->f_ffree = 0;
sb->f_favail = 0;
sb->f_flag = 0;
#if FF_USE_LFN
sb->f_namemax = FF_LFN_BUF - 1;
#else
sb->f_namemax = 12;
#endif
rtems_fatfs_unlock( root_loc->mt_entry );
return 0;
}
const rtems_filesystem_operations_table rtems_fatfs_ops = {
.lock_h = rtems_fatfs_lock,
.unlock_h = rtems_fatfs_unlock,
.eval_path_h = rtems_fatfs_eval_path,
.link_h = rtems_filesystem_default_link,
.are_nodes_equal_h = rtems_fatfs_are_nodes_equal,
.mknod_h = rtems_fatfs_mknod,
.rmnod_h = rtems_fatfs_rmnod,
.fchmod_h = rtems_filesystem_default_fchmod,
.chown_h = rtems_filesystem_default_chown,
.clonenod_h = rtems_fatfs_clone_node,
.freenod_h = rtems_fatfs_free_node,
.mount_h = rtems_fatfs_initialize,
.unmount_h = rtems_filesystem_default_unmount,
.fsunmount_me_h = rtems_fatfs_fsunmount_me,
.utimens_h = rtems_fatfs_utimens,
.symlink_h = rtems_filesystem_default_symlink,
.readlink_h = rtems_filesystem_default_readlink,
.rename_h = rtems_fatfs_rename,
.statvfs_h = rtems_fatfs_statvfs
};

View File

@@ -0,0 +1,372 @@
/**
* @file
*
* @ingroup FatFS
*
* @brief RTEMS FatFS headers
*/
/*
Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*/
#ifndef RTEMS_FATFS_H
#define RTEMS_FATFS_H
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <rtems/diskdevs.h>
#include <rtems/libio.h>
#include <rtems/seterr.h>
#include "ff.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DT_REG 8
#define DT_DIR 4
typedef struct {
int fd;
rtems_disk_device *dd;
bool initialized;
} fatfs_volume_t;
#define RTEMS_FATFS_POSIX_CACHE_SIZE 64
typedef struct {
char path[ 256 ];
time_t mtime;
time_t ctime;
bool valid;
} rtems_fatfs_posix_cache_entry_t;
typedef struct {
FATFS fatfs;
rtems_recursive_mutex vol_mutex;
const rtems_filesystem_file_handlers_r *file_handlers;
const rtems_filesystem_file_handlers_r *dir_handlers;
uint8_t drive_number;
char mount_path[ 256 ];
char current_dir[ 256 ];
rtems_fatfs_posix_cache_entry_t posix_cache[ RTEMS_FATFS_POSIX_CACHE_SIZE ];
int posix_cache_next;
} rtems_fatfs_fs_info_t;
typedef struct {
union {
FIL file;
DIR dir;
} handle;
FILINFO info;
bool is_directory;
bool is_open;
char path[ 256 ];
bool is_root_node;
int ref_count;
time_t posix_mtime;
time_t posix_ctime;
bool posix_time_valid;
} rtems_fatfs_node_t;
/* Disk I/O interface */
int fatfs_diskio_register_device( uint8_t pdrv, const char *device_path );
void fatfs_diskio_unregister_device( uint8_t pdrv );
/* Utility functions */
static inline int rtems_fatfs_get_full_path(
const rtems_fatfs_fs_info_t *fs_info,
const char *relative_path,
char *full_path,
size_t path_size
)
{
size_t mount_len = strlen( fs_info->mount_path );
size_t rel_len = strlen( relative_path );
if ( mount_len + rel_len >= path_size ) {
rtems_set_errno_and_return_minus_one( ENAMETOOLONG );
}
memcpy( full_path, fs_info->mount_path, mount_len );
memcpy( full_path + mount_len, relative_path, rel_len + 1 );
return 0;
}
static inline int rtems_fatfs_fresult_to_errno( FRESULT fr )
{
switch ( fr ) {
case FR_OK:
return 0;
case FR_DISK_ERR:
case FR_INT_ERR:
case FR_NOT_READY:
return EIO;
case FR_NO_FILE:
case FR_NO_PATH:
return ENOENT;
case FR_INVALID_NAME:
return EINVAL;
case FR_DENIED:
return EACCES;
case FR_EXIST:
return EEXIST;
case FR_INVALID_OBJECT:
return EBADF;
case FR_WRITE_PROTECTED:
return EROFS;
case FR_INVALID_DRIVE:
case FR_NOT_ENABLED:
case FR_NO_FILESYSTEM:
return ENXIO;
case FR_MKFS_ABORTED:
return EINVAL;
case FR_TIMEOUT:
return ETIMEDOUT;
case FR_LOCKED:
return EBUSY;
case FR_NOT_ENOUGH_CORE:
return ENOMEM;
case FR_TOO_MANY_OPEN_FILES:
return EMFILE;
case FR_INVALID_PARAMETER:
return EINVAL;
default:
return EIO;
}
}
static inline void rtems_fatfs_unlock(
const rtems_filesystem_mount_table_entry_t *mt_entry
)
{
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *) mt_entry->fs_info;
rtems_recursive_mutex_unlock( &fs_info->vol_mutex );
}
static inline int rtems_fatfs_resolve_node_path(
const rtems_fatfs_fs_info_t *fs_info,
const rtems_fatfs_node_t *node,
char *full_path,
size_t path_size,
const rtems_filesystem_mount_table_entry_t *mt_entry
)
{
int
rc = rtems_fatfs_get_full_path( fs_info, node->path, full_path, path_size );
if ( rc != 0 && mt_entry != NULL ) {
rtems_fatfs_unlock( mt_entry );
}
return rc;
}
static inline void rtems_fatfs_filinfo_to_stat(
const FILINFO *fno,
struct stat *st
)
{
const char *name = fno->fname;
ino_t inode_hash = 1;
memset( st, 0, sizeof( *st ) );
while ( *name != '\0' ) {
inode_hash = ( inode_hash * 33 ) + (unsigned char) *name;
name++;
}
inode_hash ^= fno->fsize ^ ( fno->fdate << 16 ) ^ fno->ftime;
st->st_ino = ( inode_hash != 0 ) ? inode_hash : 1;
st->st_dev = 1;
st->st_size = (off_t) fno->fsize;
st->st_blksize = 512;
st->st_blocks = ( st->st_size + 511 ) / 512;
if ( fno->fdate != 0 || fno->ftime != 0 ) {
struct tm tm;
memset( &tm, 0, sizeof( tm ) );
tm.tm_year = ( ( fno->fdate >> 9 ) & 0x7F ) + 80;
tm.tm_mon = ( ( fno->fdate >> 5 ) & 0x0F ) - 1;
tm.tm_mday = fno->fdate & 0x1F;
tm.tm_hour = ( fno->ftime >> 11 ) & 0x1F;
tm.tm_min = ( fno->ftime >> 5 ) & 0x3F;
tm.tm_sec = ( fno->ftime & 0x1F ) * 2;
st->st_mtime = mktime( &tm );
st->st_ctime = st->st_mtime;
st->st_atime = st->st_mtime;
}
if ( fno->fattrib & AM_DIR ) {
st->st_mode = S_IFDIR | 0755;
} else {
st->st_mode = S_IFREG | 0644;
}
if ( fno->fattrib & AM_RDO ) {
st->st_mode &= ~( S_IWUSR | S_IWGRP | S_IWOTH );
}
}
static inline void rtems_fatfs_node_to_stat_basic(
const rtems_fatfs_node_t *node,
struct stat *st
)
{
rtems_fatfs_filinfo_to_stat( &node->info, st );
}
static inline void rtems_fatfs_node_to_stat(
const rtems_fatfs_node_t *node,
struct stat *st,
rtems_fatfs_fs_info_t *fs_info
)
{
rtems_fatfs_filinfo_to_stat( &node->info, st );
if ( fs_info != NULL ) {
blksize_t cluster_size = fs_info->fatfs.csize * 512;
st->st_blksize = cluster_size;
blkcnt_t clusters_used = ( st->st_size + cluster_size - 1 ) / cluster_size;
st->st_blocks = clusters_used * fs_info->fatfs.csize;
}
if ( node->posix_time_valid ) {
st->st_mtime = node->posix_mtime;
st->st_ctime = node->posix_ctime;
st->st_atime = node->posix_mtime;
} else if ( fs_info != NULL ) {
for ( int i = 0; i < RTEMS_FATFS_POSIX_CACHE_SIZE; i++ ) {
if ( fs_info->posix_cache[ i ].valid &&
strcmp( fs_info->posix_cache[ i ].path, node->path ) == 0 ) {
st->st_mtime = fs_info->posix_cache[ i ].mtime;
st->st_ctime = fs_info->posix_cache[ i ].ctime;
st->st_atime = fs_info->posix_cache[ i ].mtime;
break;
}
}
}
}
static inline void rtems_fatfs_lock(
const rtems_filesystem_mount_table_entry_t *mt_entry
)
{
rtems_fatfs_fs_info_t *fs_info = (rtems_fatfs_fs_info_t *) mt_entry->fs_info;
rtems_recursive_mutex_lock( &fs_info->vol_mutex );
}
/* Filesystem operations - declared in rtems-fatfs-init.c */
int rtems_fatfs_initialize(
rtems_filesystem_mount_table_entry_t *mt_entry,
const void *data
);
void rtems_fatfs_fsunmount_me( rtems_filesystem_mount_table_entry_t *mt_entry );
void rtems_fatfs_eval_path( rtems_filesystem_eval_path_context_t *ctx );
int rtems_fatfs_mknod(
const rtems_filesystem_location_info_t *parentloc,
const char *name,
size_t namelen,
mode_t mode,
dev_t dev
);
int rtems_fatfs_rmnod(
const rtems_filesystem_location_info_t *parentloc,
const rtems_filesystem_location_info_t *loc
);
int rtems_fatfs_rename(
const rtems_filesystem_location_info_t *old_parent_loc,
const rtems_filesystem_location_info_t *old_loc,
const rtems_filesystem_location_info_t *new_parent_loc,
const char *new_name,
size_t new_namelen
);
int rtems_fatfs_utimens(
const rtems_filesystem_location_info_t *loc,
struct timespec times[ 2 ]
);
int rtems_fatfs_statvfs(
const rtems_filesystem_location_info_t *root_loc,
struct statvfs *sb
);
bool rtems_fatfs_are_nodes_equal(
const rtems_filesystem_location_info_t *a,
const rtems_filesystem_location_info_t *b
);
int rtems_fatfs_clone_node( rtems_filesystem_location_info_t *loc );
void rtems_fatfs_free_node( const rtems_filesystem_location_info_t *loc );
/* File operations - declared in rtems-fatfs-file.c */
ssize_t rtems_fatfs_file_read( rtems_libio_t *iop, void *buffer, size_t count );
ssize_t rtems_fatfs_file_write(
rtems_libio_t *iop,
const void *buffer,
size_t count
);
off_t rtems_fatfs_file_lseek( rtems_libio_t *iop, off_t offset, int whence );
int rtems_fatfs_file_fstat(
const rtems_filesystem_location_info_t *loc,
struct stat *buf
);
int rtems_fatfs_file_ftruncate( rtems_libio_t *iop, off_t length );
int rtems_fatfs_file_fsync( rtems_libio_t *iop );
/* Directory operations - declared in rtems-fatfs-dir.c */
ssize_t rtems_fatfs_dir_read( rtems_libio_t *iop, void *buffer, size_t count );
int rtems_fatfs_dir_fstat(
const rtems_filesystem_location_info_t *loc,
struct stat *buf
);
int rtems_fatfs_opendir(
rtems_libio_t *iop,
const char *path,
int oflag,
mode_t mode
);
int rtems_fatfs_closedir( rtems_libio_t *iop );
int rtems_fatfs_openfile(
rtems_libio_t *iop,
const char *path,
int oflag,
mode_t mode
);
int rtems_fatfs_closefile( rtems_libio_t *iop );
/* Handler tables */
extern const rtems_filesystem_operations_table rtems_fatfs_ops;
extern const rtems_filesystem_file_handlers_r rtems_fatfs_file_handlers;
extern const rtems_filesystem_file_handlers_r rtems_fatfs_dir_handlers;
#ifdef __cplusplus
}
#endif
#endif /* RTEMS_FATFS_H */

View File

@@ -55,6 +55,9 @@ install:
- bsps/include/libchip/spi-memdrv.h
- bsps/include/libchip/spi-sd-card.h
- bsps/include/libchip/wd80x3.h
- destination: ${BSP_INCLUDEDIR}/dev/nor
source:
- bsps/include/dev/nor/config-parser.h
- destination: ${BSP_INCLUDEDIR}/dev/flash
source:
- bsps/include/dev/flash/jffs2_flashdev.h
@@ -89,6 +92,7 @@ source:
- bsps/shared/dev/ide/ata.c
- bsps/shared/dev/ide/ata_util.c
- bsps/shared/dev/ide/ide_controller.c
- bsps/shared/dev/nor/config-parser.c
- bsps/shared/dev/rtc/abeoz9.c
- bsps/shared/dev/rtc/ds1375.c
- bsps/shared/dev/rtc/i2c-rtc.c

View File

@@ -4,7 +4,30 @@ cflags:
- ${COVERAGE_COMPILER_FLAGS}
copyrights:
- Copyright (C) 2020 embedded brains GmbH & Co. KG
cppflags: []
cppflags:
- -Ddisk_status=rtems_fatfs_disk_status
- -Ddisk_initialize=rtems_fatfs_disk_initialize
- -Ddisk_read=rtems_fatfs_disk_read
- -Ddisk_write=rtems_fatfs_disk_write
- -Ddisk_ioctl=rtems_fatfs_disk_ioctl
- -Dget_fattime=rtems_fatfs_get_fattime
- -DFFCONF_DEF=5385
- -DFF_VOLUMES=1
- -DFF_USE_MKFS=1
- -DFF_USE_CHMOD=1
- -DFF_USE_LFN=1
- -DFF_FS_RPATH=2
- -DFF_FS_EXFAT=1
- -DFF_MAX_SS=512
- -DFF_MIN_SS=512
- -DFF_LFN_BUF=255
- -DFF_SFN_BUF=12
- -DFF_MAX_LFN=255
- -DFF_USE_LABEL=0
- -DFF_STR_VOLUME_ID=0
- -DFF_MULTI_PARTITION=0
- -DFF_FS_READONLY=0
- -DFF_LBA64=0
cxxflags:
- ${COVERAGE_COMPILER_FLAGS}
enabled-by: true
@@ -118,6 +141,7 @@ install:
- cpukit/include/rtems/devzero.h
- cpukit/include/rtems/diskdevs.h
- cpukit/include/rtems/dosfs.h
- cpukit/include/rtems/fatfs.h
- cpukit/include/rtems/dumpbuf.h
- cpukit/include/rtems/endian.h
- cpukit/include/rtems/error.h
@@ -861,6 +885,13 @@ source:
- cpukit/libfs/src/dosfs/msdos_rename.c
- cpukit/libfs/src/dosfs/msdos_rmnod.c
- cpukit/libfs/src/dosfs/msdos_statvfs.c
- cpukit/libfs/src/fatfs/ff.c
- cpukit/libfs/src/fatfs/ffunicode.c
- cpukit/libfs/src/fatfs/ffsystem.c
- cpukit/libfs/src/fatfs/rtems-diskio.c
- cpukit/libfs/src/fatfs/rtems-fatfs-init.c
- cpukit/libfs/src/fatfs/rtems-fatfs-file.c
- cpukit/libfs/src/fatfs/rtems-fatfs-dir.c
- cpukit/libfs/src/imfs/deviceio.c
- cpukit/libfs/src/imfs/imfs_add_node.c
- cpukit/libfs/src/imfs/imfs_chown.c

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsclose01/init.c
stlib: []
target: testsuites/fstests/fatfs_fsclose01.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fserror/test.c
stlib: []
target: testsuites/fstests/fatfs_fserror.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fspatheval/test.c
stlib: []
target: testsuites/fstests/fatfs_fspatheval.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrdwr/init.c
stlib: []
target: testsuites/fstests/fatfs_fsrdwr.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrename/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrename.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,22 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags:
- -DTEST_STATE_EXPECTED_FAIL=1
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrenameexisting/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrenameexisting.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrenamelongname/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrenamelongname.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,22 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags:
- -DTEST_STATE_EXPECTED_FAIL=1
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrenamemaxlinks/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrenamemaxlinks.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsrmdirparent/test.c
stlib: []
target: testsuites/fstests/fatfs_fsrmdirparent.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsscandir01/init.c
stlib: []
target: testsuites/fstests/fatfs_fsscandir01.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fsstatvfs/test.c
stlib: []
target: testsuites/fstests/fatfs_fsstatvfs.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,21 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes:
- testsuites/fstests/fatfs_support
ldflags: []
links: []
source:
- testsuites/fstests/fstime/test.c
stlib: []
target: testsuites/fstests/fatfs_fstime.exe
type: build
use-after: []
use-before:
- testfatfs

View File

@@ -0,0 +1,19 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes: []
ldflags: []
links: []
source:
- testsuites/fstests/fsfatfsformat01/init.c
stlib: []
target: testsuites/fstests/fsfatfsformat01.exe
type: build
use-after: []
use-before: []

View File

@@ -0,0 +1,19 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes: []
ldflags: []
links: []
source:
- testsuites/fstests/fsfatfssync01/init.c
stlib: []
target: testsuites/fstests/fsfatfssync01.exe
type: build
use-after: []
use-before: []

View File

@@ -27,6 +27,8 @@ links:
uid: libmimfs
- role: build-dependency
uid: librfs
- role: build-dependency
uid: libfatfs
- role: build-dependency
uid: fsbdpart01
- role: build-dependency
@@ -41,6 +43,8 @@ links:
uid: fsdosfssync01
- role: build-dependency
uid: fsdosfswrite01
- role: build-dependency
uid: fsfatfssync01
- role: build-dependency
uid: fsfseeko01
- role: build-dependency
@@ -221,6 +225,32 @@ links:
uid: mrfsfstime
- role: build-dependency
uid: tftpfs
- role: build-dependency
uid: fsfatfsformat01
- role: build-dependency
uid: fatfsfsrdwr
- role: build-dependency
uid: fatfsfserror
- role: build-dependency
uid: fatfsfspatheval
- role: build-dependency
uid: fatfsfsrename
- role: build-dependency
uid: fatfsfsrenameexisting
- role: build-dependency
uid: fatfsfsrenamelongname
- role: build-dependency
uid: fatfsfsrenamemaxlinks
- role: build-dependency
uid: fatfsfsrmdirparent
- role: build-dependency
uid: fatfsfsscandir01
- role: build-dependency
uid: fatfsfsstatvfs
- role: build-dependency
uid: fatfsfstime
- role: build-dependency
uid: fatfsfsclose01
type: build
use-after: []
use-before:

View File

@@ -0,0 +1,19 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: library
cflags: []
copyrights:
- Copyright (C) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
cppflags: []
cxxflags: []
enabled-by: true
includes:
- testsuites/fstests/fatfs_support
install: []
install-path: null
links: []
source:
- testsuites/fstests/fatfs_support/fs_support.c
- testsuites/fstests/support/fstest_support.c
- testsuites/fstests/support/ramdisk_support.c
target: testfatfs
type: build

View File

@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* COPYRIGHT (c) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
* On-Line Applications Research Corporation (OAR).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __FATFS_SUPPORT_h
#define __FATFS_SUPPORT_h
#define FILESYSTEM "FATFS"
#endif

View File

@@ -0,0 +1,183 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* COPYRIGHT (c) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
* On-Line Applications Research Corporation (OAR).
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/stat.h>
#include <sys/types.h>
#include <tmacros.h>
#include <rtems/fatfs.h>
#include <rtems/libcsupport.h>
#include <rtems/libio.h>
#include "fs_config.h"
#include "fstest.h"
#include "fstest_support.h"
#include "ramdisk_support.h"
/* Include FatFS headers - these are internal to the FatFS implementation */
extern int fatfs_diskio_register_device(
unsigned char pdrv,
const char *device_path
);
extern void fatfs_diskio_unregister_device( unsigned char pdrv );
/* FatFS constants and structures */
#define FR_OK 0
#define FM_FAT 0x01
#define FM_FAT32 0x02
typedef struct {
unsigned char fmt;
unsigned char num_fat;
unsigned int align;
unsigned int n_root;
unsigned long auto_cluster_size;
} mkfs_parm;
typedef unsigned char FRESULT;
/* FatFS function declaration */
extern FRESULT f_mkfs(
const char *path,
const mkfs_parm *opt,
void *work,
unsigned int len
);
#define BLOCK_SIZE 512
static const mkfs_parm fatfs_format_options = {
.fmt = FM_FAT, /* Format as FAT12/16 (auto-detect) */
.num_fat = 2, /* Number of FAT copies */
.align = 0, /* Auto data area alignment */
.n_root = 512, /* Number of root directory entries for FAT12/16 */
.auto_cluster_size = 0 /* Auto cluster size */
};
static rtems_resource_snapshot before_mount;
static unsigned char fatfs_work_buffer[ 4096 ]; /* Work buffer for f_mkfs() */
static int fatfs_format_disk( const char *device_path )
{
FRESULT fr;
int rc;
rc = fatfs_diskio_register_device( 0, device_path );
if ( rc != 0 ) {
printf( "Device registration failed: %d\n", rc );
return -1;
}
fr = f_mkfs(
"0:",
&fatfs_format_options,
fatfs_work_buffer,
sizeof( fatfs_work_buffer )
);
if ( fr != FR_OK ) {
printf( "FatFS formatting failed: %d\n", fr );
fatfs_diskio_unregister_device( 0 );
return -1;
}
fatfs_diskio_unregister_device( 0 );
return 0;
}
void test_initialize_filesystem( void )
{
int rc = 0;
rc = mkdir( BASE_FOR_TEST, S_IRWXU | S_IRWXG | S_IRWXO );
rtems_test_assert( rc == 0 );
init_ramdisk();
rc = fatfs_format_disk( RAMDISK_PATH );
rtems_test_assert( rc == 0 );
rtems_resource_snapshot_take( &before_mount );
rc = mount(
RAMDISK_PATH,
BASE_FOR_TEST,
"fatfs",
RTEMS_FILESYSTEM_READ_WRITE,
NULL
);
if ( rc != 0 ) {
printf( "Mount failed with errno: %s\n", strerror( errno ) );
}
rtems_test_assert( rc == 0 );
}
void test_shutdown_filesystem( void )
{
int rc = 0;
rc = unmount( BASE_FOR_TEST );
if ( rc != 0 ) {
printf( "Unmount failed with errno: %d (%s)\n", errno, strerror( errno ) );
} else {
printf( "Unmount successful!\n" );
}
rtems_test_assert( rc == 0 );
rtems_test_assert( rtems_resource_snapshot_check( &before_mount ) );
del_ramdisk();
}
/* configuration information */
/* drivers */
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
/**
* Configure base RTEMS resources.
*/
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_MAXIMUM_SEMAPHORES 10
#define CONFIGURE_MAXIMUM_TASKS 10
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 40
#define CONFIGURE_INIT_TASK_STACK_SIZE ( 16 * 1024 )
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_INIT
#include <rtems/confdefs.h>

View File

@@ -0,0 +1,445 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <sys/statvfs.h>
#include <rtems/blkdev.h>
#include <rtems/libio.h>
#include <rtems/sparse-disk.h>
#include <bsp.h>
#include "tmacros.h"
const char rtems_test_name[] = "FSFATFSFORMAT 1";
#define MAX_PATH_LENGTH 100
#define SECTOR_SIZE 512
#define FAT12_MAX_CLN 4085
#define FAT16_MAX_CLN 65525
#define FAT12_DEFAULT_SECTORS_PER_CLUSTER 8
#define FAT16_DEFAULT_SECTORS_PER_CLUSTER 32
/* FatFS constants and structures */
#define FR_OK 0
#define FR_INVALID_PARAMETER 19
#define FM_FAT 0x01
#define FM_FAT32 0x02
#define FM_ANY 0x07
typedef struct {
unsigned char fmt;
unsigned char num_fat;
unsigned int align;
unsigned int n_root;
unsigned long auto_cluster_size;
} mkfs_parm;
/* FatFS MKFS_PARM structure for proper parameter passing */
typedef struct {
unsigned char fmt; /* Format option (FM_FAT, FM_FAT32, etc.) */
unsigned char n_fat; /* Number of FATs */
unsigned int align; /* Data area alignment (sector) */
unsigned int n_root; /* Number of root directory entries */
unsigned long au_size; /* Cluster size (byte) */
} MKFS_PARM;
typedef unsigned char FRESULT;
extern int fatfs_diskio_register_device(
unsigned char pdrv,
const char *device_path
);
extern void fatfs_diskio_unregister_device( unsigned char pdrv );
extern FRESULT f_mkfs(
const char *path,
const MKFS_PARM *opt,
void *work,
unsigned int len
);
static unsigned char fatfs_work_buffer[ 4096 ];
static int fatfs_format_disk(
const char *device_path,
const mkfs_parm *format_options
)
{
FRESULT fr;
int rc;
MKFS_PARM fatfs_opts;
rc = fatfs_diskio_register_device( 0, device_path );
if ( rc != 0 ) {
return -1;
}
/* Convert test structure to FatFS structure */
fatfs_opts.fmt = format_options->fmt;
fatfs_opts.n_fat = format_options->num_fat;
fatfs_opts.align = format_options->align;
fatfs_opts.n_root = format_options->n_root;
fatfs_opts.au_size = format_options->auto_cluster_size *
512; /* Convert sectors to bytes */
fr = f_mkfs(
"0:",
&fatfs_opts,
fatfs_work_buffer,
sizeof( fatfs_work_buffer )
);
fatfs_diskio_unregister_device( 0 );
return ( fr == FR_OK ) ? 0 : -1;
}
static void test_disk_params(
const char *dev_name,
const char *mount_dir,
const blksize_t sector_size,
const blksize_t cluster_size,
const blkcnt_t sectors_per_cluster
)
{
int rv;
int fildes;
struct stat stat_buff;
char file_name[ MAX_PATH_LENGTH + 1 ];
ssize_t num_bytes;
unsigned int value = (unsigned int) -1;
snprintf( file_name, MAX_PATH_LENGTH, "%s/file1.txt", mount_dir );
memset( &stat_buff, 0, sizeof( stat_buff ) );
rv = mount( dev_name, mount_dir, "fatfs", RTEMS_FILESYSTEM_READ_WRITE, NULL );
rtems_test_assert( 0 == rv );
fildes = open(
file_name,
O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
);
rtems_test_assert( -1 != fildes );
num_bytes = write( fildes, &value, sizeof( value ) );
rtems_test_assert( sizeof( value ) == num_bytes );
rv = fstat( fildes, &stat_buff );
rtems_test_assert( 0 == rv );
rtems_test_assert( S_ISREG( stat_buff.st_mode ) );
rtems_test_assert( sizeof( value ) == stat_buff.st_size );
rtems_test_assert( cluster_size == stat_buff.st_blksize );
rtems_test_assert(
sectors_per_cluster == ( stat_buff.st_blocks * sector_size / 512 )
);
rtems_test_assert(
( ( ( stat_buff.st_size + cluster_size - 1 ) / cluster_size ) *
cluster_size / 512 ) == stat_buff.st_blocks
);
rv = close( fildes );
rtems_test_assert( 0 == rv );
rv = unmount( mount_dir );
rtems_test_assert( 0 == rv );
rv = mount( dev_name, mount_dir, "fatfs", RTEMS_FILESYSTEM_READ_WRITE, NULL );
rtems_test_assert( 0 == rv );
rv = unmount( mount_dir );
rtems_test_assert( 0 == rv );
}
static void test_create_file(
const char *mount_dir,
uint32_t file_idx,
bool expect_ok
)
{
char file_name[ MAX_PATH_LENGTH + 1 ];
int fd;
snprintf(
file_name,
MAX_PATH_LENGTH,
"%s/file%" PRIu32 ".txt",
mount_dir,
file_idx
);
fd = open(
file_name,
O_RDWR | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
);
if ( expect_ok ) {
int rv;
rtems_test_assert( fd >= 0 );
rv = close( fd );
rtems_test_assert( rv == 0 );
} else {
rtems_test_assert( fd == -1 );
}
}
static void test_file_creation(
const char *dev_name,
const char *mount_dir,
const uint32_t number_of_files
)
{
int rv;
uint32_t file_idx;
char file_name[ MAX_PATH_LENGTH + 1 ];
rv = mount( dev_name, mount_dir, "fatfs", RTEMS_FILESYSTEM_READ_WRITE, NULL );
rtems_test_assert( 0 == rv );
for ( file_idx = 0; file_idx < number_of_files; ++file_idx ) {
test_create_file( mount_dir, file_idx, true );
}
test_create_file( mount_dir, file_idx, false );
for ( file_idx = 0; file_idx < number_of_files; ++file_idx ) {
snprintf(
file_name,
MAX_PATH_LENGTH,
"%s/file%" PRIu32 ".txt",
mount_dir,
file_idx
);
rv = unlink( file_name );
rtems_test_assert( 0 == rv );
}
rv = unmount( mount_dir );
rtems_test_assert( 0 == rv );
}
static void test( void )
{
rtems_status_code sc;
int rv;
const char dev_name[] = "/dev/rda";
const char mount_dir[] = "/mnt";
mkfs_parm rqdata;
rtems_blkdev_bnum media_block_count;
memset( &rqdata, 0, sizeof( rqdata ) );
rv = mkdir( mount_dir, S_IRWXU | S_IRWXG | S_IRWXO );
rtems_test_assert( 0 == rv );
/* FAT12 */
/* For 1.44 MB disks */
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
64,
2880,
0
);
rtems_test_assert( RTEMS_SUCCESSFUL == sc );
/* Optimized for disk space */
rqdata.fmt = FM_FAT;
rqdata.num_fat = 1;
rqdata.align = 0;
rqdata.n_root = 32;
rqdata.auto_cluster_size = 1;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE, 1 );
test_file_creation( dev_name, mount_dir, rqdata.n_root );
/* FatFS silently clamps invalid parameters rather than returning errors,
* so we skip this test as it doesn't match FatFS behavior */
/* Optimized for read/write speed */
rqdata.fmt = FM_FAT;
rqdata.num_fat = 2;
rqdata.align = 0;
rqdata.n_root = 0;
rqdata.auto_cluster_size = 8;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE * 8, 8 );
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
/* Largest FAT12 disk */
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
64,
( FAT12_MAX_CLN * FAT12_DEFAULT_SECTORS_PER_CLUSTER ) - 1L,
0
);
rtems_test_assert( RTEMS_SUCCESSFUL == sc );
/* Default parameters - let FatFS choose cluster size */
rqdata.fmt = FM_FAT;
rqdata.num_fat = 2;
rqdata.align = 0;
rqdata.n_root = 512;
rqdata.auto_cluster_size = 0;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
/* FatFS chooses cluster size based on volume size, so test actual result */
test_disk_params(
dev_name,
mount_dir,
SECTOR_SIZE,
SECTOR_SIZE * 4, /* FatFS actually chooses 4 sectors per cluster */
4
);
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
/* FAT16 */
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
1024,
( FAT12_MAX_CLN * FAT12_DEFAULT_SECTORS_PER_CLUSTER ) + 1L,
0
);
rtems_test_assert( RTEMS_SUCCESSFUL == sc );
/* Optimized for disk space */
rqdata.fmt = FM_FAT;
rqdata.num_fat = 1;
rqdata.align = 0;
rqdata.n_root = 32;
rqdata.auto_cluster_size = 1;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE, 1 );
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
/* FAT32 */
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
1024,
( FAT16_MAX_CLN * FAT16_DEFAULT_SECTORS_PER_CLUSTER ) + 41L,
0
);
rtems_test_assert( RTEMS_SUCCESSFUL == sc );
/* Default parameters - FAT32 chooses larger cluster sizes */
rqdata.fmt = FM_FAT32;
rqdata.num_fat = 2;
rqdata.align = 0;
rqdata.n_root = 0;
rqdata.auto_cluster_size = 0;
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE * 16, 16 );
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
/* Format some disks from 1MB up to 128GB - let FatFS choose appropriate FAT
* type */
rqdata.fmt = FM_ANY;
rqdata.num_fat = 2;
rqdata.align = 0;
rqdata.n_root = 0;
rqdata.auto_cluster_size = 64;
for ( media_block_count = 1 * 1024 * ( 1024 / SECTOR_SIZE );
media_block_count <= 128 * 1024 * 1024 * ( 1024 / SECTOR_SIZE );
media_block_count *= 2 ) {
sc = rtems_sparse_disk_create_and_register(
dev_name,
SECTOR_SIZE,
64,
media_block_count,
0
);
rtems_test_assert( sc == RTEMS_SUCCESSFUL );
rv = fatfs_format_disk( dev_name, &rqdata );
rtems_test_assert( rv == 0 );
test_disk_params( dev_name, mount_dir, SECTOR_SIZE, SECTOR_SIZE * 64, 64 );
rv = unlink( dev_name );
rtems_test_assert( rv == 0 );
}
}
static void Init( rtems_task_argument arg )
{
(void) arg;
TEST_BEGIN();
test();
TEST_END();
rtems_test_exit( 0 );
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 5
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_MAXIMUM_TASKS 1
#define CONFIGURE_MAXIMUM_SEMAPHORES 10
#define CONFIGURE_INIT_TASK_STACK_SIZE ( 32 * 1024 )
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
#define CONFIGURE_BDBUF_BUFFER_MAX_SIZE ( 32 * 1024 )
#define CONFIGURE_INIT
#include <rtems/confdefs.h>

View File

@@ -0,0 +1,17 @@
This file documents the test fsfatfssync01.
test set name: fsfatfssync01
test purpose:
Verify FatFS auto-sync behavior and explicit fsync() functionality.
test description:
Tests FatFS's synchronization behavior by:
- Creating a file and verifying it persists after disk cache purge
- Writing data without explicit fsync() and verifying it persists after disk cache purge
(demonstrates FatFS auto-syncs on file close)
- Writing additional data with explicit fsync() and verifying it persists after disk cache purge
Note: Unlike traditional POSIX filesystems, FatFS automatically calls f_sync() during
f_close(), ensuring all writes are immediately persisted to disk regardless of whether
fsync() was explicitly called.

View File

@@ -0,0 +1,237 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2025 Sepehr Ganji <sepehrganji79@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <rtems/blkdev.h>
#include <rtems/fatfs.h>
#include <rtems/libio.h>
#include <rtems/ramdisk.h>
#include "tmacros.h"
const char rtems_test_name[] = "FSFATFSSYNC 1";
/* FatFS constants and structures */
#define FR_OK 0
#define FM_FAT 0x01
#define FM_FAT32 0x02
typedef struct {
unsigned char fmt;
unsigned char num_fat;
unsigned int align;
unsigned int n_root;
unsigned long auto_cluster_size;
} MKFS_PARM;
typedef unsigned char FRESULT;
/* External FatFS functions */
extern int fatfs_diskio_register_device(
unsigned char pdrv,
const char *device_path
);
extern void fatfs_diskio_unregister_device( unsigned char pdrv );
extern FRESULT f_mkfs(
const char *path,
const MKFS_PARM *opt,
void *work,
unsigned int len
);
static unsigned char fatfs_work_buffer[ 4096 ];
static void create_file( const char *file )
{
int fd;
int rv;
fd = creat( file, S_IRWXU | S_IRWXG | S_IRWXO );
rtems_test_assert( fd >= 0 );
rv = fsync( fd );
rtems_test_assert( rv == 0 );
rv = close( fd );
rtems_test_assert( rv == 0 );
}
static void write_to_file( const char *file, bool sync )
{
int fd;
char buf[ 1 ] = { 'A' };
ssize_t n;
off_t pos_before, pos_after;
int rv;
fd = open( file, O_RDWR );
rtems_test_assert( fd >= 0 );
pos_before = lseek( fd, 0, SEEK_END );
n = write( fd, buf, sizeof( buf ) );
rtems_test_assert( n == (ssize_t) sizeof( buf ) );
pos_after = lseek( fd, 0, SEEK_END );
rtems_test_assert( pos_after == pos_before + sizeof( buf ) );
if ( sync ) {
rv = fsync( fd );
rtems_test_assert( rv == 0 );
}
rv = close( fd );
rtems_test_assert( rv == 0 );
}
static void check_file_size( const char *file, off_t size )
{
int fd;
off_t pos;
int rv;
fd = open( file, O_RDWR );
rtems_test_assert( fd >= 0 );
pos = lseek( fd, 0, SEEK_END );
rtems_test_assert( pos == size );
rv = close( fd );
rtems_test_assert( rv == 0 );
}
static void test( const char *rda, const char *mnt, const char *file )
{
static const MKFS_PARM fatfs_format_options = {
.fmt = FM_FAT,
.num_fat = 2,
.align = 0,
.n_root = 512,
.auto_cluster_size = 0
};
int disk_fd;
int rv;
FRESULT fr;
disk_fd = open( rda, O_RDWR );
rtems_test_assert( disk_fd >= 0 );
rv = fatfs_diskio_register_device( 0, rda );
rtems_test_assert( rv == 0 );
/* Format using FatFS */
fr = f_mkfs(
"0:",
&fatfs_format_options,
fatfs_work_buffer,
sizeof( fatfs_work_buffer )
);
rtems_test_assert( fr == FR_OK );
fatfs_diskio_unregister_device( 0 );
rv = mount_and_make_target_path(
rda,
mnt,
"fatfs",
RTEMS_FILESYSTEM_READ_WRITE,
NULL
);
rtems_test_assert( rv == 0 );
create_file( file );
rv = rtems_disk_fd_purge( disk_fd );
rtems_test_assert( rv == 0 );
write_to_file( file, false );
rv = rtems_disk_fd_purge( disk_fd );
rtems_test_assert( rv == 0 );
check_file_size( file, 1 );
write_to_file( file, true );
rv = rtems_disk_fd_purge( disk_fd );
rtems_test_assert( rv == 0 );
check_file_size( file, 2 );
rv = unmount( mnt );
rtems_test_assert( rv == 0 );
rv = close( disk_fd );
rtems_test_assert( rv == 0 );
}
static void Init( rtems_task_argument arg )
{
(void) arg;
TEST_BEGIN();
test( "/dev/rda", "/mnt", "/mnt/file" );
TEST_END();
rtems_test_exit( 0 );
}
rtems_ramdisk_config rtems_ramdisk_configuration[] = {
{ .block_size = 512, .block_num = 1024 }
};
size_t rtems_ramdisk_configuration_size = 1;
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_EXTRA_DRIVERS RAMDISK_DRIVER_TABLE_ENTRY
#define CONFIGURE_APPLICATION_NEEDS_LIBBLOCK
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 6
#define CONFIGURE_FILESYSTEM_FATFS
#define CONFIGURE_MAXIMUM_TASKS 2
#define CONFIGURE_EXTRA_TASK_STACKS ( 8 * 1024 )
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT_TASK_ATTRIBUTES RTEMS_FLOATING_POINT
#define CONFIGURE_INIT
#include <rtems/confdefs.h>