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

Referring to it as a gadget might seem a bit confusing. You can also call it a USB Mass Storage Device, which you may find is the more common nomenclature.

We show you how to simulate a USB Mass Storage Device. Two functional parts are required to simulate a USB Mass Storage Device:

  1. USB Device Controller (UDC) driver: provided in the Microchip Linux Board Support Package (BSP)
  2. 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 figure below shows the communication capabilities of the SOM1-EK1.

ATSAMA5D27_SOM1_EK1_communications.png

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.

usb_j17_schematic.png

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 in 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) include files (*.dtsi and *.dts) are located in the Buildroot output directory: /output/build/linux-linux4sam_6.0/arch/arm/boot/dts/.

1

Examine the sama5d2.dtsi file and observe the UDPHS functionality assignments:

147   usb0: gadget@300000 {
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 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 is “disabled.” It will be set to “okay” in the at91-sama5d27_som1_ek.dts file below.

Line 158 is the definition for the USB endpoint.

2

Examine the at91-sama5d27_som1_ek.dts file and observe the UDPHS functionality assignments:

69    usb0: gadget@300000 {
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.

1

From the buildroot directory, run the Linux kernel menuconfig:

$ make linux-menuconfig

The top-level menu will be displayed:

linux-config-top-level.png

Device Drivers

2

Select Device Drivers —->.

linux-config-device-drivers.png

3

Select [*] USB support —->.

usb_support.png

4

Select <*> USB Gadget Support —->.

usb_gadget_support.png

USB Peripheral Controller

5

Select USB Peripheral Controller —->.

usb_peripheral_controller.png

6

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

7

Exit and return to USB Gadget Support.

8

Observe that <M> USB Gadget precomposed configurations is selected.

Observe all the supported USB gadgets listed.

usb_gadget_precomposed.png

9

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 from 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, 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 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 value will be capped.

If this parameter is provided, and the number of files specified in file argument is greater then 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 (sting)

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.

cable_to_usb_j17.png

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.

For how to add new partition to the SD memory card (mmc) in Buildroot see Appendix A below.

# modprobe g_mass_storage file=/dev/mmcblk0p1 removable=1
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

# modprobe g_mass_storage file=/dev/mmcblk0p1 removable=1 ro=1
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.

For how to install genisoimage command in Buildroot, please see Appendix B below.

# mount /dev/mmcblk0p1 /mnt
# 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).

# modprobe g_mass_storage file=/root/image.iso,/dev/mmcblk0p1,/dev/mmcblk0p3 removable=1,1,1 cdrom=1,0,0 ro=1,1,0 iSerialNumber=0000000000000001,0000000000000002,0000000000000003
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:

Mass Storage Function, version: 2009/09/11
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 following code in f_mass_storage.c:

if (!cfg->filename && !cfg->removable) {
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:

bootargs=console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rw rootwait g_mass_storage.removable=1

Using the uboot console, set bootargs with following commands:

=> setenv bootargs "console=ttyS0,115200 earlyprintk root=/dev/mmcblk0p2 rw rootwait g_mass_storage.removable=1"
=> saveenv
=> boot

After bootarg updates successfully, the following booting logs will be displayed:

Mass Storage Function, version: 2009/09/11
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:

# cd /sys/devices/platform/ahb/300000.gadget/gadget/lun0
# ls
file    nofua   power   ro      uevent
# echo /dev/mmcblk0p1 > file

We can also use following command to eject storage medium:

# echo > file

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 for SD card boot on Atmel SAMA5D2 Xplained boards
# 
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.

$ make menuconfig

cdrkit.png

Then rebuild buildroot:

$ make

© 2020 Microchip Technology, Inc.
Notice: ARM and Cortex are the registered trademarks of ARM Limited in the EU and other countries.
Information contained on this site regarding device applications and the like is provided only for your convenience and may be superseded by updates. It is your responsibility to ensure that your application meets with your specifications. MICROCHIP MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO THE INFORMATION, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, PERFORMANCE, MERCHANTABILITY OR FITNESS FOR PURPOSE. Microchip disclaims all liability arising from this information and its use. Use of Microchip devices in life support and/or safety applications is entirely at the buyer's risk, and the buyer agrees to defend, indemnify and hold harmless Microchip from any and all damages, claims, suits, or expenses resulting from such use. No licenses are conveyed, implicitly or otherwise, under any Microchip intellectual property rights.