/** ****************************************************************************** * @file usbd_msc_scsi.c * @author MCD Application Team * @version V1.1.0 * @date 19-March-2012 * @brief This file provides all the USBD SCSI layer functions. ****************************************************************************** * @attention * *

© COPYRIGHT 2012 STMicroelectronics

* * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.st.com/software_license_agreement_liberty_v2 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "usbd_msc_bot.h" #include "usbd_msc_scsi.h" #include "usbd_msc_mem.h" #include "usbd_msc_data.h" #include "usbd_msc_conf.h" /** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY * @{ */ /** @defgroup MSC_SCSI * @brief Mass storage SCSI layer module * @{ */ /** @defgroup MSC_SCSI_Private_TypesDefinitions * @{ */ /** * @} */ /** @defgroup MSC_SCSI_Private_Defines * @{ */ /** * @} */ /** @defgroup MSC_SCSI_Private_Macros * @{ */ /** * @} */ /** @defgroup MSC_SCSI_Private_Variables * @{ */ SCSI_Sense_TypeDef SCSI_Sense [SENSE_LIST_DEEPTH]; uint8_t SCSI_Sense_Head; uint8_t SCSI_Sense_Tail; uint32_t SCSI_blk_size; uint32_t SCSI_blk_nbr; uint32_t SCSI_blk_addr; uint32_t SCSI_blk_len; USB_OTG_CORE_HANDLE *cdev; /** * @} */ /** @defgroup MSC_SCSI_Private_FunctionPrototypes * @{ */ static int8_t SCSI_TestUnitReady(uint8_t lun, uint8_t *params); static int8_t SCSI_Inquiry(uint8_t lun, uint8_t *params); static int8_t SCSI_ReadFormatCapacity(uint8_t lun, uint8_t *params); static int8_t SCSI_ReadCapacity10(uint8_t lun, uint8_t *params); static int8_t SCSI_RequestSense (uint8_t lun, uint8_t *params); static int8_t SCSI_StartStopUnit(uint8_t lun, uint8_t *params); static int8_t SCSI_ModeSense6 (uint8_t lun, uint8_t *params); static int8_t SCSI_ModeSense10 (uint8_t lun, uint8_t *params); static int8_t SCSI_Write10(uint8_t lun , uint8_t *params); static int8_t SCSI_Read10(uint8_t lun , uint8_t *params); static int8_t SCSI_Verify10(uint8_t lun, uint8_t *params); static int8_t SCSI_CheckAddressRange (uint8_t lun , uint32_t blk_offset , uint16_t blk_nbr); static int8_t SCSI_ProcessRead (uint8_t lun); static int8_t SCSI_ProcessWrite (uint8_t lun); /** * @} */ /** @defgroup MSC_SCSI_Private_Functions * @{ */ /** * @brief SCSI_ProcessCmd * Process SCSI commands * @param pdev: device instance * @param lun: Logical unit number * @param params: Command parameters * @retval status */ int8_t SCSI_ProcessCmd(USB_OTG_CORE_HANDLE *pdev, uint8_t lun, uint8_t *params) { cdev = pdev; switch (params[0]) { case SCSI_TEST_UNIT_READY: return SCSI_TestUnitReady(lun, params); case SCSI_REQUEST_SENSE: return SCSI_RequestSense (lun, params); case SCSI_INQUIRY: return SCSI_Inquiry(lun, params); case SCSI_START_STOP_UNIT: return SCSI_StartStopUnit(lun, params); case SCSI_ALLOW_MEDIUM_REMOVAL: return SCSI_StartStopUnit(lun, params); case SCSI_MODE_SENSE6: return SCSI_ModeSense6 (lun, params); case SCSI_MODE_SENSE10: return SCSI_ModeSense10 (lun, params); case SCSI_READ_FORMAT_CAPACITIES: return SCSI_ReadFormatCapacity(lun, params); case SCSI_READ_CAPACITY10: return SCSI_ReadCapacity10(lun, params); case SCSI_READ10: return SCSI_Read10(lun, params); case SCSI_WRITE10: return SCSI_Write10(lun, params); case SCSI_VERIFY10: return SCSI_Verify10(lun, params); default: SCSI_SenseCode(lun, ILLEGAL_REQUEST, INVALID_CDB); return -1; } } /** * @brief SCSI_TestUnitReady * Process SCSI Test Unit Ready Command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_TestUnitReady(uint8_t lun, uint8_t *params) { /* case 9 : Hi > D0 */ if (MSC_BOT_cbw.dDataLength != 0) { SCSI_SenseCode(MSC_BOT_cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB); return -1; } if(USBD_STORAGE_fops->IsReady(lun) !=0 ) { SCSI_SenseCode(lun, NOT_READY, MEDIUM_NOT_PRESENT); return -1; } MSC_BOT_DataLen = 0; return 0; } /** * @brief SCSI_Inquiry * Process Inquiry command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_Inquiry(uint8_t lun, uint8_t *params) { uint8_t* pPage; uint16_t len; if (params[1] & 0x01)/*Evpd is set*/ { pPage = (uint8_t *)MSC_Page00_Inquiry_Data; len = LENGTH_INQUIRY_PAGE00; } else { pPage = (uint8_t *)&USBD_STORAGE_fops->pInquiry[lun * USBD_STD_INQUIRY_LENGTH]; len = pPage[4] + 5; if (params[4] <= len) { len = params[4]; } } MSC_BOT_DataLen = len; while (len) { len--; MSC_BOT_Data[len] = pPage[len]; } return 0; } /** * @brief SCSI_ReadCapacity10 * Process Read Capacity 10 command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_ReadCapacity10(uint8_t lun, uint8_t *params) { if(USBD_STORAGE_fops->GetCapacity(lun, &SCSI_blk_nbr, &SCSI_blk_size) != 0) { SCSI_SenseCode(lun, NOT_READY, MEDIUM_NOT_PRESENT); return -1; } else { MSC_BOT_Data[0] = (uint8_t)((SCSI_blk_nbr - 1) >> 24); // dpgeorge added paren MSC_BOT_Data[1] = (uint8_t)((SCSI_blk_nbr - 1) >> 16); // dpgeorge added paren MSC_BOT_Data[2] = (uint8_t)((SCSI_blk_nbr - 1) >> 8); // dpgeorge added paren MSC_BOT_Data[3] = (uint8_t)(SCSI_blk_nbr - 1); MSC_BOT_Data[4] = (uint8_t)(SCSI_blk_size >> 24); MSC_BOT_Data[5] = (uint8_t)(SCSI_blk_size >> 16); MSC_BOT_Data[6] = (uint8_t)(SCSI_blk_size >> 8); MSC_BOT_Data[7] = (uint8_t)(SCSI_blk_size); MSC_BOT_DataLen = 8; return 0; } } /** * @brief SCSI_ReadFormatCapacity * Process Read Format Capacity command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_ReadFormatCapacity(uint8_t lun, uint8_t *params) { uint32_t blk_size; uint32_t blk_nbr; uint16_t i; for(i=0 ; i < 12 ; i++) { MSC_BOT_Data[i] = 0; } if(USBD_STORAGE_fops->GetCapacity(lun, &blk_nbr, &blk_size) != 0) { SCSI_SenseCode(lun, NOT_READY, MEDIUM_NOT_PRESENT); return -1; } else { MSC_BOT_Data[3] = 0x08; MSC_BOT_Data[4] = (uint8_t)((blk_nbr - 1) >> 24); // dpgeorge added paren MSC_BOT_Data[5] = (uint8_t)((blk_nbr - 1) >> 16); // dpgeorge added paren MSC_BOT_Data[6] = (uint8_t)((blk_nbr - 1) >> 8); // dpgeorge added paren MSC_BOT_Data[7] = (uint8_t)(blk_nbr - 1); MSC_BOT_Data[8] = 0x02; MSC_BOT_Data[9] = (uint8_t)(blk_size >> 16); MSC_BOT_Data[10] = (uint8_t)(blk_size >> 8); MSC_BOT_Data[11] = (uint8_t)(blk_size); MSC_BOT_DataLen = 12; return 0; } } /** * @brief SCSI_ModeSense6 * Process Mode Sense6 command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_ModeSense6 (uint8_t lun, uint8_t *params) { uint16_t len = 8 ; MSC_BOT_DataLen = len; while (len) { len--; MSC_BOT_Data[len] = MSC_Mode_Sense6_data[len]; } return 0; } /** * @brief SCSI_ModeSense10 * Process Mode Sense10 command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_ModeSense10 (uint8_t lun, uint8_t *params) { uint16_t len = 8; MSC_BOT_DataLen = len; while (len) { len--; MSC_BOT_Data[len] = MSC_Mode_Sense10_data[len]; } return 0; } /** * @brief SCSI_RequestSense * Process Request Sense command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_RequestSense (uint8_t lun, uint8_t *params) { uint8_t i; for(i=0 ; i < REQUEST_SENSE_DATA_LEN ; i++) { MSC_BOT_Data[i] = 0; } MSC_BOT_Data[0] = 0x70; MSC_BOT_Data[7] = REQUEST_SENSE_DATA_LEN - 6; if((SCSI_Sense_Head != SCSI_Sense_Tail)) { MSC_BOT_Data[2] = SCSI_Sense[SCSI_Sense_Head].Skey; MSC_BOT_Data[12] = SCSI_Sense[SCSI_Sense_Head].w.b.ASCQ; MSC_BOT_Data[13] = SCSI_Sense[SCSI_Sense_Head].w.b.ASC; SCSI_Sense_Head++; if (SCSI_Sense_Head == SENSE_LIST_DEEPTH) { SCSI_Sense_Head = 0; } } MSC_BOT_DataLen = REQUEST_SENSE_DATA_LEN; if (params[4] <= REQUEST_SENSE_DATA_LEN) { MSC_BOT_DataLen = params[4]; } return 0; } /** * @brief SCSI_SenseCode * Load the last error code in the error list * @param lun: Logical unit number * @param sKey: Sense Key * @param ASC: Additional Sense Key * @retval none */ void SCSI_SenseCode(uint8_t lun, uint8_t sKey, uint8_t ASC) { SCSI_Sense[SCSI_Sense_Tail].Skey = sKey; SCSI_Sense[SCSI_Sense_Tail].w.ASC = ASC << 8; SCSI_Sense_Tail++; if (SCSI_Sense_Tail == SENSE_LIST_DEEPTH) { SCSI_Sense_Tail = 0; } } /** * @brief SCSI_StartStopUnit * Process Start Stop Unit command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_StartStopUnit(uint8_t lun, uint8_t *params) { MSC_BOT_DataLen = 0; return 0; } /** * @brief SCSI_Read10 * Process Read10 command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_Read10(uint8_t lun , uint8_t *params) { if(MSC_BOT_State == BOT_IDLE) /* Idle */ { /* case 10 : Ho <> Di */ if ((MSC_BOT_cbw.bmFlags & 0x80) != 0x80) { SCSI_SenseCode(MSC_BOT_cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB); return -1; } if(USBD_STORAGE_fops->IsReady(lun) !=0 ) { SCSI_SenseCode(lun, NOT_READY, MEDIUM_NOT_PRESENT); return -1; } SCSI_blk_addr = (params[2] << 24) | \ (params[3] << 16) | \ (params[4] << 8) | \ params[5]; SCSI_blk_len = (params[7] << 8) | \ params[8]; if( SCSI_CheckAddressRange(lun, SCSI_blk_addr, SCSI_blk_len) < 0) { return -1; /* error */ } MSC_BOT_State = BOT_DATA_IN; SCSI_blk_addr *= SCSI_blk_size; SCSI_blk_len *= SCSI_blk_size; /* cases 4,5 : Hi <> Dn */ if (MSC_BOT_cbw.dDataLength != SCSI_blk_len) { SCSI_SenseCode(MSC_BOT_cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB); return -1; } } MSC_BOT_DataLen = MSC_MEDIA_PACKET; return SCSI_ProcessRead(lun); } /** * @brief SCSI_Write10 * Process Write10 command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_Write10 (uint8_t lun , uint8_t *params) { if (MSC_BOT_State == BOT_IDLE) /* Idle */ { /* case 8 : Hi <> Do */ if ((MSC_BOT_cbw.bmFlags & 0x80) == 0x80) { SCSI_SenseCode(MSC_BOT_cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB); return -1; } /* Check whether Media is ready */ if(USBD_STORAGE_fops->IsReady(lun) !=0 ) { SCSI_SenseCode(lun, NOT_READY, MEDIUM_NOT_PRESENT); return -1; } /* Check If media is write-protected */ if(USBD_STORAGE_fops->IsWriteProtected(lun) !=0 ) { SCSI_SenseCode(lun, NOT_READY, WRITE_PROTECTED); return -1; } SCSI_blk_addr = (params[2] << 24) | \ (params[3] << 16) | \ (params[4] << 8) | \ params[5]; SCSI_blk_len = (params[7] << 8) | \ params[8]; /* check if LBA address is in the right range */ if(SCSI_CheckAddressRange(lun, SCSI_blk_addr, SCSI_blk_len) < 0) { return -1; /* error */ } SCSI_blk_addr *= SCSI_blk_size; SCSI_blk_len *= SCSI_blk_size; /* cases 3,11,13 : Hn,Ho <> D0 */ if (MSC_BOT_cbw.dDataLength != SCSI_blk_len) { SCSI_SenseCode(MSC_BOT_cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB); return -1; } /* Prepare EP to receive first data packet */ MSC_BOT_State = BOT_DATA_OUT; DCD_EP_PrepareRx (cdev, MSC_OUT_EP, MSC_BOT_Data, MIN (SCSI_blk_len, MSC_MEDIA_PACKET)); } else /* Write Process ongoing */ { return SCSI_ProcessWrite(lun); } return 0; } /** * @brief SCSI_Verify10 * Process Verify10 command * @param lun: Logical unit number * @param params: Command parameters * @retval status */ static int8_t SCSI_Verify10(uint8_t lun , uint8_t *params){ if ((params[1]& 0x02) == 0x02) { SCSI_SenseCode (lun, ILLEGAL_REQUEST, INVALID_FIELED_IN_COMMAND); return -1; /* Error, Verify Mode Not supported*/ } if(SCSI_CheckAddressRange(lun, SCSI_blk_addr, SCSI_blk_len) < 0) { return -1; /* error */ } MSC_BOT_DataLen = 0; return 0; } /** * @brief SCSI_CheckAddressRange * Check address range * @param lun: Logical unit number * @param blk_offset: first block address * @param blk_nbr: number of block to be processed * @retval status */ static int8_t SCSI_CheckAddressRange (uint8_t lun , uint32_t blk_offset , uint16_t blk_nbr) { if ((blk_offset + blk_nbr) > SCSI_blk_nbr ) { SCSI_SenseCode(lun, ILLEGAL_REQUEST, ADDRESS_OUT_OF_RANGE); return -1; } return 0; } /** * @brief SCSI_ProcessRead * Handle Read Process * @param lun: Logical unit number * @retval status */ static int8_t SCSI_ProcessRead (uint8_t lun) { uint32_t len; len = MIN(SCSI_blk_len , MSC_MEDIA_PACKET); if( USBD_STORAGE_fops->Read(lun , MSC_BOT_Data, SCSI_blk_addr / SCSI_blk_size, len / SCSI_blk_size) < 0) { SCSI_SenseCode(lun, HARDWARE_ERROR, UNRECOVERED_READ_ERROR); return -1; } DCD_EP_Tx (cdev, MSC_IN_EP, MSC_BOT_Data, len); SCSI_blk_addr += len; SCSI_blk_len -= len; /* case 6 : Hi = Di */ MSC_BOT_csw.dDataResidue -= len; if (SCSI_blk_len == 0) { MSC_BOT_State = BOT_LAST_DATA_IN; } return 0; } /** * @brief SCSI_ProcessWrite * Handle Write Process * @param lun: Logical unit number * @retval status */ static int8_t SCSI_ProcessWrite (uint8_t lun) { uint32_t len; len = MIN(SCSI_blk_len , MSC_MEDIA_PACKET); if(USBD_STORAGE_fops->Write(lun , MSC_BOT_Data, SCSI_blk_addr / SCSI_blk_size, len / SCSI_blk_size) < 0) { SCSI_SenseCode(lun, HARDWARE_ERROR, WRITE_FAULT); return -1; } SCSI_blk_addr += len; SCSI_blk_len -= len; /* case 12 : Ho = Do */ MSC_BOT_csw.dDataResidue -= len; if (SCSI_blk_len == 0) { MSC_BOT_SendCSW (cdev, CSW_CMD_PASSED); } else { /* Prapare EP to Receive next packet */ DCD_EP_PrepareRx (cdev, MSC_OUT_EP, MSC_BOT_Data, MIN (SCSI_blk_len, MSC_MEDIA_PACKET)); } return 0; } /** * @} */ /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/