USB Component  Version 6.5
MDK-Professional Middleware for USB Device and Host
 All Data Structures Functions Variables Enumerations Enumerator Groups Pages
MSC: Mass Storage Class Functions

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

Content

 Configuration
 Configuration of the USB Device MSC Class in µVision.
 

Functions

void USBD_MSCn_Initialize (void)
 Called during USBD_Initialize to initialize the USB MSC class Device.
 
void USBD_MSCn_Uninitialize (void)
 Called during USBD_Uninitialize to de-initialize the USB MSC class Device.
 
bool USBD_MSCn_GetCacheInfo (uint32_t *buffer, uint32_t *size)
 Get cache information.
 
bool USBD_MSCn_GetMediaCapacity (uint32_t *block_count, uint32_t *block_size)
 Get media capacity.
 
bool USBD_MSCn_Read (uint32_t lba, uint32_t cnt, uint8_t *buf)
 Read data from media.
 
bool USBD_MSCn_Write (uint32_t lba, uint32_t cnt, const uint8_t *buf)
 Write data to media.
 
uint32_t USBD_MSCn_CheckMedia (void)
 Check media presence and write protect status.
 

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_5

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

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-2015 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.1
*----------------------------------------------------------------------------*/
#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)
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 Device MSC class instance n.
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 < 0U) { 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 \ref USBD_Uninitialize to de-initialize the USB Device MSC class instance n.
// 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, uint16_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, uint16_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;
default:
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:Host:MSC
* Copyright (c) 2004-2015 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
*----------------------------------------------------------------------------*/
/*
* 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 "stdint.h" // data type definitions
#include "stdbool.h" // data type definitions
#include "cmsis_os.h" // Keil.MDK-Pro::CMSIS:CORE
#include "USBD_MSC_n.h" // Media ownership control for USB Device
extern volatile uint8_t usbd_mscn_media_own; // USB MSCn media ownership
int32_t USBD_MSCn_SetMediaOwnerUSB (void) {
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) {
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;
}
:

Function Documentation

uint32_t USBD_MSCn_CheckMedia ( void  )

Check media presence and write protect status.

Returns
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

The function USBD_MSCn_CheckMedia is called automatically upon specific USB Host requests (like Test Unit Ready) to check if media is ready for read/write operations. It needs no invocation in the user code. Modify this function to the application's needs. If media check is not available in hardware this function can be omitted.

Code Example

#include "rl_usb.h"
uint32_t USBD_MSC0_CheckMedia (void) {
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;
default:
break;
}
// Store current owner so no new request can interfere
own = usbd_msc0_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_msc0_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_msc0_media_own & MEDIA_OWN_USB))) {
return 0U;
}
return media_state;
}
bool USBD_MSCn_GetCacheInfo ( uint32_t *  buffer,
uint32_t *  size 
)

Get cache information.

Parameters
[out]buffercache buffer address.
[out]sizecache buffer size.
Returns
true operation succeeded.
false operation failed.

The function USBD_MSCn_GetCacheInfo enables an MSC device to reuse RAM used by the File System for caching purposes as USB cache. This is required when a USB Host has control over the media so that it cannot be accessed by File System.

The argument buffer returns the cache buffer address, while the argument size returns the size of the cache buffer.

Code Example

#include "rl_usb.h"
bool USBD_MSC0_GetCacheInfo (uint32_t *buffer, uint32_t *size) {
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
return true;
}
bool USBD_MSCn_GetMediaCapacity ( uint32_t *  block_count,
uint32_t *  block_size 
)

Get media capacity.

Parameters
[out]block_counttotal number of blocks on media.
[out]block_sizemedia block size.
Returns
true operation succeeded.
false operation failed.

The function USBD_MSCn_GetMediaCapacity can be used to determine the geometry of the attached storage media.

The argument block_count returns the total number of blocks on the media, while the argument block_size returns the block size of the media.

Code Example

#include "rl_usb.h"
bool USBD_MSC0_GetMediaCapacity (uint32_t *block_count, uint32_t *block_size) {
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
return true;
}
void USBD_MSCn_Initialize ( void  )

Called during USBD_Initialize to initialize the USB MSC class Device.

Returns
none.

The function USBD_MSCn_Initialize is called automatically upon initialization of a Mass Storage Class Device and needs no invocation in the user code. Modify this function to the application's needs to allocate resources and initialize additional peripherals.

Code Example

#include "rl_usb.h"
void USBD_MSC0_Initialize (void) {
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
}
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
}
drv_id = fs_ioc_get_id (MEDIA_DRIVE); // Get ID of media drive
if (drv_id < 0U) { return; } // If ID is invalid exit
if (fs_ioc_lock (drv_id)) { // Lock media for USB usage
return; // Exit if failed
}
media_ok = true; // Media was initialized and is ok
}
bool USBD_MSCn_Read ( uint32_t  lba,
uint32_t  cnt,
uint8_t *  buf 
)

Read data from media.

Parameters
[in]lbalogical address of first block to read.
[in]cntnumber of contiguous blocks to read from media.
[out]bufdata buffer for data read from media.
Returns
true read succeeded.
false read failed.

The function USBD_MSCn_Read reads the data that should be returned to the USB Host that requested it. Modify this function to the application's needs.

The argument lba specifies the logical address of the first block that is to be read.

The argument cnt specifies the number of contiguous blocks to be read from the media.

The argument buf is pointing to the buffer where the read data should be stored.

Code Example

#include rl_usb.h
bool USBD_MSC0_Read (uint32_t lba, uint32_t cnt, uint8_t *buf) {
// Read data directly from media
if (fs_ioc_read_sector (drv_id, lba, buf, cnt) != fsOK) {
return false;
}
return true;
}
void USBD_MSCn_Uninitialize ( void  )

Called during USBD_Uninitialize to de-initialize the USB MSC class Device.

Returns
none.

The function USBD_MSCn_Uninitialize is called automatically upon de-initialization of a Mass Storage Class Device and needs no invocation in the user code. If USBD_MSCn_Initialize has been adapted to the application, USBD_MSCn_Uninitialize should release resources and should de-initialize peripherals.

Code Example

#include "rl_usb.h"
int main (void) {
..
USBD_Initialize (0); // USB Device 0 Initialization
...
USBD_Uninitialize (0); // USB Device 0 De-Initialization
..
}
bool USBD_MSCn_Write ( uint32_t  lba,
uint32_t  cnt,
const uint8_t *  buf 
)

Write data to media.

Parameters
[in]lbalogical address of first block to write.
[in]cntnumber of contiguous blocks to write to media.
[out]bufdata buffer containing data to write to media.
Returns
true write succeeded.
false write failed.

The function USBD_MSCn_Write writes data received from the USB Host. Modify this function to the application's needs.

The argument lba specifies the logical address of the first block that is to be written.

The argument cnt specifies the number of contiguous blocks to be written to the media.

The argument buf is pointing to the buffer containing the data to be written.

Code Example

#include rl_usb.h
bool USBD_MSC0_Write (uint32_t lba, uint32_t cnt, const uint8_t *buf) {
// Write data directly to media
if (fs_ioc_write_sector (drv_id, lba, buf, cnt) != fsOK) {
return false;
}
return true;
}