Making Vybrid Visible as USB Storage Using Linux Print


This application note explains how to make the Vybrid running Linux visible as a USB storage device to a host such as, for instance, Windows or Linux PC or notebook.

The most typical example of when this functionality is required is a "data harvesting" application. In such an application, a remote Vybrid device collects data readings from various sensors and then stores collected readings into a non-volatile storage, such as NAND Flash. Once in a while, a technician visits the remote site to offload harvested data and take it to a main site for further processing. Using the functionality described in this note, the data offloading is as simple as connecting the Vybrid as a USB device to a technician's notebook, which immediately recognizes it as USB storage. Collected data can then be simply copied from the USB storage to other disks on the notebook.

A variation of the same application is a movable device that is periodically taken to a main office for data offloading. In this scenario, the embedded device is simply connected to a PC as a USB device and data is copied from the USB storage to the host computer.

Hardware Platform

The hardware platform is theEmcraft Systems Vybrid VF6 system-on-module (SOM) plugged into the TWR-VF6-SOM-BSB development baseboard.

High-Level Architecture

We will use the Linux File-backed Storage Gadget (FSG) to implement what we need. The main idea is that the FSG presents a file in the local file system as a storage device to the USB host. On the target side, the content of the disk is stored in a file (hence the "File-backed Storage"). The backing file is a "Unix file" that is given to the File-backed Storage Gadget as a parameter, which can be a normal file or a block device, such as a disk partition.

To make data sharing workable using a Windows host, the USB storage must be formatted as a FAT file system. This way, data gathered by the target into the USB storage can be copied or manipulated otherwise, using standard Windows tools on the USB host end.

The implication of the above is that the backing file must be mounted as a FAT32 file system on the target side. This is not really a problem since Linux supports the FAT32 file system (along with many other file systems). Clearly, a file system can be mounted on a block device such as a disk or a disk partition but it is also possible to mount a file system on a normal file using the so-called "loop" device.

The big question is where we want the backing file to reside on a Vybrid-based device. We need the file to be on a non-volatile media. It is not really a hard requirement and we could keep the backing file in a RAM-based filesystem too, however in case there is an unexpected power cut, which happens all the time in embedded applications, all data we have collected would be lost. Not really acceptable, in a generic application.

The backing file must be in Flash, then. The Vybrid VF6 system-on-module (SOM) provides a 128 MBytes SPI Flash. In Linux, that Flash can be split on as many partitions as makes sense from the application point of view. A few first MBytes of the SPI Flash are taken by the U-boot environment variables and a bootable Linux image, however the remaining Flash is sufficient to allocate a partition specifically for hosting the USB storage backing file.

Now, having allocated a Flash partition to the USB backing file, we have two options, potentially:

  • The entire Flash partition could be mounted as a FAT32 file system. It won't work however, for two reasons. First and foremost, Flash needs to be erased before it can be written to. The FAT32 implementation does not assume that it can be used with a raw Flash device so, as soon as an attempt is made to write data to an unerased Flash sector, an I/O error will be reported by the kernel. Secondly, even supposing that the FAT32 implementation is somehow amended to work on top of a raw Flash device, FAT32 is not architecturally designed to be hosted in a Flash device. Some Flash sectors would be written to more frequently than others and a Flash wear scenario will quickly occur.
  • The backing file can be a file in a JFFS2 file system mounted on the Flash partition. This is a reasonable solution since JFFS2 takes care, transparently for upper-level software, both of erasing Flash prior to writing and of ensuring that Flash sectors are uses evenly to avoid a Flash wear.

To summarize it all, the architecture we want to implement will be as follows:

  • The Linux File-backed Storage Gadget (FSG) will be used to implement a USB storage device accessible by host software.
  • To ensure that the USB storage content survives power-cycles and resets, the backing file will be in Flash.
  • To allow upper-level software accessing the backing file transparently and avoid a Flash wear, the backing file will be a normal file in a JFFS2 file system.
  • The JFFS2 file system will be mounted on a dedicated partition in the SPI Flash.
  • The backing file will be mounted as a FAT32 file system on both the Linux and host PC sides. On the Linux side, the backing file will be mounted as a FAT2 file system using the "loop" block device.

