Implement a flash translation layer.
A flash translation layer does block remapping, translating from visible block addresses used by a file system to physical cells on one or more flash chips. This provides wear leveling, which is essential for effective use of flash, and also typically some amount of read caching and write buffering. (And it takes care of excluding cells that have gone bad.)
This allows FFS, LFS, msdosfs, or whatever other conventional file system to be used on raw flash chips. (Note that SSDs and USB flash drives and so forth contain their own FTLs.)
FTLs involve quite a bit of voodoo and there is a lot of prior art and research; do not just sit down and start coding.
There are also some research FTLs that we might be able to get the code for; it is probably worth looking into this.
Note that NAND flash and NOR flash are different and need different handling, and the various cell types and other variations also warrant different policy choices.
The degree of overprovisioning (that is, the ratio of the raw capacity of the flash chips to the advertised size of the resulting device) should be configurable as this is a critical factor for performance.
Making the device recoverable rather than munching itself in system crashes or power failures is a nice extra, although apparently the market considers this an optional feature for consumer devices.
The flash translation layer should probably be packaged a driver that attaches to one or more flash chips and provides a disk-type block/character device pair.