USB Device MSC Class#
This section describes the mass storage device class (MSC) supported by Silicon Labs USB Device. MSC is a protocol that enables the transfer of information between a USB device and a host. The information being transferred is anything that can be stored electronically, such as executable programs, source code, documents, images, configuration data, or other text or numeric data. The USB device appears as an external storage medium to the host, enabling the transfer of files via drag and drop.
A file system defines how the files are organized in the storage media. The USB mass storage class specification does not require any particular file system to be used on conforming devices. Instead, it provides a simple interface to read and write sectors of data using the Small Computer System Interface (SCSI) transparent command set. As such, operating systems may treat the USB drive like a hard drive, and can format it with any file system they like.
The USB mass storage device class supports two transport protocols, as follows:
Bulk-Only Transport (BOT)
Control/Bulk/Interrupt (CBI) Transport (used only for floppy disk drives)
The mass storage device class implements the SCSI transparent command set using the BOT protocol only, which signifies that only bulk endpoints will be used to transmit data and status information. The MSC implementation supports multiple logical units.
The MSC implementation is in compliance with the following specifications:
Universal Serial Bus Mass Storage Class Specification Overview, Revision 1.3 Sept. 5, 2008.
Universal Serial Bus Mass Storage Class Bulk-Only Transport, Revision 1.0 Sept. 31, 1999.
USB Device MSC Class Overview#
Protocol#
In this section, we will discuss the Bulk-Only Transport (BOT) protocol of the Mass Storage Class. The Bulk-Only Transport protocol has three stages:
The Command Transport
The Data Transport
The Status Transport
Mass storage commands are sent by the host through a structure called the Command Block Wrapper (CBW). For commands requiring a data transport stage, the host will attempt to send or receive the exact number of bytes from the device as specified by the length and flag fields of the CBW. After the data transport stage, the host attempts to receive a Command Status Wrapper (CSW) from the device that details the status of the command as well as any data residue (if any). For commands that do not include a data transport stage, the host attempts to receive the CSW directly after CBW is sent. The protocol is detailed in Figure - MSC Protocol.
Figure - MSC Protocol#
Endpoints#
On the device side, in compliance with the BOT specification, the MSC is composed of the following endpoints:
A pair of control IN and OUT endpoints called default endpoint.
A pair of bulk IN and OUT endpoints.
The table below indicates the different usages of the endpoints.
Table - MSC Endpoint Usage#
Endpoint | Direction | Usage |
---|---|---|
Control IN | Device to Host | Enumeration and MSC class-specific requests |
Control OUT | Host to Device | Enumeration and MSC class-specific requests |
Bulk IN | Device to Host | Send CSW and data |
Bulk OUT | Host to Device | Receive CBW and data |
Class Requests#
There are two defined control requests for the MSC BOT protocol. These requests and their descriptions are detailed in the table below.
Table - Mass Storage Class Requests#
Class Requests | Description |
---|---|
Bulk-Only Mass Storage Reset | This request is used to reset the mass storage device and its associated interface. This request readies the device to receive the next command block. |
Get Max LUN | This request is used to return the highest logical unit number (LUN) supported by the device. For example, a device with LUN 0 and LUN 1 will return a value of 1. A device with a single logical unit will return 0 or stall the request. The maximum value that can be returned is 15. |
Small Computer System Interface (SCSI)#
At the programming interface level, the MSC device implements one of the standard storage-media communication protocols, like SCSI and SFF-8020i (ATAPI). The "Programming Interface" specifies which protocol is implemented, and helps the host operating system to load the suitable device driver for communicating with the USB storage device. SCSI is the most common protocol used with USB MSC storage devices. We provide an implementation for MSC SCSI subclass that our GSDK users can use out of the box.
SCSI is a set of standards for handling communication between computers and peripheral devices. These standards include commands, protocols, electrical interfaces and optical interfaces. Storage devices that use other hardware interfaces, such as USB, use SCSI commands for obtaining device/host information and controlling the device’s operation and transferring blocks of data in the storage media.
SCSI commands cover a vast range of device types and functions and as such, devices need a subset of these commands. In general, the following commands are necessary for basic communication:
INQUIRY
READ CAPACITY(10)
READ(10)
REQUEST SENSE
TEST UNIT READY
WRITE(10)
USB Device MSC Class Resource Needs from Core#
Each time you add an MSC class instance to a USB configuration via the function sl_usbd_msc_add_to_configuration()
, the following resources will be allocated from the core.
Resource | Quantity |
---|---|
Interfaces | 1 |
Alternate interfaces | 1 |
Endpoints | 2 |
Interface groups | 0 |
Note that those numbers are per configuration. When setting up your SL_USBD_INTERFACE_QUANTITY
, SL_USBD_ALT_INTERFACE_QUANTITY
, SL_USBD_INTERFACE_GROUP_QUANTITY
and SL_USBD_DESCRIPTOR_QUANTITY
configuration values, don't forget to take into account on how many configurations the class will be added.
For the SL_USBD_OPEN_ENDPOINTS_QUANTITY
configuration value, since endpoints are opened only when a configuration is set by the host, you just need to take into account the number of needed endpoints for a class instance.
USB Device MSC Class Configuration#
Two groups of configuration parameters are used to configure the MSC class:
USB Device MSC Class Application-Specific Configurations#
Class Compile-Time Configurations#
Silicon Labs USB Device MSC class and SCSI subclass are configurable at compile time via #defines located in the sl_usbd_core_config.h file.
Table - Generic Configuration Constants#
Configuration Name | Description | Default Value |
---|---|---|
| Number of class instances you will allocate via a call to the function | 2 |
| Number of configuration to which a class instance can be added via a call to the function | 1 |
| Number of logical units per class instance that you will add via a call to the function | 2 |
| Enables or disables support for Logical Block Address (LBA) of 64 bits. | 0 |
| Size of data buffer per class instance in bytes | 512 |
Class Instance Creation#
Creating a USB Device MSC SCSI class instance is done by calling the sl_usbd_msc_scsi_create_instance()
function. This function takes one configuration argument that is described below.
p_scsi_callbacks#
p_scsi_callbacks is a pointer to a configuration structure of type sl_usbd_msc_scsi_callbacks_t
. In addition to the common usb device class callbacks connect/disconnect, it provides the MSC class with a set of optional callback functions that are called when an event occurs on the logical unit. A null pointer (NULL
) can be passed to this argument if no callbacks are needed.
The table below describes each configuration field available in this configuration structure.
Table - sl_usbd_msc_scsi_callbacks_t Configuration Structure#
Fields | Description | Function Signature |
---|---|---|
.enable | Called when the USB class instance is enabled successfully. | void app_usbd_msc_scsi_enable(uint8_t class_nbr); |
.disable | Called when the USB class instance is disabled. | void app_usbd_msc_scsi_disable(uint8_t class_nbr); |
.host_eject | Function called when a logical unit is ejected from the host. | void app_usbd_msc_scsi_host_eject(uint8_t class_nbr, uint8_t lu_nbr); |
USB Device MSC Class Logical Unit Configuration#
Adding a logical unit to an MSC class instance is done by calling the function sl_usbd_msc_lun_add()
. This function takes one configuration argument that is described below.
p_lu_info#
p_lu_info
is a pointer to a structure of type sl_usbd_msc_scsi_lun_info_t
. Its purpose is to provide the information on the logical unit to the MSC class.
The table below describes each configuration field available in this configuration structure.
Table - sl_usbd_msc_scsi_lun_info_t Configuration Structure#
Field | Description |
---|---|
| Pointer to the media driver API that will handle this logical unit. See USB Device MSC Class Storage Drivers for more information on storage drivers. |
| Pointer to a string that contains the vendor identification of the logical unit. The maximum length of the string is 8 characters. |
| Pointer to a string that contains the product identification of the logical unit. The maximum length of the string is 16 characters. |
| Product revision level. |
| Flag that indicates if the logical unit should be seen as read only from the point of view of the host ( |
USB Device MSC Class Programming Guide#
This section explains how to use the MSC class.
Initializing the USB Device MSC Class#
To add MSC SCSI class functionality to your device, first initialize the MSC base class and the SCSI subclass by calling the function sl_usbd_msc_init()
and sl_usbd_msc_scsi_init()
.
The example below shows how to call sl_usbd_msc_init()
and sl_usbd_msc_scsi_init()
.
Example - Calling sl_usbd_msc_init() and sl_usbd_msc_scsi_init()#
sl_status_t status;
status = sl_usbd_msc_init();
if (status != SL_STATUS_OK) {
/* An error occurred. Error handling should be added here. */
}
status = sl_usbd_msc_scsi_init();
if (status != SL_STATUS_OK) {
/* An error occurred. Error handling should be added here. */
}
Adding a USB Device MSC SCSI Class Instance to Your Device#
To add MSC SCSI class functionality to your device, first create an instance, then add it to your device's configuration(s). You must add at least one logical unit to your instance.
Creating an MSC SCSI Class Instance#
Create a MSC SCSI class instance by calling the function sl_usbd_msc_scsi_create_instance()
.
The example below shows how to call sl_usbd_msc_scsi_create_instance()
using default arguments. For more information on the configuration arguments to pass to sl_usbd_msc_scsi_create_instance()
, see USB Device MSC Class Application Specific Configurations .
Example - Calling sl_usbd_msc_scsi_create_instance()
#
uint8_t class_nbr;
sl_status_t status;
sl_usbd_msc_scsi_callbacks_t app_usbd_msc_scsi_callbacks = {
.enable = NULL,
.disable = NULL,
.host_eject = NULL
};
status = sl_usbd_msc_scsi_create_instance(&app_usbd_msc_scsi_callbacks,0
&class_nbr);
if (status != SL_STATUS_OK) {
/* An error occurred. Error handling should be added here. */
}
Adding the MSC Class Instance to Your Device's Configuration(s)#
After you have created an MSC class instance, you can add it to a configuration by calling the function sl_usbd_msc_add_to_configuration()
.
The example below show how to call sl_usbd_msc_scsi_add_to_configuration()
using default arguments.
Example - Calling sl_usbd_msc_scsi_add_to_configuration()
#
sl_status_t status;
status = sl_usbd_msc_scsi_add_to_configuration(class_nbr, (1)
config_nbr_fs); (2)
if (status != SL_STATUS_OK) {
/* An error occurred. Error handling should be added here. */
}
(1) Class number to add to the configuration returned by sl_usbd_msc_scsi_create_instance()
.
(32) Configuration number (here adding it to a Full-Speed configuration).
USB Device MSC Class Logical Unit Handling#
Adding a Logical Unit#
When adding a logical unit to your MSC SCSI class instance, it must be bound to a storage medium (RAMDisk, SD card, flash memory, etc). The MSC class uses a storage driver to communicate with storage media. This driver will need to be supply when adding the logical unit.
The example below shows how to add a logical unit via sl_usbd_msc_scsi_lun_add()
.
Example - Adding a Logical Unit via sl_usbd_msc_scsi_lun_add()#
sl_usbd_msc_scsi_lun_t *lu_object_ptr = NULL;
sl_usbd_msc_scsi_lun_info_t lu_info;
sl_status_t status;
lu_info.sl_usbd_msc_scsi_lun_api_t = &app_usbd_scsi_storage_block_device_api;
lu_info.vendor_id_ptr = "Silicon Labs";
lu_info.product_id_ptr = "block device example";
lu_info.product_revision_level = 0x1000u;
lu_info.is_read_only = false;
status = sl_usbd_msc_scsi_lun_add(class_nbr,
&lu_info,
&lu_object_ptr);
if (status != SL_STATUS_OK) {
/* An error occurred. Error handling should be added here. */
}
Attaching/Detaching a Storage Medium#
After the logical unit has been added, a storage medium must be attached to be available from the host side.
The MSC class offers two functions to control the storage media association to the logical unit: sl_usbd_msc_scsi_lun_attach()
and sl_usbd_msc_scsi_lun_detach()
. These functions allow you to emulate the removal of a storage device in order to re-gain access from the embedded application if necessary.
The example below shows how to use the function sl_usbd_msc_scsi_lun_attach()
and sl_usbd_msc_scsi_lun_detach()
.
Example - Media Attach/Detach#
sl_status_t status;
status = sl_usbd_msc_scsi_lun_attach(lu_object_ptr);
if (status != SL_STATUS_OK) {
/* An error occurred. Error handling should be added here. */
}
... (1)
status = sl_usbd_msc_scsi_lun_detach(lu_object_ptr);
if (status != SL_STATUS_OK) {
/* An error occurred. Error handling should be added here. */
}
... (2)
status = sl_usbd_msc_scsi_lun_attach(lu_object_ptr)
if (status != SL_STATUS_OK) {
/* An error occurred. Error handling should be added here. */
}
... (3)
(1) From this moment, if the MSC device is connected to a host, the storage media is accessible.
(2) If the MSC device is connected to a host, the media will now appear as unavailable. At this moment, operations can be performed on the media from the embedded application.
(3) Again, if the MSC device is connected to the host, the storage media will appear as connected.
USB Device MSC Class Storage Drivers#
The USB Device MSC Class needs a storage driver to communicate with a storage medium. For the moment, Silicon Labs doesn't offer drivers.
The driver API is defined by typedef sl_usbd_msc_scsi_lun_api_t
. Your sl_usbd_msc_scsi_lun_api_t
variable must be included to your sl_usbd_msc_scsi_lun_info_t
variable, passed as argument when you add a logical unit with sl_usbd_msc_scsi_lun_add()
. See section USB Device MSC SCSI API for more details on the structures.
The storage driver implementation can be as simple as an array of sectors in RAM. Typical sector size (i.e., block size) is 512 for mass storage devices, and 2048 for CD-ROMs.