Applications - USB Mass Storage
Introduction
Learn how the Mass Storage Gadget (MSG) functionality of the SAMA5D2 Series Arm® Cortex®-A5 Microprocessor Unit (MPU) is enabled in the Linux® kernel.
According to the "Mass Storage Gadget (MSG)" page from kernel.org, the MSG acts as a USB Mass Storage device, appearing to the host as a disk or a CD-ROM drive. It supports multiple Logical Units (LUNs). Backing storage for each LUN is provided by a regular file or a block device, access can be limited to read-only, and a gadget can indicate that it is removable and/or CD-ROM (the latter implies read-only access).
We show you how to simulate a USB Mass Storage Device. Two functional parts are required to simulate a USB Mass Storage Device:
- USB Device Controller (UDC) driver: provided in the Microchip Linux Board Support Package (BSP)
- Gadget driver: contained in the Linux kernel
Prerequisites
This application is developed for the ATSAMA5D27-SOM1-EK1 development platform:
This application is developed using the Buildroot build system.
Hardware
For this application, you will be controlling J17, the USB 2.0 High-Speed Device of the ATSAMA5D27-SOM1-EK1. The accompanying figure shows the communication capabilities of the SOM1-EK1.
USB 2.0 Device (J17)
Onboard the ATSAMA5D27-SOM1, the ATSAMA5D27C-D1G SiP contains one high-speed USB Device (J17), one high-speed USB Host (J19), and one High-Speed InterChip (HSIC) (J18) Interface.
The USB High-Speed Device peripheral (UDPHS) is connected to J17, a Micro-B USB connector.
Buildroot Configuration
Objective: Using Buildroot, build a bootable image and Flash onto an SD memory card for the ATSAMA5D27-SOM1-EK1 development board.
Follow the steps for building the image on the "Buildroot - Create Project with Default Configuration" page. You will use the default configuration file: atmel_sama5d27_som1_ek_mmc_dev_defconfig.
Device Tree
Objective: Observe how the USB High-Speed Device peripheral (UDPHS) functionality was configured in the Device Tree. No changes are required.
Once Buildroot has completed its build, the UDPHS for the ATSAMA5D27-SOM1-EK1 was configured by a Device Tree. The Device Tree Source (DTS) includes files (*.dtsi and *.dts) are located in the Buildroot output directory: /output/build/linux-linux4sam_6.0/arch/arm/boot/dts/.
Examine the sama5d2.dtsi file and observe the UDPHS functionality assignments:
148 #address-cells = <1>;
149 #size-cells = <0>;
150 compatible = "atmel,sama5d3-udc";
151 reg = <0x00300000 0x100000
152 0xfc02c000 0x400>;
153 interrupts = <42 IRQ_TYPE_LEVEL_HIGH 2>;
154 clocks = <&udphs_clk>, <&utmi>;
155 clock-names = "pclk", "hclk";
156 status = "disabled";
157
158 ep@0 {
159 reg = <0>;
160 atmel,fifo-size = <64>;
161 atmel,nb-banks = <1>;
163 };
164
165 ep@1 {
166 reg = <1>;
167 atmel,fifo-size = <1024>;
168 atmel,nb-banks = <3>;
169 atmel,can-dma;
170 atmel,can-isoc;
171 };
.
.
172-275 ep@2 through ep@15
Line 150 specifies which driver will be used for this USB device.
Line 151 is the definition for FIFO memory region; base address 0x300000, size 0x100000.
Line 152 is the definition for the control memory region; base address 0xfc02c000, size 0x400.
Line 153 shows the PID of usb0 is 42, high level triggered, priority 2.
Line 154 is the definition for usb0 clock.
Line 156 shows the default status as “disabled.” It will be set to “okay” in the at91-sama5d27_som1_ek.dts accompanying file.
Line 158 is the definition for the USB endpoint.
Examine the at91-sama5d27_som1_ek.dts file and observe the UDPHS functionality assignments:
70 atmel,vbus-gpio = <&pioA PIN_PD20 GPIO_ACTIVE_HIGH>;
71 pinctrl-names = "default";
72 pinctrl-0 = <&pinctrl_usba_vbus>;
73 status = "okay";
74 };
Line 70 is the USB vbus pin definition.
Line 72 is the definition for usb0 pins.
Line 73 shows the status of the usb0 device is set to “okay.”
Kernel
Objective: Observe how USB High-Speed Device peripheral (UDPHS) functionality was configured in the Linux kernel. No changes are required.
From the buildroot directory, run the Linux kernel menuconfig:
The top-level menu will be displayed:
Device Drivers
Select [*] USB support ---->
USB Peripheral Controller
Select USB Peripheral Controller ---->
Observe that <M> Atmel USBA is selected.
USBA is the integrated high-speed USB Device controller on the AT32AP700x, some AT91SAM9, and AT91CAP9 processors.
Observe that the <M> module was selected for the USB Device Port High-Speed controller (UDPHS) driver. This driver module will be inserted into the kernel automatically by udev once usb0 in the Device Tree is registered.
USB Gadget Support
Observe that <M> USB Gadget precomposed configurations is selected.
Observe all the supported USB gadgets listed.
Observe that <M> Mass Storage Gadget is selected.
Observe that Mass Storage Gadget has been selected as a <M> module. It will be built as a device driver module.
Observe that other gadget device driver modules have been selected. After building, all driver modules will be stored in the rootfs and they can be dynamically inserted and removed at runtime according to the requirements of running application programs.
Software Design
The USB MSG was built using the default Buildroot configuration atmel_sama5d27_som1_ek_mmc_dev_defconfig (see Buildroot Configuration above).
During the kernel build, the MSG provides mass storage functionality. Keep in mind that the MSG can be built in the kernel image or as a driver module. Because important parameters are passed via module parameters, it is better to build the MSG as a driver module. Otherwise, bootargs in the uboot environment must be modified to pass these parameters to the MSG (g_mass_storage).
The following are descriptions for the MSG-specific module parameters of g_mass_storage (copied from kernel_dir/Documentation/usb/mass-storage.txt):
file=filename[,filename…]
This parameter lists paths to files or block devices used for backing storage for each logical unit. There may be at most FSG_MAX_LUNS (8) LUNs set. If more files are specified, they will be silently ignored. See also “luns” parameter.
Beware that if a file is used as backing storage, it may not be modified by any other process. This is because the host assumes the data does not change without its knowledge. It may be read, but (if the logical unit is writable) due to buffering on the host side, the contents are not well defined.
The size of the logical unit will be rounded down to a full logical block. The logical block size is 2048 bytes for LUNs simulating CD-ROM, the block size of the device if the backing file is a block device, or 512 bytes otherwise.
removable=b[,b…]
This parameter specifies whether each logical unit should be removable. “b” here is either “y”, “Y” or “1” for true or “n”, “N” or “0” for false.
If this option is set for a logical unit, the gadget will accept an “eject” SCSI request (Start/Stop Unit). When it is sent, the backing file will be closed to simulate ejection and the logical unit will not be mountable by the host until a new backing file is specified by userspace on the device (see “sysfs entries” section).
If a logical unit is not removable (the default), a backing file must be specified for it with the file parameter as the module is loaded. The same applies if the module is built-in, with no exceptions.
The default value of the flag is false, however, it used to be true. This has been changed to better match File Storage Gadget, and because it seems like a saner default after all. Thus, to maintain compatibility with older kernels, it's best to specify the default values. Also, if one relied on the old default, explicit “n” needs to be specified now.
Note that “removable” means the logical unit's medium can be ejected or removed (as is true for a CD-ROM drive or a card reader). It does *not* mean that the entire gadget can be unplugged from the host; the proper term for that is “hot-unpluggable”.
cdrom=b[,b…]
This parameter specifies whether each logical unit should simulate CD-ROM. The default is false.
ro=b[,b…]
This parameter specifies whether each logical unit should be reported as read-only. This will prevent the host from modifying the backing files.
Note that if this flag for a given logical unit is false but the backing file could not be opened in read/write mode, the gadget will fall back to read-only mode anyway.
The default value for non-CD-ROM logical units is false; for logical units simulating CD-ROM, it is forced to true.
nofua=b[,b…]
This parameter specifies whether the Force Unit Access (FUA) flag should be ignored in SCSI Write10 and Write12 commands sent to given logical units.
MS Windows mounts removable storage in “Removal optimized mode” by default. All the writes to the media are synchronous, which is achieved by setting the FUA bit in SCSI Write(10,12) commands. This forces each write to wait until the data has been written out and prevents I/O requests aggregation in the block layer dramatically decreasing performance.
Note that this may mean that if the device is powered from USB and the user unplugs the device without unmounting it first (which at least some Windows users do), the data may be lost.
The default value is false.
luns=N
This parameter specifies the number of logical units the gadget will have. It is limited by FSG_MAX_LUNS (8) and higher values will be capped.
If this parameter is provided, and the number of files specified in the file argument is greater than the value of “luns”, all excess files will be ignored.
If this parameter is not present, the number of logical units will be deduced from the number of files specified in the file parameter. If the file parameter is missing as well, one is assumed.
stall=b
Specifies whether the gadget is allowed to halt bulk endpoints. The default is determined according to the type of USB device controller, but usually true.
Composite gadget common parameters:
- idVendor: USB Vendor ID (16-bit integer)
- idProduct: USB Product ID (16-bit integer)
- bcdDevice: USB Device version (BCD) (16-bit integer)
- iManufacturer: USB Manufacturer string (string)
- iProduct: USB Product string (string)
- iSerialNumber: SerialNumber string (string)
Connection
For the hands-on portion of this topic, connect a USB cable to J17 USB 2.0 device (Micro-B Connector) and your PC.
Hands-On with g_mass_storage.ko Module
Simplest Mass Storage Device
The file parameter lists paths to files or block devices used for backing storage for each LUN. In this demonstration, the boot partition on the SD memory card (mmc) was used as backing storage.
Mass Storage Function, version: 2009/09/11
LUN: removable file: (no medium)
LUN: removable file: /dev/mmcblk0p1
Number of LUNs=1
g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
g_mass_storage gadget: userspace failed to provide iSerialNumber
g_mass_storage gadget: g_mass_storage ready
# g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage
Read-Only Mass Storage Device
Mass Storage Function, version: 2009/09/11
LUN: removable file: (no medium)
LUN: removable read only file: /dev/mmcblk0p1
Number of LUNs=1
g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
g_mass_storage gadget: userspace failed to provide iSerialNumber
g_mass_storage gadget: g_mass_storage ready
# g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage
CD-ROM Mass Storage Device
Generate ISO9660 filesystem image first, then use generated *.iso file as LUN’s backing storage.
# ls /mnt
System Volume Information u-boot.bin
at91-sama5d27_som1_ek.dtb zImage
boot.bin
# genisoimage -allow-lowercase -l -o image.iso /mnt
Warning: creating filesystem that does not conform to ISO-9660.
I: -input-charset not specified, using ascii (detected in locale settings)
Total translation table size: 0
Total rockridge attributes bytes: 0
Total directory bytes: 2048
Path table size(bytes): 44
Max brk space used 5000
2342 extents written (4 MB)
# ls
image.iso
# modprobe g_mass_storage file=/root/image.iso removable=1 cdrom=1
Mass Storage Function, version: 2009/09/11
LUN: removable file: (no medium)
LUN: removable read only CD-ROM file: /root/image.iso
Number of LUNs=1
g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
g_mass_storage gadget: userspace failed to provide iSerialNumber
g_mass_storage gadget: g_mass_storage ready
# g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage
Mass Storage Device with Multiple LUNs
Multiple LUNs are supported by the MSG driver. For this demonstration, a new mmc partition was added. iSerialNumber must be provided, otherwise, only the first disk or drive will be detected in Windows (however, there is no such kind in Ubuntu).
Mass Storage Function, version: 2009/09/11
LUN: removable file: (no medium)
LUN: removable read only CD-ROM file: /root/image.iso
LUN: removable read only file: /dev/mmcblk0p1
LUN: removable file: /dev/mmcblk0p3
Number of LUNs=3
g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
g_mass_storage gadget: g_mass_storage ready
# g_mass_storage gadget: high-speed config #1: Linux File-Backed Storage
Hands On with g_mass_storage.ko Built In
As mentioned above in the "Software Design" section, g_mass_storage can be built into the kernel image. However, the following error log will be received when booting if bootargs was not updated in uboot:
LUN: removable file: (no medium)
no file given for LUN0
g_mass_storage 300000.gadget: failed to start g_mass_storage: -22
This error return was caused by the following code in f_mass_storage.c:
pr_err("no file given for LUN%d\n", id);
return -EINVAL;
}
Some important parameters must be passed during g_mass_storage initialization. First, we need to pass parameter “removable” to g_mass_storage by updating bootargs. We show you how to interact with g_mass_storage via sysfs interface below.
The updated bootargs are shown as follows:
Using the uboot console, set bootargs with the following commands:
=> saveenv
=> boot
After bootarg updates successfully, the following booting logs will be displayed:
LUN: removable file: (no medium)
LUN: removable file: (no medium)
Number of LUNs=1
g_mass_storage gadget: Mass Storage Gadget, version: 2009/09/11
g_mass_storage gadget: userspace failed to provide iSerialNumber
g_mass_storage gadget: g_mass_storage ready
Next, insert storage medium /dev/mmcblk0p1 to USB mass storage drive:
# ls
file nofua power ro uevent
# echo /dev/mmcblk0p1 > file
We can also use the following command to eject the storage medium:
Summary
You used Buildroot to build an image with USB Mass Storage device functionality for the ATSAMA5D2 Series MPU. You were provided with hands-on exercises to demonstrate the functionality. You walked through the device tree and kernel to observe how the embedded Linux system configures the source code for building.
Appendix A
How to Add a New Partition to SD Memory Card (mmc) in Buildroot
The following code example will show you how to add one partition to the SD memory card (mmc) for the mass storage function.
Modify the buildroot-at91/board/atmel/sama5d27_som1_ek_mmc/genimage.cfg file to add a new mmc partition:
#
image boot.vfat {
vfat {
files = {
"zImage",
"at91-sama5d27_som1_ek.dtb",
"boot.bin",
"u-boot.bin"
}
}
size = 16M
}
image sdcard.img {
hdimage {
}
partition boot {
partition-type = 0xC
bootable = "true"
image = "boot.vfat"
offset = 1M
}
partition rootfs {
partition-type = 0x83
image = "rootfs.ext4"
size = 512M
}
# add new mmc partition here
partition mass-storage1 {
partition-type = 0x83
size = 32M
}
}
Appendix B
How to Install genisoimage Command in Buildroot
Add [*] cdrkit package support in Buildroot:
cdrkit is a suite of programs for recording CDs and DVDs, blanking CD-RW media, creating ISO-9660 filesystem images, extracting audio CD data, and more.
Then rebuild buildroot: