filesystemsemulationmicrocontrollerstm32fatfs

How to emulate FatFS?


I'd like to present some data on a microcontroller (STM32L4xx) to appear as though it was a single file on a filesystem mounted over USB to a host PC. I have functions that can produce the data at any requested offset in the (emulated) file. The file can be read only, there is no reason to write to it; and it can have a fixed name and size.

This is pretty much the opposite use case of Chan's FatFS and similar libraries: I don't have a real filesystem, and I don't want to access a filesystem, I just want to do whatever is necessary to present an emulated (fake) filesystem (with a single file) to the USB host.

I think bootloaders that appear as a mass storage device (eg Mbed) must do something similar, since the program data (probably) isn't written to a real filesystem when you drag and drop a hex file onto what looks like the Mbed storage device; I imagine it is stored somewhere but not in an actual filesystem as the one that is visible.

Could anyone point me to some sample code along these lines?


Solution

  • Because there is only one fixed length file, you don't have to bother much with the filesystem structure, but create it once on a PC, and treat it as a fixed header. The device needs no FatFS code at all. A file on an otherwise empty FAT filesystem will be stored contiguously from the first free sector onwards.

    First, let's create a minimal FAT filesystem image, e.g. using the mtools package on linux.

    mformat -v "EMBEDDED FS" -t 1 -h 1 -s 5 -S 2 -C -i fs.img -c 1 -r 1 -L 1
    

    this creates a disk image with one side, one track and five 512 bytes long sectors. The first four sectors are the boot sector, two FATs, and the root directory, the fifth sector can hold an 512 bytes long file.

    $ mdir -i fs.img 
     Volume in drive : is EMBEDDED FS
     Volume Serial Number is 061F-DA50
    Directory for ::/
    
    No files
                                    512 bytes free
    

    Increase the number of sectors if you need more space.


    UPDATE

    I've found that my mformat has trouble creating larger filesystems (or I couldn't figure out the right parameters). There is another utility called mkdosfs, which worked better for me, e.g.

    /sbin/mkdosfs -f 1 -n "EMBEDDED FS" -r 16 -s 64 -S 512 -v -C big.img 524256
    

    creates a 512 MB filesystem.

    $ mdir -i big.img
     Volume in drive : is EMBEDDED FS
     Volume Serial Number is 5457-0DF4
    Directory for ::/
    
    No files
                            536 739 840 bytes free
    

    A larger filesystem will of course contain more overhead, but you'll find that most of it is just a running number (the cluster chain in the FAT) that can be generated on the fly as well, as long as the image contains a single big file only.


    Now create a file with the required length and copy it into the image

    echo 'hello world' > testfile.txt
    mcopy -i fs.img testfile.txt ::/
    

    and dump the results with hd (* means that the preceding line is repeated)

    $ hd fs.img
    00000000  eb 3c 90 4d 54 4f 4f 34  30 31 38 00 02 01 01 00  |.<.MTOO4018.....|
    00000010  02 10 00 05 00 f0 01 00  05 00 01 00 00 00 00 00  |................|
    00000020  00 00 00 00 00 00 29 65  83 db 06 45 4d 42 45 44  |......)e...EMBED|
    00000030  44 45 44 20 46 53 46 41  54 31 32 20 20 20 fa 31  |DED FSFAT12   .1|
    00000040  c0 8e d8 8e c0 fc b9 00  01 be 00 7c bf 00 80 f3  |...........|....|
    00000050  a5 ea 56 00 00 08 b8 01  02 bb 00 7c ba 80 00 b9  |..V........|....|
    00000060  01 00 cd 13 72 05 ea 00  7c 00 00 cd 19 00 00 00  |....r...|.......|
    00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 80 00  |................|
    000001c0  01 00 01 00 05 00 00 00  00 00 05 00 00 00 00 00  |................|
    000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
    00000200  f0 ff ff ff 0f 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000210  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    00000400  f0 ff ff ff 0f 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    00000600  45 4d 42 45 44 44 45 44  20 46 53 08 00 00 33 80  |EMBEDDED FS...3.|
    00000610  3e 4f 3e 4f 00 00 33 80  3e 4f 00 00 00 00 00 00  |>O>O..3.>O......|
    00000620  54 45 53 54 46 49 4c 45  54 58 54 20 18 00 a7 80  |TESTFILETXT ....|
    00000630  3e 4f 3e 4f 00 00 a7 80  3e 4f 02 00 0c 00 00 00  |>O>O....>O......|
    00000640  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    00000800  68 65 6c 6c 6f 20 77 6f  72 6c 64 0a 00 00 00 00  |hello world.....|
    00000810  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    00000a00
    

    As expected, the file content is stored starting at offset 0x0800, right after the four reserved sectors. You can now take the first 2048 bytes of the image, and put it in your program to initialize the virtual disk image.

    For the USB device code, you can start with the Applications/USB_Device/MSC_Standalone demo in STM32CubeL4, which turns the STM32 into an SD card reader. Throw away the SD card related code, and just deliver the fixed image data for the first four sectors, and your file data for the rest.

    I haven't tried it myself, so it's possible that some operating systems would choke on this tiny disk image, in that case try creating a standard floppy image with mformat.