/* ---------------------------------------------------------------------------- * 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. * ---------------------------------------------------------------------------- */ /** \file * \addtogroup usbd_msd *@{ */ /*------------------------------------------------------------------------------ * Headers *------------------------------------------------------------------------------*/ #include "MSDLun.h" #include #include /*------------------------------------------------------------------------------ * 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, /* ??? */ {'A','T','M','E','L',' ',' ',' '}, {'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, uint8_t *ioBuffer, uint32_t ioBufferSize, uint32_t baseAddress, uint32_t size, uint16_t blockSize, uint8_t protected, void (*dataMonitor)(uint8_t flowDirection, uint32_t dataLength, uint32_t fifoNullCount, uint32_t fifoFullCount)) { uint32_t 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 */ uint32_t 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 * \param argument Optional callback argument. * \return Operation result code */ uint32_t LUN_Write(MSDLun *lun, uint32_t blockAddress, void *data, uint32_t length, TransferCallback callback, void *argument) { uint32_t medBlk, medLen; uint8_t 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 * \param argument Optional callback argument. * \return Operation result code */ uint32_t LUN_Read(MSDLun *lun, uint32_t blockAddress, void *data, uint32_t length, TransferCallback callback, void *argument) { uint32_t medBlk, medLen; uint8_t 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; } /**@}*/