Building the Demo

In order to test the USB gadget functionality, apply this patch to the Emcraft release 1.14.0 kernel tree. Then, make the following changes to the networking project included in the Emcraft software distribution. On the development host, go to projects/networking and run the make kmenuconfig command to access kernel configuration menu. Navigate to the Device Drivers -> Block Devices menu and enable the Loopback device support menu item:

Navigate to the Device Drivers -> USB Support menu and enable the USB Gadget Support item:

Then enter the USB Gadget Support menu and enable the File-backed Storage Gadget kernel module:

Run the make bmenuconfig command to enter the Busybox configuration menu.

Navigate to the Coreutils menu and enable the dd menu item:

Navigate to the Linux System Utilities menu and enable the losetup menu item:

In the target file system, there must be appropriate device nodes to access the loopback device using standard Linux tools and utilities. Add the following lines to your <project>.initramfs file to create the device nodes for the loopback device:

nod /dev/loop-control 0600 0 0 c 10 237
nod /dev/loop0 0600 0 0 b 7 0

Also, the losetup utility and the kernel module must be added to the target file system. Add the following lines to your <project>.initramfs file:

slink /bin/losetup busybox 777 0 0
file /g_file_storage.ko ${INSTALL_ROOT}/linux/drivers/usb/gadget/g_file_storage.ko 755 0 0

Preparing USB Storage

This section explains how to prepare the USB storage for deployment. This command sequence is a once-off procedure that needs to be performed on the Vybrid at software manufacturing time.

With the bootable Linux image (from the previous section) installed, U-Boot loads the Linux image from the NAND Flash to the RAM and passes control to the kernel entry point:

U-Boot 2011.12-vf6-1.14.0 (May 14 2015 - 19:50:44)

CPU: Freescale VyBrid 600 family rev1.3 at 498 MHz
Board: VF6-SOM Rev 3.a, www.emcraft.com
DDR controller is initialized
DRAM: 512 MiB
NAND: 512 MiB
MMC: FSL_SDHC: 0
Bad block table found at page 262080, version 0x01
Bad block table found at page 262016, version 0x01
In: serial
Out: serial
Err: serial
Net: FEC0
Hit any key to stop autoboot: 0
## Booting kernel from Legacy Image at 80007fc0 ...
Image Name: Linux-3.0.15-g8097505
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 3240784 Bytes = 3.1 MiB
Load Address: 80008000
Entry Point: 80008000
XIP Kernel Image ... OK
OK

Starting kernel ...

The kernel proceeds to boot-up, initializing the configured I/O interfaces and sub-systems:

Starting kernel ...
Linux version 3.0.15-g8097505 ( This e-mail address is being protected from spambots. You need JavaScript enabled to view it ) (gcc version 4.7.2 (GCC) ) #35 Thu Jul 23 13:03:48 +0400 2015
CPU: ARMv7 Processor [410fc051] revision 1 (ARMv7), cr=10c53c7d
CPU: VIPT nonaliasing data cache, VIPT aliasing instruction cache
Machine: Emcraft Vybrid SOM Board
Memory policy: ECC disabled, Data cache writeback
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 130048
Kernel command line: mem=512M console=ttymxc0,115200 ip=172.17.33.21:172.17.0.1:17

