USB Component  Version 6.17.0
MDK Middleware for USB Device and Host Communication
MSC: Mass Storage Class

Implement application specific behavior of a Mass Storage Class (MSC) USB Device. More...

Content

 User API
 User API reference of the Mass Storage Class.
 
 Configuration
 Configuration of the USB Device MSC Class in µVision.
 

Description

Implement application specific behavior of a Mass Storage Class (MSC) USB Device.

The MSC class in the USB Component is used for data storage.

Refer to:

The USB Component allows multiple instances of the MSC class. Each MSC class instance has a separate files and interface functions:

This documentation uses n as a placeholder for the instance number 0 - 3. Most applications only require one instance of a MSC class. For the first MSC class instance the instance number is 0:

Software Structure

The handling for the MSC class endpoint events is implemented in USBD_MSCn_Thread which is started by USBD_Initialize. Each instance of a MSC class runs an instance of USBD_MSCn_Thread which calls the data functions USBD_MSCn_Read and USBD_MSCn_Write.

msc_inline_mscgraph_7

Implementation

To create an USB Device with a MSC class:

Media Ownership

Sometimes, it is required to implement the ownership control over attached media and changing the ownership between USB and File System. This is required if you have a device that connects to a PC as a USB MSC device while the storage media also needs to be accessible to a user application. Using the two functions USBD_MSCn_SetMediaOwnerUSB and USBD_MSCn_SetMediaOwnerFS you can change the owner of the media to either the USB (the host PC) or the File System (the user application). The user code template USBD_MSC.c provides means to manage the ownership.

The following picture shows the connection of the device to the PC and the user application running on the device:

USB MSC Device connected to a PC with a user application accessing the attached storage medium

In the file USBD_MSC.c the variable usbd_msc0_media_own is used to set the ownership of the media device to the application or the File System. In the file USBD_User_MSC.c the variable is used to initialize it at the beginning of the application (in the USBD_MSCn_Initialize function) and to check the ownership of the media (in the USBD_MSCn_CheckMedia function). The application then only makes use of the two functions USBD_MSCn_SetMediaOwnerUSB and USBD_MSCn_SetMediaOwnerFS as explained in this code example.

User Code Templates
There are three user code templates available that help to add support for a MSC device:

  1. USBD_User_MSC.c contains all the callback functions that need to be implemented by the user.
  2. USBD_MSC.c is a code template for the application specific functionality of a USB Device MSC instance and implements ownership control for attached media devices.
  3. USBD_User_MSC_LUN.c is a code template that includes the required callback functions to implement multiple logical units (LUNs).

User Code Template USBD_User_MSC.c

The following source code can be used to implement the application specific behavior of a USB MSC Device.

