Implement application specific behavior of a Mass Storage Class (MSC) USB Device.
More...
|
| User API |
| User API reference of the Mass Storage Class.
|
|
| Configuration |
| Configuration of the USB Device MSC Class.
|
|
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:
- A configuration file USBD_Config_MSC_n.h.
- An application-specific user source code file, which can be implemented with the USBD_User_MSC_n.c.
- Functions that start with the prefix USBD_MSCn_ are available for each instance of a MSC class.
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:
- USBD_Config_MSC_0.h
- USBD_User_MSC_0.c
- The function prefix is USBD_MSC0_
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.
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:
- USBD_User_MSC.c contains all the callback functions that need to be implemented by the user.
- 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.
- 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.
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define USE_FILE_SYSTEM 1
#define MEDIA_DRIVE "M0:"
#if (USE_FILE_SYSTEM == 1)
#include "rl_fs.h"
#define MEDIA_OWN_USB (1U )
#define MEDIA_OWN_CHG (1U << 1)
extern
volatile uint8_t usbd_msc%Instance%_media_own;
volatile uint8_t usbd_msc%Instance%_media_own;
static int32_t drv_id;
static bool media_ok;
#else
static uint32_t memory [8192/4];
static uint32_t block_buf[ 512/4];
extern
const uint8_t memory_disk_image[4096];
#endif
void USBD_MSC%Instance%_Initialize (void) {
#if (USE_FILE_SYSTEM == 1)
uint32_t param_status;
usbd_msc%Instance%_media_own = MEDIA_OWN_USB;
media_ok = false;
if (finit (MEDIA_DRIVE) != fsOK) {
return;
}
drv_id = fs_ioc_get_id (MEDIA_DRIVE);
if (drv_id < 0) { return; }
param_status = 0U;
if (fs_ioc_device_ctrl (drv_id, fsDevCtrlCodeControlMedia, ¶m_status) != fsOK) {
return;
}
if (fs_ioc_lock (drv_id)) {
return;
}
media_ok = true;
#else
memcpy (memory, memory_disk_image, sizeof(memory_disk_image));
#endif
}
void USBD_MSC%Instance%_Uninitialize (void) {
}
bool USBD_MSC%Instance%_GetCacheInfo (uint32_t *buffer, uint32_t *size) {
#if (USE_FILE_SYSTEM == 1)
fsIOC_Cache cache_info;
if (fs_ioc_get_cache(drv_id, &cache_info) != fsOK) {
return false;
}
*buffer = (uint32_t)cache_info.buffer;
*size = cache_info.size;
#else
*buffer = (uint32_t)block_buf;
*size = sizeof(block_buf);
#endif
return true;
}
bool USBD_MSC%Instance%_GetMediaCapacity (uint32_t *block_count, uint32_t *block_size) {
#if (USE_FILE_SYSTEM == 1)
fsMediaInfo media_info;
if (fs_ioc_read_info(drv_id, &media_info) != fsOK) {
return false;
}
*block_count = media_info.block_cnt;
*block_size = media_info.read_blen;
#else
*block_count = sizeof(memory)/512U;
*block_size = 512U;
#endif
return true;
}
bool USBD_MSC%Instance%_Read (uint32_t lba, uint32_t cnt, uint8_t *buf) {
#if (USE_FILE_SYSTEM == 1)
if (fs_ioc_read_sector (drv_id, lba, buf, cnt) != fsOK) {
return false;
}
#else
memcpy (buf, &memory[lba * (512U/4U)], cnt * 512U);
#endif
return true;
}
bool USBD_MSC%Instance%_Write (uint32_t lba, uint32_t cnt, const uint8_t *buf) {
#if (USE_FILE_SYSTEM == 1)
if (fs_ioc_write_sector (drv_id, lba, buf, cnt) != fsOK) {
return false;
}
#else
memcpy (&memory[lba * (512U/4U)], buf, cnt * 512U);
#endif
return true;
}
uint32_t USBD_MSC%Instance%_CheckMedia (void) {
#if (USE_FILE_SYSTEM == 1)
uint32_t param_status;
uint8_t media_state;
static uint8_t media_ready_ex = 0U;
uint8_t own;
media_state = 0U;
switch (fs_ioc_device_ctrl (drv_id, fsDevCtrlCodeCheckMedia, ¶m_status)) {
case fsOK:
if (param_status & FS_MEDIA_NOCHKMEDIA) {
break;
}
if (param_status & FS_MEDIA_INSERTED) {
}
if (param_status & FS_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;
}
own = usbd_msc%Instance%_media_own;
if (own & MEDIA_OWN_CHG) {
if (own & MEDIA_OWN_USB) {
(void)funmount (MEDIA_DRIVE);
} else {
(void)fs_ioc_unlock (drv_id);
}
}
if ((own & MEDIA_OWN_CHG) ||
(media_state ^ media_ready_ex)) {
if (own & MEDIA_OWN_USB){
media_ok = false;
param_status = 0U;
if (fs_ioc_device_ctrl (drv_id, fsDevCtrlCodeControlMedia, ¶m_status) == fsOK) {
if (fs_ioc_lock (drv_id) == fsOK) {
media_ok = true;
}
}
} else {
if (fmount (MEDIA_DRIVE) == fsOK) {
media_ok = true;
}
}
}
if (own & MEDIA_OWN_CHG) {
usbd_msc%Instance%_media_own &= ~MEDIA_OWN_CHG;
}
}
if ((!media_ok) || (!(usbd_msc%Instance%_media_own & MEDIA_OWN_USB))) {
return 0U;
}
return media_state;
#else
#endif
}
User Code Template USBD_MSC_n.c
The following source code can be used to implement ownership control for attached media devices.
#include "USBD_MSC_%Instance%.h"
#include "USBD_Config_MSC_%Instance%.h"
#ifndef RTE_CMSIS_RTOS2
#error This user template requires CMSIS-RTOS2!
#else
extern volatile uint8_t usbd_msc%Instance%_media_own;
int32_t USBD_MSC%Instance%_SetMediaOwnerUSB (void) {
uint32_t timeout_cnt;
timeout_cnt = 300U;
usbd_msc%Instance%_media_own = USBD_MSC%Instance%_MEDIA_OWN_CHG | USBD_MSC%Instance%_MEDIA_OWN_USB;
while (usbd_msc%Instance%_media_own & USBD_MSC%Instance%_MEDIA_OWN_CHG) {
(void)osDelay(10);
if ((--timeout_cnt) == 0) { return USBD_MSC%Instance%_ERROR; }
}
return USBD_MSC%Instance%_OK;
}
int32_t USBD_MSC%Instance%_SetMediaOwnerFS (void) {
uint32_t timeout_cnt;
timeout_cnt = 300U;
usbd_msc%Instance%_media_own = USBD_MSC%Instance%_MEDIA_OWN_CHG;
while (usbd_msc%Instance%_media_own & USBD_MSC%Instance%_MEDIA_OWN_CHG) {
(void)USBD_MSC%Instance%_CheckMedia();
}
(void)osDelay(10);
if ((--timeout_cnt) == 0) { return USBD_MSC%Instance%_ERROR; }
}
return USBD_MSC%Instance%_OK;
}
#endif
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:
USBD_MSC0_SetMediaOwnerFS();
DeviceState = DEV_DO_IT;
break;
case DEV_STOP_DOING_SOMETHING:
USBD_MSC0_SetMediaOwnerUSB();
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.
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
static uint32_t memory [2][8192/4];
static uint32_t block_buf[ 512/4];
extern
const uint8_t memory_disk_image[4096];
void USBD_MSC%Instance%_Initialize (void) {
memcpy (&memory[0][0], memory_disk_image, sizeof(memory_disk_image));
memcpy (&memory[1][0], memory_disk_image, sizeof(memory_disk_image));
}
void USBD_MSC%Instance%_Uninitialize (void) {
}
bool USBD_MSC%Instance%_GetCacheInfo (uint32_t *buffer, uint32_t *size) {
*buffer = (uint32_t)block_buf;
*size = sizeof(block_buf);
return true;
}
uint8_t USBD_MSC%Instance%_GetMaxLUN (void) {
return 2U;
}
bool USBD_MSC%Instance%_LUN_GetMediaCapacity (uint8_t lun, uint32_t *block_count, uint32_t *block_size) {
(void)lun;
*block_count =(sizeof(memory)/2)/512;
*block_size = 512U;
return true;
}
bool USBD_MSC%Instance%_LUN_Read (uint8_t lun, uint32_t lba, uint32_t cnt, uint8_t *buf) {
memcpy (buf, &memory[lun][lba * (512U/4U)], cnt * 512U);
return true;
}
bool USBD_MSC%Instance%_LUN_Write (uint8_t lun, uint32_t lba, uint32_t cnt, const uint8_t *buf) {
memcpy (&memory[lun][lba * (512U/4U)], buf, cnt * 512U);
return true;
}
uint32_t USBD_MSC%Instance%_LUN_CheckMedia (uint8_t lun) {
(void)lun;
}