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?
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
.