369 lines
14 KiB
C
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;
|
|
}
|