/*------------------------------------------------------------------------------
* MDK Middleware - Component ::USB:Device:MSC
* Copyright (c) 2004-2020 Arm Limited (or its affiliates). All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_User_MSC_n.c
* Purpose: USB Device Mass Storage Device class (MSC) User module
* Rev.: V6.3.4
*----------------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "rl_usb.h"
// If the USE_FILE_SYSTEM value is 1 then File System is used and media can
// be accessed from application code or can be accessed by USB Mass Storage
// requests, default media in that case is one selected as default in File
// System Configuration file.
// If the USE_FILE_SYSTEM value is 0 then File System is not used and media
// can only be accessed by USB Mass Storage requests, default media in that
// case is RAM memory containing dummy disk image.
#define USE_FILE_SYSTEM 1 // 1 = File System is used, 0 = File System is not used
// Definition MEDIA_DRIVE is used to define Drive to be used for media
// Available options are:
// "R:" or "R0:" if media is RAM
// "M:" or "M0:" if media is Memory Card 0
// "M1:" if media is Memory Card 1
// "N:" or "N0:" if media is NAND Flash 0
// "N1:" if media is NAND Flash 1
#define MEDIA_DRIVE "M0:"
#if (USE_FILE_SYSTEM == 1) // If File System is used
#include "rl_fs.h"
#define MEDIA_OWN_USB (1U ) // Media owned by USB (bit mask)
#define MEDIA_OWN_CHG (1U << 1) // Media ownership change requested (bit mask)
extern
volatile uint8_t usbd_mscn_media_own;
volatile uint8_t usbd_mscn_media_own; // USB MSCn media ownership
static int32_t drv_id; // FAT drive id
static bool media_ok; // Media is initialized and ok
#else
static uint32_t memory [8192/4]; // Memory in RAM for dummy disk image
static uint32_t block_buf[ 512/4]; // Buffer for block read/write to media
extern
const uint8_t memory_disk_image[4096]; // Dummy Memory Disk Image
#endif
// Called during USBD_Initialize to initialize the USB MSC class instance.
void USBD_MSCn_Initialize (void) {
#if (USE_FILE_SYSTEM == 1) // If File System is used
uint32_t param_status;
usbd_mscn_media_own = MEDIA_OWN_USB; // Initially media is owned by USB
media_ok = false; // Current media status (not initialized = not ok)
if (finit (MEDIA_DRIVE) != fsOK) { // Initialize File System
return; // Exit if failed
}
drv_id = fs_ioc_get_id (MEDIA_DRIVE); // Get ID of media drive
if (drv_id < 0) { return; } // If ID is invalid exit
param_status = 0U; // Parameter for function call is 0
// Initialize media
if (fs_ioc_device_ctrl (drv_id, fsDevCtrlCodeControlMedia, &param_status) != fsOK) {
return; // Exit if failed
}
if (fs_ioc_lock (drv_id)) { // Lock media for USB usage
return; // Exit if failed
}
media_ok = true; // Media was initialized and is ok
#else
// Copy the dummy image from code to RAM
memcpy (memory, memory_disk_image, sizeof(memory_disk_image));
#endif
}
// \brief Called during USBD_Uninitialize to de-initialize the USB MSC class instance.
// Add code for de-initialization
}
// Get cache information.
// \param[out] buffer cache buffer address.
// \param[out] size cache buffer size.
// \return true operation succeeded.
// \return false operation failed.
bool USBD_MSCn_GetCacheInfo (uint32_t *buffer, uint32_t *size) {
#if (USE_FILE_SYSTEM == 1) // If File System is used
fsIOC_Cache cache_info;
// Get cache settings of File System
if (fs_ioc_get_cache(drv_id, &cache_info) != fsOK) {
return false; // Exit if failed
}
// Use File Systems cache for MSC
*buffer = (uint32_t)cache_info.buffer;// Cache buffer from File System
*size = cache_info.size; // Cache size
#else
*buffer = (uint32_t)block_buf; // Local buffer for data
*size = sizeof(block_buf); // Size of local buffer
#endif
return true;
}
// Get media capacity.
// \param[out] block_count total number of blocks on media.
// \param[out] block_size media block size.
// \return true operation succeeded.
// \return false operation failed.
bool USBD_MSCn_GetMediaCapacity (uint32_t *block_count, uint32_t *block_size) {
#if (USE_FILE_SYSTEM == 1) // If File System is used
fsMediaInfo media_info;
// Read media information of actual media
if (fs_ioc_read_info(drv_id, &media_info) != fsOK) {
return false; // Exit if failed
}
*block_count = media_info.block_cnt; // Total number of blocks on media
*block_size = media_info.read_blen; // Block size of blocks on media
#else
*block_count = sizeof(memory)/512U; // Total number of blocks on media
*block_size = 512U; // Block size of blocks on media
#endif
return true;
}
// Read data from media.
// \param[in] lba logical address of first block to read.
// \param[in] cnt number of contiguous blocks to read from media.
// \param[out] buf data buffer for data read from media.
// \return true read succeeded.
// \return false read failed.
bool USBD_MSCn_Read (uint32_t lba, uint32_t cnt, uint8_t *buf) {
#if (USE_FILE_SYSTEM == 1) // If File System is used
// Read data directly from media
if (fs_ioc_read_sector (drv_id, lba, buf, cnt) != fsOK) {
return false;
}
#else
// Read data from dummy image in RAM
memcpy (buf, &memory[lba * (512U/4U)], cnt * 512U);
#endif
return true;
}
// Write data to media.
// \param[in] lba logical address of first block to write.
// \param[in] cnt number of contiguous blocks to write to media.
// \param[out] buf data buffer containing data to write to media.
// \return true write succeeded.
// \return false write failed.
bool USBD_MSCn_Write (uint32_t lba, uint32_t cnt, const uint8_t *buf) {
#if (USE_FILE_SYSTEM == 1) // If File System is used
// Write data directly to media
if (fs_ioc_write_sector (drv_id, lba, buf, cnt) != fsOK) {
return false;
}
#else
// Write data to image in RAM
memcpy (&memory[lba * (512U/4U)], buf, cnt * 512U);
#endif
return true;
}
// Check media presence and write protect status.
// (if media is not owned by USB it returns that media is not ready)
// \return media presence and write protected status
// bit 1: write protect bit
// - value 1: media is write protected
// - value 0: media is not write protected
// bit 0: media presence bit
// - value 1: media is present
// - value 0: media is not present
uint32_t USBD_MSCn_CheckMedia (void) {
#if (USE_FILE_SYSTEM == 1) // If File System is used
uint32_t param_status;
uint8_t media_state; // Bit 0. media ready, Bit 1. media write protect
static uint8_t media_ready_ex = 0U; // Previous media ready state
uint8_t own;
// Get current media status
media_state = 0U;
switch (fs_ioc_device_ctrl (drv_id, fsDevCtrlCodeCheckMedia, &param_status)) {
case fsOK:
if (param_status & FS_MEDIA_NOCHKMEDIA) {
// If check media not available on hardware layer
media_state = USBD_MSC_MEDIA_READY;
break;
}
if (param_status & FS_MEDIA_INSERTED) {
media_state = USBD_MSC_MEDIA_READY;
}
if (param_status & FS_MEDIA_PROTECTED) {
media_state |= USBD_MSC_MEDIA_PROTECTED;
}
break;
case fsError:
case fsUnsupported:
case fsAccessDenied:
case fsInvalidParameter:
case fsInvalidDrive:
case fsInvalidPath:
case fsUninitializedDrive:
case fsDriverError:
case fsMediaError:
case fsNoMedia:
case fsNoFileSystem:
case fsNoFreeSpace:
case fsFileNotFound:
case fsDirNotEmpty:
case fsTooManyOpenFiles:
case fsAlreadyExists:
case fsNotDirectory:
break;
}
// Store current owner so no new request can interfere
own = usbd_mscn_media_own;
// De-initialize media according to previous owner
if (own & MEDIA_OWN_CHG) { // If owner change requested
if (own & MEDIA_OWN_USB) { // If new requested owner is USB (previous owner was File System)
(void)funmount (MEDIA_DRIVE); // De-initialize media and dismount Drive
} else { // If new requested owner is File System (previous owner was USB)
(void)fs_ioc_unlock (drv_id); // Un-lock media
}
}
// Initialize media according to current owner
if ((own & MEDIA_OWN_CHG) || // If owner change requested or
(media_state ^ media_ready_ex)) { // if media ready state has changed (disconnect(SD remove)/connect(SD insert))
if (media_state & USBD_MSC_MEDIA_READY) { // If media is ready
if (own & MEDIA_OWN_USB){ // If current owner is USB
media_ok = false; // Invalidate current media status (not initialized = not ok)
param_status = 0U; // Parameter for function call is 0
if (fs_ioc_device_ctrl (drv_id, fsDevCtrlCodeControlMedia, &param_status) == fsOK) {
// Initialization of media has succeeded
if (fs_ioc_lock (drv_id) == fsOK) { // If lock media for USB usage has succeeded
media_ok = true; // Media was initialized and is ok
}
}
} else { // If current owner is File System
if (fmount (MEDIA_DRIVE) == fsOK) { // Initialize media and Mount Drive for File System usage
media_ok = true; // Media was initialized and is ok
}
}
}
if (own & MEDIA_OWN_CHG) {
usbd_mscn_media_own &= ~MEDIA_OWN_CHG; // Clear request to change media owner if it was handled
}
media_ready_ex = media_state & USBD_MSC_MEDIA_READY;
}
// If media is not ok or owned by File System return that it is not ready for USB
if ((!media_ok) || (!(usbd_mscn_media_own & MEDIA_OWN_USB))) {
return 0U;
}
return media_state;
#else
return USBD_MSC_MEDIA_READY;
#endif
}

User Code Template USBD_MSC_n.c

The following source code can be used to implement ownership control for attached media devices.

/*------------------------------------------------------------------------------
* MDK Middleware - Component ::USB:Device:MSC
* Copyright (c) 2004-2020 Arm Limited (or its affiliates). All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_MSC_n.c
* Purpose: Functions for media ownership control between USB and File System
* Rev.: V6.3.6
*----------------------------------------------------------------------------*/
/*
* USBD_MSC_n.c is a code template for the application specific functionality of
* the USB Device MSC class n instance. It implements the ownership control over
* media and changing the owner of media between USB and File System.
*
* USBD_MSC_n.h is the related header file.
*
* To select USB as owner of media you can call function:
* USBD_MSCn_SetMediaOwnerUSB ()
*
* To select File System as owner of media you can call function:
* USBD_MSCn_SetMediaOwnerFS ()
*
*/
#include "rl_usb.h"
#include "USBD_MSC_n.h" // Media ownership control for USB Device
#include "USBD_Config_MSC_n.h"
extern volatile uint8_t usbd_mscn_media_own; // USB MSCn media ownership
uint32_t timeout_cnt;
timeout_cnt = 300U; // 3 second timeout (300 * 10 ms)
usbd_mscn_media_own = USBD_MSCn_MEDIA_OWN_CHG | USBD_MSCn_MEDIA_OWN_USB;
while (usbd_mscn_media_own & USBD_MSCn_MEDIA_OWN_CHG) {
(void)osDelay(10);
if ((--timeout_cnt) == 0) { return USBD_MSCn_ERROR; }
}
return USBD_MSCn_OK;
}
int32_t USBD_MSCn_SetMediaOwnerFS (void) {
uint32_t timeout_cnt;
timeout_cnt = 300U; // 3 second timeout (300 * 10 ms)
usbd_mscn_media_own = USBD_MSCn_MEDIA_OWN_CHG;
while (usbd_mscn_media_own & USBD_MSCn_MEDIA_OWN_CHG) {
if(!USBD_Configured(USBD_MSCn_DEV)) {
/* USB device not configured, so call CheckMedia to do ownership handling */
}
(void)osDelay(10);
if ((--timeout_cnt) == 0) { return USBD_MSCn_ERROR; }
}
return USBD_MSCn_OK;
}