...

ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
fsl-ehci fsl-ehci.1: Freescale On-Chip EHCI Host Controller
fsl-ehci fsl-ehci.1: new USB bus registered, assigned bus number 1
fsl-ehci fsl-ehci.1: irq 108, io base 0x400b4000
fsl-ehci fsl-ehci.1: USB 2.0 started, EHCI 1.00
usb usb1: New USB device found, idVendor=1d6b, idProduct=0002
usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
usb usb1: Product: Freescale On-Chip EHCI Host Controller
usb usb1: Manufacturer: Linux 3.0.15-g8097505 ehci_hcd
usb usb1: SerialNumber: fsl-ehci.1
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
Initializing USB Mass Storage driver...
usbcore: registered new interface driver usb-storage
USB Mass Storage support registered.
ARC USBOTG Device Controller driver (1 August 2005)
TCP cubic registered
NET: Registered protocol family 17
Registering the dns_resolver key type
VFP support v0.3: implementor 41 architecture 2 part 30 variant 5 rev 1
Freeing init memory: 3452K
init started: BusyBox v1.17.0 (2015-07-23 12:52:55 +0400)
eth0: Freescale FEC PHY driver [Micrel KS8081] (mii_bus:phy_addr=1:01, irq=-1)
Loading /cmsis_example.bin to 0x3f400000 ...
Loaded 65872 bytes. Booting at 0x3f404119... done
~ # PHY: 1:01 - Link is Up - 100/Full

First thing we need to do is to erase the Flash partition we are going to use for the JFFS2 file system:

~ # flash_eraseall -j /dev/mtd5
Erasing 128 Kibyte @ 2c460000 - 100% complete.
~ #

We can now mount the newly created JFFS2 file system. As expected, it is empty at this time:

~ # mount -t jffs2 /dev/mtdblock5 /mnt
~ # ls -lt /mnt
~ #

Next step is to create in the JFFS2 filesystem a backing file for the FAT32 image.

The JFFS2 partition is about 80 MBytes in size so let's create a 32 MBytes FAT32 file system image:

~ # dd if=/dev/zero of=/mnt/fat32.part bs=1M count=32
32+0 records in
32+0 records out
~ # ls -lt /mnt
-rw-r--r-- 1 root root 33554432 Aug 17 23:06 fat32.part
~ #

Now, let start the File-backed Storage Gadget on the backing file:

~ # insmod /g_file_storage.ko file=/mnt/fat32.part stall=0 removable=1
g_file_storage gadget: controller 'fsl-usb2-udc' not recognized
g_file_storage gadget: No serial-number string provided!
g_file_storage gadget: File-backed Storage Gadget, version: 1 September 2010
g_file_storage gadget: Number of LUNs=1
g_file_storage gadget-lun0: ro=0, nofua=0, file: /mnt/fat32.part
fsl-usb2-udc: bind to driver g_file_storage
~ #

Assuming the Vybrid is connected to a host as a USB gadget, the above command will make it immediately visible as a USB storage device to the host. Or, the Vybrid is not connected to a host at the time the above command is run, simply connect it to a host using an appropriate USB cable. Specifically, connect a USB 2.0 A Male to Mini-B cable between a free USB port on the host and the P13 USB interface connector on the TWR-VF6-SOM-BSB-2A baseboard.

At this point the Vybrid should become visible as a USB storage device to the host, however the storage still needs to be formatted as a FAT32 file system to allow using it with standard host software. The formatting is done on the host using standard software. For instance, on a Windows machine, go to My Computer and then locate an icon for the new removable disk. Ask to format it and agree with anything that Windows suggests, until the storage is formatted as a FAT32 file system.

Harvesting Data on the Target

Ok, so now everything is ready to start deploying the Vybrid as a "data harvesting" device. In all probability, the commands shown below would be put into the /etc/rc start-up script by any reasonably application, but in order to make the set-up command sequence easier for understanding, let's run the required commands manually.

For the sake of running a clean test, let's power-cycle the device. As expected, Linux comes up to the shell:

Freeing init memory: 3452K
init started: BusyBox v1.17.0 (2015-07-23 12:52:55 +0400)
~ #

First thing is to mount the JFFS2 file system:

~ # mount -t jffs2 /dev/mtdblock5 /mnt
~ #

Next step is to associate the backing file with a loopback block device, which allows us to mount a FAT32 file system on the backing file:

~ # losetup /dev/loop0 /mnt/fat32.part
~ # mkdir -p /tmp/m
~ # mount /dev/loop0 /tmp/m
~ #

