Backing Storage for the Mass Storage Gadget
Last Modified:
20 March 2013
The Mass Storage Gadget (MSG) provides support for the USB
Mass Storage class. It can appear to a host as a set of up to 8 SCSI
disk drives (called Logical Units often referred to as LUNs,
even though it technically stands for Logical Unit Number), although
most of the time a single LUN is all you will need. The information
stored for each LUN must be maintained by the gadget somewhere, either
in a normal file or in a block device such as a disk partition or even
a ramdisk. This file or block device is called the backing
storage for the gadget, and you tell MSG where the backing storage
is when you load the gadget driver:
bash# modprobe g_mass_storage file=/root/data/backing_file
|
This command tells MSG to provide a single LUN with backing storage
maintained in /root/data/backing_file. If you wanted to have
two LUNs, where the second LUN used /dev/hda7 as its backing
storage, you would do:
bash# modprobe g_mass_storage file=/root/data/backing_file,/dev/hda7
|
Under Linux 2.6, if you add "removable=y" to the
modprobe line then MSG will act like a device with removable
media and allow you to specify the backing storage using sysfs
attributes. In fact, if you do this then you can omit the
"file=..." parameter entirely. The gadget will resemble a
ZIP drive with no cartridge inserted until you use sysfs to
specify some backing storage.
AN IMPORTANT WARNING! While MSG 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.
Creating a backing storage file
Backing storage requires some preparation before MSG can use it.
To start with, if the backing storage is a regular file then the file
must be created beforehand, with its full desired size. (MSG won't
create a backing storage file and won't change the size of an existing
file.) In the example above, if you wanted
/root/data/backing_file to represent a 64MB drive then you
would have to create it using a command something like this:
bash# dd bs=1M count=64 if=/dev/zero of=/root/data/backing_file
64+0 records in
64+0 records out
|
This has to be done before you can load g_mass_storage, but
it only has to be done once. If the backing storage is a block device
or disk partition such as /dev/hda7 then you don't have to
create it beforehand, because it will already exist.
Partitioning the backing storage
However, creating the backing storage isn't enough. It's like
having a raw disk drive; you still need to partition the disk and
install a filesystem before you can use it. (Strictly speaking you
don't need to partition it. You can treat the entire drive
as a single large device, like a floppy disk. This will be confusing,
though, and some versions of Windows won't work with an unpartitioned
USB drive.) Okay, so how do you partition the backing storage?
Answer: You create a partition table by using the fdisk
program. Here's an example showing how to do it. The example assumes
you will want to use the gadget with a Windows host. It's a little
tricky because fdisk needs help when working with something
other than an actual device. Begin by starting up fdisk and
telling it the name of your backing storage.
You'll get a message something like this:
bash# fdisk /root/data/backing_file
Device contains neither a valid DOS partition table, nor Sun or SGI disklabel
Building a new DOS disklabel. Changes will remain in memory only,
until you decide to write them. After that, of course, the previous
content won't be recoverable.
You must set heads sectors and cylinders.
You can do this from the extra functions menu.
Command (m for help):
|
Heads, Sectors, and Cylinders
As you see, fdisk says that it needs you to set the heads,
sectors, and cylinders values. (Some versions only say they need you
to set the number of cylinders, but they're wrong. You can tell by
the way they'll miscalculate the size of the backing file; they're
using default values and ignoring the actual file size.) The
numbers you use are somewhat arbitrary; the scheme shown here works
okay. Give the "x" (eXpert or eXtra) command:
Then set the number of sectors/track. g_mass_storage uses a
sector size of 512 bytes, so 8 sectors/track will give us 4096 bytes
per track. This is good because it matches the size of a memory page
(on a 32-bit processor).
Expert command (m for help): s
Number of sectors (1-63): 8
Warning: setting sector offset for DOS compatiblity
|
Next set the number of heads (or tracks/cylinder). With 4 KB per
track, 16 heads will give us a total of 64 KB per cylinder, which is
convenient since the size of the backing file is 64 MB.
Expert command (m for help): h
Number of heads (1-256): 16
|
Finally set the number of cylinders. It's important that the total
size you specify matches the actual size of the backing file. Since
we've got 64 KB per cylinder and 64 MB total, we need to use 1024
cylinders.
Expert command (m for help): c
Number of cylinders (1-131071): 1024
|
Now return to the normal menu (the "r" command):
Expert command (m for help): r
|
Creating a primary partition
Create a new primary partition ("n" for new). Let's make it number 1.
The defaults for the starting and ending cylinder are perfect because
they will make the partition occupy the entire backing file, so just
press Enter when asked for the First and Last cylinder:
Command (m for help): n
Command action
e extended
p primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-1024, default 1):
Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-1024, default 1024):
Using default value 1024
|
The new partition is created by default as a Linux partition. Since
you want to use the gadget with a Windows host, you should change the
partition type (the "t" command) to FAT32 (code "b"):
Command (m for help): t
Partition number (1-4): 1
Hex code (type L to list codes): b
Changed system type of partition 1 to b (Win95 FAT32)
|
Print out ("p") the new partition table to be sure everything's correct:
Command (m for help): p
Disk /root/data/backing_file: 16 heads, 8 sectors, 1024 cylinders
Units = cylinders of 128 * 512 bytes
Device Boot Start End Blocks Id System
/root/data/backing_file1 1 1024 65532 b Win95 FAT32
|
Finally write out ("w") the partition table to the backing storage:
Command (m for help): w
The partition table has been altered!
Calling ioctl() to re-read partition table.
Re-read table failed with error 25: Inappropriate ioctl for device.
Reboot your system to ensure the partition table is updated.
WARNING: If you have created or modified any DOS 6.x
partitions, please see the fdisk manual page for additional
information.
Syncing disks.
|
Adding a filesystem
At this point a new partition has been created but it doesn't yet
contain a filesystem. The easiest way to add a filesystem is to load
g_mass_storage, connect the gadget to a USB host, and use the
host to do the work. With a Linux host you can run mkdosfs;
with a Windows host you can double-click on the drive's icon in the
"My Computer" window.
Accessing the backing storage from the gadget
It is possible to manipulate the data in the backing storage from
the gadget (even to add the filesystem). Don't do this while the
gadget is connected to a USB host! The key is to use the
loop device driver with the "-o" (offset) option for
the losetup program.
For this to work, you have to know what the partition's offset is.
If you followed the partitioning scheme given above then the offset
will always be 4096. If not, you can use fdisk to find the
correct offset value:
# fdisk -lu /root/data/backing_file
You must set cylinders.
You can do this from the extra functions menu.
Disk data: 0 MB, 0 bytes
16 heads, 8 sectors/track, 0 cylinders, total 0 sectors
Units = sectors of 1 * 512 = 512 bytes
Device Boot Start End Blocks Id System
/root/data/backing_file1 8 8191 4092 b Win95 FAT32
|
Ignore the bogus data at the top and concentrate on the table at
the bottom. The number you want is the value in the "Start" column.
It gives the offset in sectors; to convert to bytes you must multiply
by 512. So we see that the offset is 8 x 512 = 4096 bytes.
You use the losetup program to set up the loop
device driver with the proper offset:
# losetup -o 4096 /dev/loop0 /root/data/backing_file
|
Now /dev/loop0 is mapped to the partition within the backing
storage. You can create a filesystem on it:
and then you can mount it:
# mount -t vfat /dev/loop0 /mnt/loop
|
Now you can transfer files back and forth to your heart's content!
When you're done, be sure to unmount and detach the loop
device:
# umount /dev/loop0
# losetup -d /dev/loop0
|