Files
Sani7 0cc34bfd6b LLFS
add llfs_get_filename_ext
2023-12-02 23:01:06 +01:00

6.5 KiB

LLFS (Linked List File System)

Introduction

The llfs filesystem can be generated using the mkllfs utility. The resulting C file encapsulates the filesystem data within a linked list which can be used by the llfs library.

As a flat filesystem, llfs lacks support for directories. Using the llfs API, information about the files in the filesystem can be retrieved, and the files can be read using direct memory access. Alternatively, the POSIX file functions can be used. But this is more resource intensive, as data must be copied before using it.

It's essential to note that the llfs filesystem operates in a read-only mode, restricting operations solely to read functions.

Table of contents

Initialization

Before using the llfs API, or the file related POSIX (fopen, fgetc, ...) functions, the filesystem must be initialized by calling llfs_init().

Usage of the llfs API

The llfs_file_t struct

The llfs_file_t struct contains information about a file in the filesystem.

typedef struct {
    const uint8_t* data; // Pointer to the file data (len bytes)
    const char* name;    // Null-terminated string with the filename
    size_t len;          // Length of the file data
} llfs_file_t;

The data pointer points to the data of the file in the filesystem, and can be used to read the file.

Getting a list of files

#include "llfs.h"

void main(void) {
    llfs_init();
    
    // Allocate space for 10 files
    llfs_file_t file_list[10];
    
    // Get the file list
    size_t file_count = llfs_file_list(file_list, 10, NULL);
    
    // Loop through the files and print their names and sizes
    for (int i = 0; i < file_count; i++) {
        LOG_INFO(TAG, "File: %s, size: %d", file_list[i].name, file_list[i].len);
    }
}

Result:

[Info]      (2009) [main]: File: image.bmp, size: 9270
[Info]      (2013) [main]: File: python_file.py, size: 645
[Info]      (2019) [main]: File: image2.bmp, size: 7738
[Info]      (2024) [main]: File: filename with a space.txt, size: 61
[Info]      (2031) [main]: File: file1.txt, size: 77

It is also possible to use a file extension filter (e.g. *.bmp, *.txt, *.py).

    // ...
    size_t file_count = llfs_file_list(file_list, 10, "*.bmp");
    // ...

This will only return files with the .bmp extension.

[Info]      (2009) [main]: File: image.bmp, size: 9270
[Info]      (2019) [main]: File: image2.bmp, size: 7738

It is also possible to iterate through the files without allocating an array. When the memory pointer is NULL, the iterator will start at the beginning of the file list. Each call to llfs_next_file() will return the next file in the list, if a filter is specified files that don't match the filter will be skipped.

#include "llfs.h"

void main(void) {
    llfs_init();
    
    // Get the file list
    void* mem = NULL;   // Pointer for internal use by the llfs library
    llfs_file_t* file;
    while ((file = llfs_next_file(&mem, ".bmp")) != NULL) {
        LOG_INFO(TAG, "File: %s", file->name);
    }
}

While this method doesn't require allocating memory for the file list, it is slower than the previous method due to the overhead calling the function for each file. Additionally, the required memory for the filelist is very small, so it's recommended to use the first method.

Reading a file

#include "llfs.h"

void main(void) {
    llfs_init();
    
    // Get a file by name
    llfs_file_t* file = llfs_file_open("filename with a space.txt");
    
    if (file != NULL) {
        // Print the file name, size and data
        LOG_INFO(TAG, "File found: %s, size: %d", file->name, file->len);
        LOG_INFO(TAG, "File data: %s", file->data);
    } else {
        LOG_WARN(TAG, "File not found");
    }
}

Result:

[Info]      (2040) [main]: File found: filename with a space.txt, size: 61
[Info]      (2047) [main]: File data: This is a file with a space in it's filename.

Getting the number of files

#include "llfs.h"

void main(void) {
    llfs_init();
    
    // Get the number of files
    size_t file_count = llfs_file_count();
    
    // Print the number of files
    LOG_INFO(TAG, "File count: %d", file_count);
}

Getting a file extension

#include "llfs.h"
void main(void) {
    llfs_init();
    
    // Get a file by name
    llfs_file_t* file = llfs_file_open("filename with a space.txt");
    
    if (file != NULL) {
        // Print the file name, size and data
        LOG_INFO(TAG, "File found: %s, size: %d", file->name, file->len);
        LOG_INFO(TAG, "File data: %s", file->data);
        
        // Get the file extension
        const char* ext = llfs_file_ext(file->name);
        LOG_INFO(TAG, "File extension: %s", ext);
    } else {
        LOG_WARN(TAG, "File not found");
    }
}

Using the POSIX file functions

The llfs library also supports the POSIX file functions. As the file system is read-only, write functions are not implemented. There is also a limit on the number of files that can be open concurrently, this is set by the POSIX_MAX_FILES macro in llfs.c. The default value is 10, but there are already 3 files in use (stdin, stdout, stderr), so the maximum number of files that can be open is 7.

The following functions are tested and working, but other functions might also work:

  • fopen
  • fclose
  • fgetc
  • fread
  • fseek
  • ftell
  • rewind
  • fstat
  • fileno

Enabling the external loader in STM32CubeIDE

In order to write the file system data to the QSPI flash, the external loader must be enabled in STM32CubeIDE. This can be done by opening the debug configuration:

screenshot of step 1

Then, in the Debugger tab: 3. Enable the External Loader 4. Click the Scan button 5. Select the correct loader: N25Q128A_STM32F746G-DISCO, 0x90000000 ...

screenshot of step 2