Code Example

This code snippet shows how to use the two functions in a user application:

:
switch (DeviceState) {
case DEV_IDLE:
break;
case DEV_START_DOING_SOMETHING:
// hide logical unit
USBD_MSC0_SetMediaOwnerFS();
// Application has now access to the media
// and can do something here (e.g. fopen, fread, fwrite, fclose, ...)
DeviceState = DEV_DO_IT;
break;
case DEV_STOP_DOING_SOMETHING:
// show logical unit
USBD_MSC0_SetMediaOwnerUSB();
// Media is now under control of the USB Host
// and cannot be accessed by the application
DeviceState = DEV_IDLE;
break;
}
:

User Code Template USBD_User_MSC_LUN.c

The following source code can be used to implement multiple logical units (LUNs) in a USB device.

/*------------------------------------------------------------------------------
* MDK Middleware - Component ::USB:Device:MSC
* Copyright (c) 2004-2020 Arm Limited (or its affiliates). All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_User_MSC_LUN_n.c
* Purpose: USB Device Mass Storage Device class (MSC) with 2 Logical
* Units User module
* Rev.: V1.1.1
*----------------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "rl_usb.h"
static uint32_t memory [2][8192/4]; // Memory in RAM for dummy disk image
static uint32_t block_buf[ 512/4]; // Buffer for block read/write to media
extern
const uint8_t memory_disk_image[4096]; // Dummy Memory Disk Image
// Called during USBD_Initialize to initialize all Logical Units of the USB MSC class instance.
void USBD_MSCn_Initialize (void) {
// Copy the dummy image from code to RAM
memcpy (&memory[0][0], memory_disk_image, sizeof(memory_disk_image));
memcpy (&memory[1][0], memory_disk_image, sizeof(memory_disk_image));
}
// Called during USBD_Uninitialize to de-initialize all Logical Units of the USB MSC class instance.
// Add code for de-initialization
}
// Get cache information.
// \param[out] buffer cache buffer address.
// \param[out] size cache buffer size.
// \return true operation succeeded.
// \return false operation failed.
bool USBD_MSCn_GetCacheInfo (uint32_t *buffer, uint32_t *size) {
*buffer = (uint32_t)block_buf; // Local buffer for data
*size = sizeof(block_buf); // Size of local buffer
return true;
}
// Get maximum number of logical units.
// \return number of logical units that device contains
// - value > 0 and <= 4 : maximum number of logical units
// - value 0 : use setting from configuration file
uint8_t USBD_MSCn_GetMaxLUN (void) {
return 2U; // Device contains 2 logical units
}
// Get media capacity of a logical unit.
// \param[in] lun logical unit number.
// \param[out] block_count total number of blocks on media.
// \param[out] block_size media block size.
// \return true operation succeeded.
// \return false operation failed.
bool USBD_MSCn_LUN_GetMediaCapacity (uint8_t lun, uint32_t *block_count, uint32_t *block_size) {
(void)lun;
*block_count =(sizeof(memory)/2)/512; // Total number of blocks on media
*block_size = 512U; // Block size of blocks on media
return true;
}
// Read data from media of a logical unit.
// \param[in] lun logical unit number.
// \param[in] lba logical address of first block to read.
// \param[in] cnt number of contiguous blocks to read from media.
// \param[out] buf data buffer for data read from media.
// \return true read succeeded.
// \return false read failed.
bool USBD_MSCn_LUN_Read (uint8_t lun, uint32_t lba, uint32_t cnt, uint8_t *buf) {
// Read data from dummy image in RAM
memcpy (buf, &memory[lun][lba * (512U/4U)], cnt * 512U);
return true;
}
// Write data to media of a logical unit.
// \param[in] lun logical unit number.
// \param[in] lba logical address of first block to write.
// \param[in] cnt number of contiguous blocks to write to media.
// \param[out] buf data buffer containing data to write to media.
// \return true write succeeded.
// \return false write failed.
bool USBD_MSCn_LUN_Write (uint8_t lun, uint32_t lba, uint32_t cnt, const uint8_t *buf) {
// Write data to image in RAM
memcpy (&memory[lun][lba * (512U/4U)], buf, cnt * 512U);
return true;
}
// Check media presence and write protect status of a logical unit.
// (if media is not owned by USB it returns that media is not ready)
// \param[in] lun logical unit number.
// \return media presence and write protected status
// bit 1: write protect bit
// - value 1: media is write protected
// - value 0: media is not write protected
// bit 0: media presence bit
// - value 1: media is present
// - value 0: media is not present
uint32_t USBD_MSCn_LUN_CheckMedia (uint8_t lun) {
(void)lun;
return USBD_MSC_MEDIA_READY;
}