Files
edgetx-gx12/radio/bootloader/usb/device/massstorage/MSDLun.c
T

369 lines
14 KiB
C

/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2008, Atmel Corporation
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "MSDLun.h"
#include "debug.h"
#include <usb/device/core/USBD.h>
#include "usb/device/massstorage/MSDDriver.h"
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
/// Default LUN block size in bytes
#define DEFAULT_LUN_BLOCK_SIZE 512
//------------------------------------------------------------------------------
// Internal variables
//------------------------------------------------------------------------------
/// Inquiry data to return to the host for the Lun.
static SBCInquiryData inquiryData = {
SBC_DIRECT_ACCESS_BLOCK_DEVICE, // Direct-access block device
SBC_PERIPHERAL_DEVICE_CONNECTED,// Peripheral device is connected
0x00, // Reserved bits
0x01, // Media is removable
SBC_SPC_VERSION_4, // SPC-4 supported
0x2, // Response data format, must be 0x2
0, // Hierarchical addressing not supported
0, // ACA not supported
0x0, // Obsolete bits
sizeof(SBCInquiryData) - 5, // Additional length
0, // No embedded SCC
0, // No access control coordinator
SBC_TPGS_NONE, // No target port support group
0, // Third-party copy not supported
0x0, // Reserved bits
0, // Protection information not supported
0x0, // Obsolete bit
0, // No embedded enclosure service component
0x0, // ???
0, // Device is not multi-port
0x0, // Obsolete bits
0x0, // Unused feature
0x0, // Unused features
0, // Task management model not supported
0x0, // ???
{'E','R','S','K','Y','9','X',' '},
{'M','a','s','s',' ',
'S','t','o','r','a','g','e',' ',
'M','S','D'},
{'0','.','0','1'},
{'M','a','s','s',' ',
'S','t','o','r','a','g','e',' ',
'E','x','a','m','p','l','e'},
0x00, // Unused features
0x00, // Reserved bits
{SBC_VERSION_DESCRIPTOR_SBC_3}, // SBC-3 compliant device
{0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0} // Reserved
};
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//! \brief Initializes a LUN instance. Must be invoked at least once even no
//! Media is linked.
//! \param lun Pointer to the MSDLun instance to initialize
//! \param media Media on which the LUN is constructed, set to 0 to
//! disconnect the Media or initialize an ejected LUN.
//! \param ioBuffer Pointer to a buffer used for read/write operation and
//! which must be blockSize bytes long.
//! \param ioBufferSize Size of the allocated IO buffer.
//! \param baseAddress Base address of the LUN in number of media blocks
//! \param size Total size of the LUN in number of media blocks
//! \param blockSize One block of the LUN in number of media blocks
//! \param protected The LUN area is forced to readonly even the media
//! is writable
//! \param dataMonitor Pointer to a Monitor Function to analyze the flow of
//! this LUN.
//------------------------------------------------------------------------------
void LUN_Init(MSDLun *lun,
Media *media,
unsigned char *ioBuffer,
unsigned int ioBufferSize,
unsigned int baseAddress,
unsigned int size,
unsigned short blockSize,
unsigned char protected,
void (*dataMonitor)(unsigned char flowDirection,
unsigned int dataLength,
unsigned int fifoNullCount,
unsigned int fifoFullCount))
{
unsigned int logicalBlockAddress;
TRACE_INFO("LUN init\n\r");
// Initialize inquiry data
lun->inquiryData = &inquiryData;
// Initialize request sense data
lun->requestSenseData.bResponseCode = SBC_SENSE_DATA_FIXED_CURRENT;
lun->requestSenseData.isValid = 1;
lun->requestSenseData.bObsolete1 = 0;
lun->requestSenseData.bSenseKey = SBC_SENSE_KEY_NO_SENSE;
lun->requestSenseData.bReserved1 = 0;
lun->requestSenseData.isILI = 0;
lun->requestSenseData.isEOM = 0;
lun->requestSenseData.isFilemark = 0;
lun->requestSenseData.pInformation[0] = 0;
lun->requestSenseData.pInformation[1] = 0;
lun->requestSenseData.pInformation[2] = 0;
lun->requestSenseData.pInformation[3] = 0;
lun->requestSenseData.bAdditionalSenseLength
= sizeof(SBCRequestSenseData) - 8;
lun->requestSenseData.bAdditionalSenseCode = 0;
lun->requestSenseData.bAdditionalSenseCodeQualifier = 0;
lun->requestSenseData.bFieldReplaceableUnitCode = 0;
lun->requestSenseData.bSenseKeySpecific = 0;
lun->requestSenseData.pSenseKeySpecific[0] = 0;
lun->requestSenseData.pSenseKeySpecific[0] = 0;
lun->requestSenseData.isSKSV = 0;
STORE_DWORDB(0, lun->readCapacityData.pLogicalBlockAddress);
STORE_DWORDB(0, lun->readCapacityData.pLogicalBlockLength);
// Initialize LUN
lun->media = media;
if (media == 0) {
lun->status = LUN_NOT_PRESENT;
return;
}
lun->baseAddress = baseAddress;
// Customized block size
if (blockSize) {
lun->blockSize = blockSize;
}
else {
if (media->blockSize < DEFAULT_LUN_BLOCK_SIZE)
lun->blockSize = DEFAULT_LUN_BLOCK_SIZE / media->blockSize;
else
lun->blockSize = 1;
}
if (size) {
lun->size = size;
}
else {
lun->size = media->size;
//if (blockSize)
// lun->size = media->size / blockSize;
//else {
// if (media->blockSize < DEFAULT_LUN_BLOCK_SIZE)
// lun->size = media->size / lun->blockSize;
// else
// lun->size = media->size;
//}
}
TRACE_INFO("LUN: blkSize %d, size %d\n\r", (int)lun->blockSize, (int)lun->size);
if (protected) lun->protected = 1;
else lun->protected = media->protected;
lun->ioFifo.pBuffer = ioBuffer;
lun->ioFifo.bufferSize = ioBufferSize;
lun->dataMonitor = dataMonitor;
// Initialize read capacity data
logicalBlockAddress = lun->size / lun->blockSize - 1;
STORE_DWORDB(logicalBlockAddress,
lun->readCapacityData.pLogicalBlockAddress);
STORE_DWORDB(lun->blockSize * media->blockSize,
lun->readCapacityData.pLogicalBlockLength);
// Indicate media change
lun->status = LUN_CHANGED;
}
//------------------------------------------------------------------------------
//! \brief Eject the media from a LUN
//! \param lun Pointer to the MSDLun instance to initialize
//! \return Operation result code
//------------------------------------------------------------------------------
unsigned char LUN_Eject(MSDLun *lun)
{
if (lun->media) {
// Avoid any LUN R/W in progress
if (lun->media->state == MED_STATE_BUSY) {
return USBD_STATUS_LOCKED;
}
// Remove the link of the media
lun->media = 0;
}
// LUN is removed
lun->status = LUN_NOT_PRESENT;
return USBD_STATUS_SUCCESS;
}
//------------------------------------------------------------------------------
//! \brief Writes data on the a LUN starting at the specified block address.
//! \param lun Pointer to a MSDLun instance
//! \param blockAddress First block address to write
//! \param data Pointer to the data to write
//! \param length Number of blocks to write
//! \param callback Optional callback to invoke when the write finishes
//! \return Operation result code
//------------------------------------------------------------------------------
unsigned char LUN_Write(MSDLun *lun,
unsigned int blockAddress,
void *data,
unsigned int length,
TransferCallback callback,
void *argument)
{
unsigned int medBlk, medLen;
unsigned char status;
TRACE_INFO_WP("LUNWrite(%u) ", blockAddress);
// Check that the data is not too big
if ((length + blockAddress) * lun->blockSize > lun->size) {
TRACE_WARNING("LUN_Write: Data too big\n\r");
status = USBD_STATUS_ABORTED;
}
else if (lun->media == 0 || lun->status != LUN_READY) {
TRACE_WARNING("LUN_Write: Media not ready\n\r");
status = USBD_STATUS_ABORTED;
}
else if (lun->protected) {
TRACE_WARNING("LUN_Write: LUN is readonly\n\r");
status = USBD_STATUS_ABORTED;
}
else {
// Compute write start address
medBlk = lun->baseAddress + blockAddress * lun->blockSize;
medLen = length * lun->blockSize;
// Start write operation
status = MED_Write(lun->media,
medBlk,
data,
medLen,
(MediaCallback) callback,
argument);
// Check operation result code
if (status == MED_STATUS_SUCCESS) {
status = USBD_STATUS_SUCCESS;
}
else {
TRACE_WARNING("LUN_Write: Cannot write media\n\r");
status = USBD_STATUS_ABORTED;
}
}
return status;
}
//------------------------------------------------------------------------------
//! \brief Reads data from a LUN, starting at the specified block address.
//! \param lun Pointer to a MSDLun instance
//! \param blockAddress First block address to read
//! \param data Pointer to a data buffer in which to store the data
//! \param length Number of blocks to read
//! \param callback Optional callback to invoke when the read finishes
//! \return Operation result code
//------------------------------------------------------------------------------
unsigned char LUN_Read(MSDLun *lun,
unsigned int blockAddress,
void *data,
unsigned int length,
TransferCallback callback,
void *argument)
{
unsigned int medBlk, medLen;
unsigned char status;
// Check that the data is not too big
if ((length + blockAddress) * lun->blockSize > lun->size) {
TRACE_WARNING("LUN_Read: Area: (%d + %d)*%d > %d\n\r",
(int)length, (int)blockAddress, (int)lun->blockSize, (int)lun->size);
status = USBD_STATUS_ABORTED;
}
else if (lun->media == 0 || lun->status != LUN_READY) {
TRACE_WARNING("LUN_Read: Media not present\n\r");
status = USBD_STATUS_ABORTED;
}
else {
TRACE_INFO_WP("LUNRead(%u) ", blockAddress);
// Compute read start address
medBlk = lun->baseAddress + (blockAddress * lun->blockSize);
medLen = length * lun->blockSize;
// Start write operation
status = MED_Read(lun->media,
medBlk,
data,
medLen,
(MediaCallback) callback,
argument);
// Check result code
if (status == MED_STATUS_SUCCESS) {
status = USBD_STATUS_SUCCESS;
}
else {
TRACE_WARNING("LUN_Read: Cannot read media\n\r");
status = USBD_STATUS_ABORTED;
}
}
return status;
}