Now, it is time to start the File-backed Storage Gadget making the FAT32 file system mounted on the backing file visible to a USB host as a storage device:

~ # insmod /g_file_storage.ko file=/mnt/fat32.part stall=0 removable=1
g_file_storage gadget: controller 'fsl-usb2-udc' not recognized
g_file_storage gadget: No serial-number string provided!
g_file_storage gadget: File-backed Storage Gadget, version: 1 September 2010
g_file_storage gadget: Number of LUNs=1
g_file_storage gadget-lun0: ro=0, nofua=0, file: /mnt/fat32.part
fsl-usb2-udc: bind to driver g_file_storage
~ #

Let's "harvest" some data and store what is collected into a file in the FAT32 file system. In this demo, we emulate a data stream by taking a snapshot of the system time each second:

~ # while date >> /tmp/m/data.log; do sleep 1; done

Having let the "data harvesting" run for a while, let's interrupt it (by pressing ^-C) and take a look at what data we have collected:

^C

~ # ls -l /tmp/m/data.log
-rwxr-xr-x 1 root root 319 Aug 18 01:57 /tmp/m/data.log
~ # cat /tmp/m/data.log
Fri Aug 18 01:56:52 UTC 2000
Fri Aug 18 01:56:53 UTC 2000
Fri Aug 18 01:56:54 UTC 2000
Fri Aug 18 01:56:55 UTC 2000
Fri Aug 18 01:56:56 UTC 2000
Fri Aug 18 01:56:57 UTC 2000
Fri Aug 18 01:56:58 UTC 2000
Fri Aug 18 01:56:59 UTC 2000
Fri Aug 18 01:57:00 UTC 2000
Fri Aug 18 01:57:01 UTC 2000
Fri Aug 18 01:57:02 UTC 2000

All looks as expected, so we are ready to unmount the device and try accessing the FAT32 file system from a USB host.

~ # umount /dev/loop0

Processing Data on the Host

Given the above set-up, a host will be able to see the Vybrid as a removable disk as soon as a USB connection is made between a free port on the host and the USB P13 connector on the target. A message like this will appear on the target console when a connection to the host has been made:

~ # g_file_storage gadget: high speed config #1

Host software should recognize the newly plugged USB device as a storage device and mount it as a FAT32 file system. Data collected on the target can be copied to other host disks for further processing.

Note that the format of Windows and Unix text files differs slightly. In Windows, lines end with both the line feed and carriage return ASCII characters, but Unix uses only a line feed. As a consequence, some Windows applications will not show the line breaks in Unix-format files. Assuming that data is stored in a text file (vs a binary file) and Windows is a data processing host, Linux data harvesting applications should take care of the difference by adding a carriage return character to data logs.

Data Synchronization Issues

It is important to note that it is not safe to update the USB storage both on the target and host sides at the same time. Here is what the File-backed Storage Gadget documentation has to say about this:

AN IMPORTANT WARNING! While FSG is running and the gadget is connected to a USB host, that USB host will use the backing storage as a private disk drive. It will not expect to see any changes in the backing storage other than the ones it makes. Extraneous changes are liable to corrupt the filesystem and may even crash the host. Only one system (normally, the USB host) may write to the backing storage, and if one system is writing that data, no other should be reading it. The only safe way to share the backing storage between the host and the gadget's operating system at the same time is to make it read-only on both sides.

Given that the primary use of this technology is to let the host copy harvested data for further processing, this should be an acceptable limitation for most applications. It is important to understand however that the host will see the USB storage in a state it was in when the USB cable was connected between the target and the host. Further updates made to the storage by the target will not be visible on the host until a next plug-in.

As mentioned above, updates made on the host side may be unsafe in case the target continues harvesting data when the host is connected. Probably, the best strategy is to assume a read-only mount of the USB storage on the host side and let application code on the target take care of purging data at appropriate times. Note the File-backed Storage Gadget provides a special flag (ro=1) to allows mounting the USB storage for read-only access by the host.