USB Component  Version 6.12.8
MDK Middleware for USB Device and Host Communication
 All Data Structures Functions Variables Enumerations Enumerator Groups Pages
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:

usbd_msc_media_access.png
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
* Copyright (c) 2004-2017 ARM Germany GmbH. All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_User_MSC_n.c
* Purpose: USB Device Mass Storage Device class (MSC) User module
* Rev.: V6.3.3
*----------------------------------------------------------------------------*/
#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:"
extern bool USBD_MSCn_Read (uint32_t lba, uint32_t cnt, uint8_t *buf);
extern bool USBD_MSCn_Write (uint32_t lba, uint32_t cnt, const uint8_t *buf);
#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:
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)
funmount (MEDIA_DRIVE); // De-initialize media and dismount Drive
} else { // If new requested owner is File System (previous owner was USB)
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) == 0) { // 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-2017 ARM Germany GmbH. All rights reserved.
*------------------------------------------------------------------------------
* Name: USBD_MSC_n.c
* Purpose: Functions for media ownership control between USB and File System
* Rev.: V6.3.3
*----------------------------------------------------------------------------*/
/*
* 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 ".\RTE\USB\USBD_Config_MSC_n.h"
extern volatile uint8_t usbd_mscn_media_own; // USB MSCn media ownership
extern uint32_t USBD_MSCn_CheckMedia (void); // USB MSCn media check callback
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) {
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 */
}
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
* Copyright (c) 2004-2017 ARM Germany GmbH. 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.0
*----------------------------------------------------------------------------*/
#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/512U; // 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;
}