Skip to content

Commit

Permalink
Adds FlashFile (#598)
Browse files Browse the repository at this point in the history
FlashFile is a file driver for laying out storage directly in flash,
taking in some limitations on what form of writes can be performed.
FlashFile supports both SPIFlash driver, as well as on-chip flash
(although no on-chip flash examples exist at this point).
  • Loading branch information
balazsracz authored Jan 1, 2022
1 parent e874b9d commit 56c9a78
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 2 deletions.
8 changes: 6 additions & 2 deletions src/freertos_drivers/common/DeviceFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*
* \file DeviceFile.hxx
* This implements a common device file abstraction.
*
* Base class for implementing block devices and other file drivers which can
* be seeked and addressed by absolute byte offsets.
*
* @author Stuart W. Baker
* @date 16 July 2016
Expand All @@ -38,7 +40,9 @@

#include "Devtab.hxx"

/** Common base class for all DeviceFile access.
/**
* Base class for implementing block devices and other file drivers which can
* be seeked and addressed by absolute byte offsets.
*/
class DeviceFile : public Node
{
Expand Down
163 changes: 163 additions & 0 deletions src/freertos_drivers/common/FlashFile.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/** \copyright
* Copyright (c) 2021, Balazs Racz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - 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 HOLDER 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.
*
* \file FlashFile.hxx
*
* File with backing data in flash (on-chip or SPI) in direct layout. This file
* has restrictions on how it can be written.
*
* @author Balazs Racz
* @date 30 Dec 2021
*/

#ifndef _FREERTOS_DRIVERS_COMMON_FLASHFILE_HXX_
#define _FREERTOS_DRIVERS_COMMON_FLASHFILE_HXX_

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "freertos_drivers/common/DeviceFile.hxx"

/// FlashFile is a driver for a single file that is backed by flash.
/// Instantiations may use serial flash (using SPIFlash) or internal flash.
///
/// There are limitations on writes.
///
/// - A sequential write of the file with no seeks from the beginning to the
/// end will work. Whenever the first byte of a sector is written, the entire
/// sector will be erased.
///
/// - If the file is opened with O_TRUNC, then all sectors of the file are
/// erased.
///
/// - The file does not remember its size. fstat always returns the maximum
/// size.
template <class FLASH> class FlashFile : public DeviceFile
{
public:
/// Constructor.
/// @param name what should be the name of this file be (to pass to ::open).
/// @param flash accessor object to the backing flash. One such object can
/// be used for multiple FlashFiles. The flash object shall do locking
/// internally.
/// @param address where does the data of this file start. Must be aligned
/// on a sector boundary on the flash device. The unit is whatever address
/// the flash driver understands.
/// @param size maximum size of this file on flash.
FlashFile(const char *name, FLASH *flash, size_t address, size_t size)
: DeviceFile(name)
, flash_(flash)
, flashStart_(address)
, size_(size)
{
auto start_sec = flash_->next_sector_address(flashStart_);
// Beginning of the file must be on a sector boundary.
HASSERT(start_sec == flashStart_);
}

/// Overrides behavior of open for O_TRUNC.
int open(File *file, const char *path, int flags, int mode) override
{
if ((mode & O_WRONLY) && (flags & O_TRUNC))
{
// erase entire file.
flash_->erase(flashStart_, size_);
flags &= ~O_TRUNC;
}
return DeviceFile::open(file, path, flags, mode);
}

/// Implements querying the file size.
int fstat(File *file, struct stat *stat) override
{
DeviceFile::fstat(file, stat);
stat->st_size = size_;
return 0;
}

/// Write to the flash.
/// @param index index within the file address space to start write
/// @param buf data to write
/// @param len length in bytes of data to write
/// @return number of bytes written upon success, -errno upon failure
ssize_t write(unsigned int index, const void *buf, size_t len) override
{
if (index >= size_)
{
return 0; // EOF
}
if (len > size_ - index)
{
len = size_ - index;
}
size_t addr = flashStart_ + index;
size_t sec_addr = flash_->next_sector_address(addr);
if (addr == sec_addr)
{
/// Writing at the beginning of a sector. Need an erase.
size_t next_sec = flash_->next_sector_address(sec_addr + 1);
flash_->erase(sec_addr, next_sec - sec_addr);
sec_addr = next_sec;
}
if ((sec_addr - addr) < len)
{
len = sec_addr - addr;
}
flash_->write(addr, buf, len);
return len;
}

/// Read from the flash.
/// @param index index within DeviceFile address space to start read
/// @param buf location to post read data
/// @param len length in bytes of data to read
/// @return number of bytes read upon success, -errno upon failure
ssize_t read(unsigned int index, void *buf, size_t len) override
{
if (index >= size_)
{
return 0; // EOF
}
if (len > size_ - index)
{
len = size_ - index;
}
size_t addr = flashStart_ + index;
flash_->read(addr, buf, len);
return len;
}

private:
/// Accessor to the flash device.
FLASH *flash_;
/// Offset where our file start on flash.
size_t flashStart_;
/// How many bytes our file is on flash.
size_t size_;
};

#endif
14 changes: 14 additions & 0 deletions src/freertos_drivers/common/SPIFlash.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
#include <spi/spidev.h>
#include <sys/types.h>

#include "utils/logging.h"

class OSMutex;
class SPI;

Expand Down Expand Up @@ -109,6 +111,8 @@ public:
: cfg_(cfg)
, lock_(lock)
{
/// This ensures that the sector size is a power of two.
HASSERT((cfg->sectorSize_ & (cfg->sectorSize_ - 1)) == 0);
}

/// @return the configuration.
Expand All @@ -134,6 +138,16 @@ public:
/// @param len how many bytes to read
void read(uint32_t addr, void *buf, size_t len);

/// Aligns an address to the next possible sector start (i.e., rounds up to
/// sector boundary).
/// @param addr an address in the flash address space.
/// @return If addr is the first byte of a sector, then returns addr
/// unmodified. Otherwise returns the starting address of the next sector.
uint32_t next_sector_address(uint32_t addr)
{
return (addr + cfg_->sectorSize_ - 1) & ~(cfg_->sectorSize_ - 1);
}

/// Erases sector(s) of the device.
/// @param addr beginning of the sector to erase. Must be sector aligned.
/// @param len how many bytes to erase (must be multiple of sector size).
Expand Down

0 comments on commit 56c9a78

Please sign in to comment.