Files
base/middleware/usb/uC-USBH/Class/MSC/usbh_msc.c
2025-11-07 09:57:14 +08:00

2907 lines
132 KiB
C

/*
*********************************************************************************************************
* uC/USB-Host
* The Embedded USB Host Stack
*
* Copyright 2004-2021 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* NOTICE
*
* Semidrive modified this file to adapt it for ssdk platform.
* The modifications are only intended for use with Semidrive chips.
* Copyright of all the modifications belongs to Semidrive Semiconductor.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* MASS STORAGE CLASS (MSC)
*
* Filename : usbh_msc.c
* Version : V3.42.01
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#define USBH_MSC_MODULE
#define MICRIUM_SOURCE
#include "usbh_msc.h"
#include "../../Source/usbh_core.h"
/*
*********************************************************************************************************
* MODULE
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
#define USBH_MSC_SIG_CBW 0x43425355u
#define USBH_MSC_SIG_CSW 0x53425355u
#define USBH_MSC_LEN_CBW 31u
#define USBH_MSC_LEN_CSW 13u
#define USBH_MSC_MAX_TRANSFER_RETRY 1000u
/*
*********************************************************************************************************
* SUBCLASS CODES
*
* Note(s) : (1) See 'USB Mass Storage Class Specification Overview', Revision 1.2, Section 2.
*********************************************************************************************************
*/
#define USBH_MSC_SUBCLASS_CODE_RBC 0x01u
#define USBH_MSC_SUBCLASS_CODE_SFF_8020i 0x02u
#define USBH_MSC_SUBCLASS_CODE_MMC_2 0x02u
#define USBH_MSC_SUBCLASS_CODE_QIC_157 0x03u
#define USBH_MSC_SUBCLASS_CODE_UFI 0x04u
#define USBH_MSC_SUBCLASS_CODE_SFF_8070i 0x05u
#define USBH_MSC_SUBCLASS_CODE_SCSI 0x06u
/*
*********************************************************************************************************
* PROTOCOL CODES
*
* Note(s) : (1) See 'USB Mass Storage Class Specification Overview', Revision 1.2, Section 3.
*********************************************************************************************************
*/
#define USBH_MSC_PROTOCOL_CODE_CTRL_BULK_INTR_CMD_INTR 0x00u
#define USBH_MSC_PROTOCOL_CODE_CTRL_BULK_INTR 0x01u
#define USBH_MSC_PROTOCOL_CODE_BULK_ONLY 0x50u
/*
*********************************************************************************************************
* CLASS-SPECIFIC REQUESTS
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 3.
*
* (2) The 'bRequest' field of a class-specific setup request may contain one of these values.
*
* (3) The mass storage reset request is "used to reset the mass storage device and its
* associated interface". The setup request packet will consist of :
*
* (a) bmRequestType = 00100001b (class, interface, host-to-device)
* (b) bRequest = 0xFF
* (c) wValue = 0x0000
* (d) wIndex = Interface number
* (e) wLength = 0x0000
*
* (4) The get max LUN is used to determine the number of LUN's supported by the device. The
* setup request packet will consist of :
*
* (a) bmRequestType = 10100001b (class, interface, device-to-host)
* (b) bRequest = 0xFE
* (c) wValue = 0x0000
* (d) wIndex = Interface number
* (e) wLength = 0x0001
*********************************************************************************************************
*/
#define USBH_MSC_REQ_MASS_STORAGE_RESET 0xFFu /* See Note #3. */
#define USBH_MSC_REQ_GET_MAX_LUN 0xFEu /* See Note #4. */
/*
*********************************************************************************************************
* COMMAND BLOCK FLAG VALUES
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 5.1.
*
* (2) The 'bmCBWFlags' field of a command block wrapper may contain one of these values.
*********************************************************************************************************
*/
#define USBH_MSC_BMCBWFLAGS_DIR_HOST_TO_DEVICE 0x00u
#define USBH_MSC_BMCBWFLAGS_DIR_DEVICE_TO_HOST 0x80u
/*
*********************************************************************************************************
* COMMAND BLOCK STATUS VALUES
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 5.3, Table 5.3.
*
* (2) The 'bCSWStatus' field of a command status wrapper may contain one of these values.
*********************************************************************************************************
*/
#define USBH_MSC_BCSWSTATUS_CMD_PASSED 0x00u
#define USBH_MSC_BCSWSTATUS_CMD_FAILED 0x01u
#define USBH_MSC_BCSWSTATUS_PHASE_ERROR 0x02u
/*
*********************************************************************************************************
* SCSI DEFINES
*********************************************************************************************************
*/
/* ------------------ SCSI OPCODES ------------------------ */
#define USBH_SCSI_CMD_TEST_UNIT_READY 0x00u
#define USBH_SCSI_CMD_REWIND 0x01u
#define USBH_SCSI_CMD_REZERO_UNIT 0x01u
#define USBH_SCSI_CMD_REQUEST_SENSE 0x03u
#define USBH_SCSI_CMD_FORMAT_UNIT 0x04u
#define USBH_SCSI_CMD_FORMAT_MEDIUM 0x04u
#define USBH_SCSI_CMD_FORMAT 0x04u
#define USBH_SCSI_CMD_READ_BLOCK_LIMITS 0x05u
#define USBH_SCSI_CMD_REASSIGN_BLOCKS 0x07u
#define USBH_SCSI_CMD_INITIALIZE_ELEMENT_STATUS 0x07u
#define USBH_SCSI_CMD_READ_06 0x08u
#define USBH_SCSI_CMD_RECEIVE 0x08u
#define USBH_SCSI_CMD_GET_MESSAGE_06 0x08u
#define USBH_SCSI_CMD_WRITE_06 0x0Au
#define USBH_SCSI_CMD_SEND_06 0x0Au
#define USBH_SCSI_CMD_SEND_MESSAGE_06 0x0Au
#define USBH_SCSI_CMD_PRINT 0x0Au
#define USBH_SCSI_CMD_SEEK_06 0x0Bu
#define USBH_SCSI_CMD_SET_CAPACITY 0x0Bu
#define USBH_SCSI_CMD_SLEW_AND_PRINT 0x0Bu
#define USBH_SCSI_CMD_READ_REVERSE_06 0x0Fu
#define USBH_SCSI_CMD_WRITE_FILEMARKS_06 0x10u
#define USBH_SCSI_CMD_SYNCHRONIZE_BUFFER 0x10u
#define USBH_SCSI_CMD_SPACE_06 0x11u
#define USBH_SCSI_CMD_INQUIRY 0x12u
#define USBH_SCSI_CMD_VERIFY_06 0x13u
#define USBH_SCSI_CMD_RECOVER_BUFFERED_DATA 0x14u
#define USBH_SCSI_CMD_MODE_SELECT_06 0x15u
#define USBH_SCSI_CMD_RESERVE_06 0x16u
#define USBH_SCSI_CMD_RESERVE_ELEMENT_06 0x16u
#define USBH_SCSI_CMD_RELEASE_06 0x17u
#define USBH_SCSI_CMD_RELEASE_ELEMENT_06 0x17u
#define USBH_SCSI_CMD_COPY 0x18u
#define USBH_SCSI_CMD_ERASE_06 0x19u
#define USBH_SCSI_CMD_MODE_SENSE_06 0x1Au
#define USBH_SCSI_CMD_START_STOP_UNIT 0x1Bu
#define USBH_SCSI_CMD_LOAD_UNLOAD 0x1Bu
#define USBH_SCSI_CMD_SCAN_06 0x1Bu
#define USBH_SCSI_CMD_STOP_PRINT 0x1Bu
#define USBH_SCSI_CMD_OPEN_CLOSE_IMPORT_EXPORT_ELEMENT 0x1Bu
#define USBH_SCSI_CMD_RECEIVE_DIAGNOSTIC_RESULTS 0x1Cu
#define USBH_SCSI_CMD_SEND_DIAGNOSTIC 0x1Du
#define USBH_SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1Eu
#define USBH_SCSI_CMD_READ_FORMAT_CAPACITIES 0x23u
#define USBH_SCSI_CMD_SET_WINDOW 0x24u
#define USBH_SCSI_CMD_READ_CAPACITY_10 0x25u
#define USBH_SCSI_CMD_READ_CAPACITY 0x25u
#define USBH_SCSI_CMD_READ_CARD_CAPACITY 0x25u
#define USBH_SCSI_CMD_GET_WINDOW 0x25u
#define USBH_SCSI_CMD_READ_10 0x28u
#define USBH_SCSI_CMD_GET_MESSAGE_10 0x28u
#define USBH_SCSI_CMD_READ_GENERATION 0x29u
#define USBH_SCSI_CMD_WRITE_10 0x2Au
#define USBH_SCSI_CMD_SEND_10 0x2Au
#define USBH_SCSI_CMD_SEND_MESSAGE_10 0x2Au
#define USBH_SCSI_CMD_SEEK_10 0x2Bu
#define USBH_SCSI_CMD_LOCATE_10 0x2Bu
#define USBH_SCSI_CMD_POSITION_TO_ELEMENT 0x2Bu
#define USBH_SCSI_CMD_ERASE_10 0x2Cu
#define USBH_SCSI_CMD_READ_UPDATED_BLOCK 0x2Du
#define USBH_SCSI_CMD_WRITE_AND_VERIFY_10 0x2Eu
#define USBH_SCSI_CMD_VERIFY_10 0x2Fu
#define USBH_SCSI_CMD_SEARCH_DATA_HIGH_10 0x30u
#define USBH_SCSI_CMD_SEARCH_DATA_EQUAL_10 0x31u
#define USBH_SCSI_CMD_OBJECT_POSITION 0x31u
#define USBH_SCSI_CMD_SEARCH_DATA_LOW_10 0x32u
#define USBH_SCSI_CMD_SET_LIMITS_10 0x33u
#define USBH_SCSI_CMD_PRE_FETCH_10 0x34u
#define USBH_SCSI_CMD_READ_POSITION 0x34u
#define USBH_SCSI_CMD_GET_DATA_BUFFER_STATUS 0x34u
#define USBH_SCSI_CMD_SYNCHRONIZE_CACHE_10 0x35u
#define USBH_SCSI_CMD_LOCK_UNLOCK_CACHE_10 0x36u
#define USBH_SCSI_CMD_READ_DEFECT_DATA_10 0x37u
#define USBH_SCSI_CMD_INIT_ELEMENT_STATUS_WITH_RANGE 0x37u
#define USBH_SCSI_CMD_MEDIUM_SCAN 0x38u
#define USBH_SCSI_CMD_COMPARE 0x39u
#define USBH_SCSI_CMD_COPY_AND_VERIFY 0x3Au
#define USBH_SCSI_CMD_WRITE_BUFFER 0x3Bu
#define USBH_SCSI_CMD_READ_BUFFER 0x3Cu
#define USBH_SCSI_CMD_UPDATE_BLOCK 0x3Du
#define USBH_SCSI_CMD_READ_LONG_10 0x3Eu
#define USBH_SCSI_CMD_WRITE_LONG_10 0x3Fu
#define USBH_SCSI_CMD_CHANGE_DEFINITION 0x40u
#define USBH_SCSI_CMD_WRITE_SAME_10 0x41u
#define USBH_SCSI_CMD_READ_SUBCHANNEL 0x42u
#define USBH_SCSI_CMD_READ_TOC_PMA_ATIP 0x43u
#define USBH_SCSI_CMD_REPORT_DENSITY_SUPPORT 0x44u
#define USBH_SCSI_CMD_READ_HEADER 0x44u
#define USBH_SCSI_CMD_PLAY_AUDIO_10 0x45u
#define USBH_SCSI_CMD_GET_CONFIGURATION 0x46u
#define USBH_SCSI_CMD_PLAY_AUDIO_MSF 0x47u
#define USBH_SCSI_CMD_GET_EVENT_STATUS_NOTIFICATION 0x4Au
#define USBH_SCSI_CMD_PAUSE_RESUME 0x4Bu
#define USBH_SCSI_CMD_LOG_SELECT 0x4Cu
#define USBH_SCSI_CMD_LOG_SENSE 0x4Du
#define USBH_SCSI_CMD_STOP_PLAY_SCAN 0x4Eu
#define USBH_SCSI_CMD_XDWRITE_10 0x50u
#define USBH_SCSI_CMD_XPWRITE_10 0x51u
#define USBH_SCSI_CMD_READ_DISC_INFORMATION 0x51u
#define USBH_SCSI_CMD_XDREAD_10 0x52u
#define USBH_SCSI_CMD_READ_TRACK_INFORMATION 0x52u
#define USBH_SCSI_CMD_RESERVE_TRACK 0x53u
#define USBH_SCSI_CMD_SEND_OPC_INFORMATION 0x54u
#define USBH_SCSI_CMD_MODE_SELECT_10 0x55u
#define USBH_SCSI_CMD_RESERVE_10 0x56u
#define USBH_SCSI_CMD_RESERVE_ELEMENT_10 0x56u
#define USBH_SCSI_CMD_RELEASE_10 0x57u
#define USBH_SCSI_CMD_RELEASE_ELEMENT_10 0x57u
#define USBH_SCSI_CMD_REPAIR_TRACK 0x58u
#define USBH_SCSI_CMD_MODE_SENSE_10 0x5Au
#define USBH_SCSI_CMD_CLOSE_TRACK_SESSION 0x5Bu
#define USBH_SCSI_CMD_READ_BUFFER_CAPACITY 0x5Cu
#define USBH_SCSI_CMD_SEND_CUE_SHEET 0x5Du
#define USBH_SCSI_CMD_PERSISTENT_RESERVE_IN 0x5Eu
#define USBH_SCSI_CMD_PERSISTENT_RESERVE_OUT 0x5Fu
#define USBH_SCSI_CMD_EXTENDED_CDB 0x7Eu
#define USBH_SCSI_CMD_VARIABLE_LENGTH_CDB 0x7Fu
#define USBH_SCSI_CMD_XDWRITE_EXTENDED_16 0x80u
#define USBH_SCSI_CMD_WRITE_FILEMARKS_16 0x80u
#define USBH_SCSI_CMD_REBUILD_16 0x81u
#define USBH_SCSI_CMD_READ_REVERSE_16 0x81u
#define USBH_SCSI_CMD_REGENERATE_16 0x82u
#define USBH_SCSI_CMD_EXTENDED_COPY 0x83u
#define USBH_SCSI_CMD_RECEIVE_COPY_RESULTS 0x84u
#define USBH_SCSI_CMD_ATA_COMMAND_PASS_THROUGH_16 0x85u
#define USBH_SCSI_CMD_ACCESS_CONTROL_IN 0x86u
#define USBH_SCSI_CMD_ACCESS_CONTROL_OUT 0x87u
#define USBH_SCSI_CMD_READ_16 0x88u
#define USBH_SCSI_CMD_WRITE_16 0x8Au
#define USBH_SCSI_CMD_ORWRITE 0x8Bu
#define USBH_SCSI_CMD_READ_ATTRIBUTE 0x8Cu
#define USBH_SCSI_CMD_WRITE_ATTRIBUTE 0x8Du
#define USBH_SCSI_CMD_WRITE_AND_VERIFY_16 0x8Eu
#define USBH_SCSI_CMD_VERIFY_16 0x8Fu
#define USBH_SCSI_CMD_PREFETCH_16 0x90u
#define USBH_SCSI_CMD_SYNCHRONIZE_CACHE_16 0x91u
#define USBH_SCSI_CMD_SPACE_16 0x91u
#define USBH_SCSI_CMD_LOCK_UNLOCK_CACHE_16 0x92u
#define USBH_SCSI_CMD_LOCATE_16 0x92u
#define USBH_SCSI_CMD_WRITE_SAME_16 0x93u
#define USBH_SCSI_CMD_ERASE_16 0x93u
#define USBH_SCSI_CMD_SERVICE_ACTION_IN_16 0x9Eu
#define USBH_SCSI_CMD_SERVICE_ACTION_OUT_16 0x9Fu
#define USBH_SCSI_CMD_REPORT_LUNS 0xA0u
#define USBH_SCSI_CMD_BLANK 0xA1u
#define USBH_SCSI_CMD_ATA_COMMAND_PASS_THROUGH_12 0xA1u
#define USBH_SCSI_CMD_SECURITY_PROTOCOL_IN 0xA2u
#define USBH_SCSI_CMD_MAINTENANCE_IN 0xA3u
#define USBH_SCSI_CMD_SEND_KEY 0xA3u
#define USBH_SCSI_CMD_MAINTENANCE_OUT 0xA4u
#define USBH_SCSI_CMD_REPORT_KEY 0xA4u
#define USBH_SCSI_CMD_MOVE_MEDIUM 0xA5u
#define USBH_SCSI_CMD_PLAY_AUDIO_12 0xA5u
#define USBH_SCSI_CMD_EXCHANGE_MEDIUM 0xA6u
#define USBH_SCSI_CMD_LOAD_UNLOAD_CDVD 0xA6u
#define USBH_SCSI_CMD_MOVE_MEDIUM_ATTACHED 0xA7u
#define USBH_SCSI_CMD_SET_READ_AHEAD 0xA7u
#define USBH_SCSI_CMD_READ_12 0xA8u
#define USBH_SCSI_CMD_GET_MESSAGE_12 0xA8u
#define USBH_SCSI_CMD_SERVICE_ACTION_OUT_12 0xA9u
#define USBH_SCSI_CMD_WRITE_12 0xAAu
#define USBH_SCSI_CMD_SEND_MESSAGE_12 0xAAu
#define USBH_SCSI_CMD_SERVICE_ACTION_IN_12 0xABu
#define USBH_SCSI_CMD_ERASE_12 0xACu
#define USBH_SCSI_CMD_GET_PERFORMANCE 0xACu
#define USBH_SCSI_CMD_READ_DVD_STRUCTURE 0xADu
#define USBH_SCSI_CMD_WRITE_AND_VERIFY_12 0xAEu
#define USBH_SCSI_CMD_VERIFY_12 0xAFu
#define USBH_SCSI_CMD_SEARCH_DATA_HIGH_12 0xB0u
#define USBH_SCSI_CMD_SEARCH_DATA_EQUAL_12 0xB1u
#define USBH_SCSI_CMD_SEARCH_DATA_LOW_12 0xB2u
#define USBH_SCSI_CMD_SET_LIMITS_12 0xB3u
#define USBH_SCSI_CMD_READ_ELEMENT_STATUS_ATTACHED 0xB4u
#define USBH_SCSI_CMD_SECURITY_PROTOCOL_OUT 0xB5u
#define USBH_SCSI_CMD_REQUEST_VOLUME_ELEMENT_ADDRESS 0xB5u
#define USBH_SCSI_CMD_SEND_VOLUME_TAG 0xB6u
#define USBH_SCSI_CMD_SET_STREAMING 0xB6u
#define USBH_SCSI_CMD_READ_DEFECT_DATA_12 0xB7u
#define USBH_SCSI_CMD_READ_ELEMENT_STATUS 0xB8u
#define USBH_SCSI_CMD_READ_CD_MSF 0xB9u
#define USBH_SCSI_CMD_REDUNDANCY_GROUP_IN 0xBAu
#define USBH_SCSI_CMD_SCAN 0xBAu
#define USBH_SCSI_CMD_REDUNDANCY_GROUP_OUT 0xBBu
#define USBH_SCSI_CMD_SET_CD_SPEED 0xBBu
#define USBH_SCSI_CMD_SPARE_IN 0xBCu
#define USBH_SCSI_CMD_SPARE_OUT 0xBDu
#define USBH_SCSI_CMD_MECHANISM_STATUS 0xBDu
#define USBH_SCSI_CMD_VOLUME_SET_IN 0xBEu
#define USBH_SCSI_CMD_READ_CD 0xBEu
#define USBH_SCSI_CMD_VOLUME_SET_OUT 0xBFu
#define USBH_SCSI_CMD_SEND_DVD_STRUCTURE 0xBFu
/* ------------------- SCSI STATUS CODES ------------------ */
#define USBH_SCSI_STATUS_GOOD 0x00u
#define USBH_SCSI_STATUS_CHECK_CONDITION 0x02u
#define USBH_SCSI_STATUS_CONDITION_MET 0x04u
#define USBH_SCSI_STATUS_BUSY 0x08u
#define USBH_SCSI_STATUS_RESERVATION_CONFLICT 0x18u
#define USBH_SCSI_STATUS_TASK_SET_FULL 0x28u
#define USBH_SCSI_STATUS_ACA_ACTIVE 0x30u
#define USBH_SCSI_STATUS_TASK_ABORTED 0x40u
/* ------------------- SCSI SENSE KEYS -------------------- */
#define USBH_SCSI_SENSE_KEY_NO_SENSE 0x00u
#define USBH_SCSI_SENSE_KEY_RECOVERED_ERROR 0x01u
#define USBH_SCSI_SENSE_KEY_NOT_RDY 0x02u
#define USBH_SCSI_SENSE_KEY_MEDIUM_ERROR 0x03u
#define USBH_SCSI_SENSE_KEY_HARDWARE_ERROR 0x04u
#define USBH_SCSI_SENSE_KEY_ILLEGAL_REQUEST 0x05u
#define USBH_SCSI_SENSE_KEY_UNIT_ATTENTION 0x06u
#define USBH_SCSI_SENSE_KEY_DATA_PROTECT 0x07u
#define USBH_SCSI_SENSE_KEY_BLANK_CHECK 0x08u
#define USBH_SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09u
#define USBH_SCSI_SENSE_KEY_COPY_ABORTED 0x0Au
#define USBH_SCSI_SENSE_KEY_ABORTED_COMMAND 0x0Bu
#define USBH_SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0Du
#define USBH_SCSI_SENSE_KEY_MISCOMPARE 0x0Eu
/* ------------- SCSI ADDITIONAL SENSE CODES -------------- */
#define USBH_SCSI_ASC_NO_ADDITIONAL_SENSE_INFO 0x00u
#define USBH_SCSI_ASC_NO_INDEX_SECTOR_SIGNAL 0x01u
#define USBH_SCSI_ASC_NO_SEEK_COMPLETE 0x02u
#define USBH_SCSI_ASC_PERIPHERAL_DEV_WR_FAULT 0x03u
#define USBH_SCSI_ASC_LOG_UNIT_NOT_RDY 0x04u
#define USBH_SCSI_ASC_LOG_UNIT_NOT_RESPOND_TO_SELECTION 0x05u
#define USBH_SCSI_ASC_NO_REFERENCE_POSITION_FOUND 0x06u
#define USBH_SCSI_ASC_MULTIPLE_PERIPHERAL_DEVS_SELECTED 0x07u
#define USBH_SCSI_ASC_LOG_UNIT_COMMUNICATION_FAIL 0x08u
#define USBH_SCSI_ASC_TRACK_FOLLOWING_ERR 0x09u
#define USBH_SCSI_ASC_ERR_LOG_OVERFLOW 0x0Au
#define USBH_SCSI_ASC_WARNING 0x0Bu
#define USBH_SCSI_ASC_WR_ERR 0x0Cu
#define USBH_SCSI_ASC_ERR_DETECTED_BY_THIRD_PARTY 0x0Du
#define USBH_SCSI_ASC_INVALID_INFO_UNIT 0x0Eu
#define USBH_SCSI_ASC_ID_CRC_OR_ECC_ERR 0x10u
#define USBH_SCSI_ASC_UNRECOVERED_RD_ERR 0x11u
#define USBH_SCSI_ASC_ADDR_MARK_NOT_FOUND_FOR_ID 0x12u
#define USBH_SCSI_ASC_ADDR_MARK_NOT_FOUND_FOR_DATA 0x13u
#define USBH_SCSI_ASC_RECORDED_ENTITY_NOT_FOUND 0x14u
#define USBH_SCSI_ASC_RANDOM_POSITIONING_ERR 0x15u
#define USBH_SCSI_ASC_DATA_SYNCHRONIZATION_MARK_ERR 0x16u
#define USBH_SCSI_ASC_RECOVERED_DATA_NO_ERR_CORRECT 0x17u
#define USBH_SCSI_ASC_RECOVERED_DATA_ERR_CORRECT 0x18u
#define USBH_SCSI_ASC_DEFECT_LIST_ERR 0x19u
#define USBH_SCSI_ASC_PARAMETER_LIST_LENGTH_ERR 0x1Au
#define USBH_SCSI_ASC_SYNCHRONOUS_DATA_TRANSFER_ERR 0x1Bu
#define USBH_SCSI_ASC_DEFECT_LIST_NOT_FOUND 0x1Cu
#define USBH_SCSI_ASC_MISCOMPARE_DURING_VERIFY_OP 0x1Du
#define USBH_SCSI_ASC_RECOVERED_ID_WITH_ECC_CORRECTION 0x1Eu
#define USBH_SCSI_ASC_PARTIAL_DEFECT_LIST_TRANSFER 0x1Fu
#define USBH_SCSI_ASC_INVALID_CMD_OP_CODE 0x20u
#define USBH_SCSI_ASC_LOG_BLOCK_ADDR_OUT_OF_RANGE 0x21u
#define USBH_SCSI_ASC_ILLEGAL_FUNCTION 0x22u
#define USBH_SCSI_ASC_INVALID_FIELD_IN_CDB 0x24u
#define USBH_SCSI_ASC_LOG_UNIT_NOT_SUPPORTED 0x25u
#define USBH_SCSI_ASC_INVALID_FIELD_IN_PARAMETER_LIST 0x26u
#define USBH_SCSI_ASC_WR_PROTECTED 0x27u
#define USBH_SCSI_ASC_NOT_RDY_TO_RDY_CHANGE 0x28u
#define USBH_SCSI_ASC_POWER_ON_OR_BUS_DEV_RESET 0x29u
#define USBH_SCSI_ASC_PARAMETERS_CHANGED 0x2Au
#define USBH_SCSI_ASC_CANNOT_COPY_CANNOT_DISCONNECT 0x2Bu
#define USBH_SCSI_ASC_CMD_SEQUENCE_ERR 0x2Cu
#define USBH_SCSI_ASC_OVERWR_ERR_ON_UPDATE_IN_PLACE 0x2Du
#define USBH_SCSI_ASC_INSUFFICIENT_TIME_FOR_OP 0x2Eu
#define USBH_SCSI_ASC_CMDS_CLEARED_BY_ANOTHER_INIT 0x2Fu
#define USBH_SCSI_ASC_INCOMPATIBLE_MEDIUM_INSTALLED 0x30u
#define USBH_SCSI_ASC_MEDIUM_FORMAT_CORRUPTED 0x31u
#define USBH_SCSI_ASC_NO_DEFECT_SPARE_LOCATION_AVAIL 0x32u
#define USBH_SCSI_ASC_TAPE_LENGTH_ERR 0x33u
#define USBH_SCSI_ASC_ENCLOSURE_FAIL 0x34u
#define USBH_SCSI_ASC_ENCLOSURE_SERVICES_FAIL 0x35u
#define USBH_SCSI_ASC_RIBBON_INK_OR_TONER_FAIL 0x36u
#define USBH_SCSI_ASC_ROUNDED_PARAMETER 0x37u
#define USBH_SCSI_ASC_EVENT_STATUS_NOTIFICATION 0x38u
#define USBH_SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED 0x39u
#define USBH_SCSI_ASC_MEDIUM_NOT_PRESENT 0x3Au
#define USBH_SCSI_ASC_SEQUENTIAL_POSITIONING_ERR 0x3Bu
#define USBH_SCSI_ASC_INVALID_BITS_IN_IDENTIFY_MSG 0x3Du
#define USBH_SCSI_ASC_LOG_UNIT_HAS_NOT_SELF_CFG_YET 0x3Eu
#define USBH_SCSI_ASC_TARGET_OP_CONDITIONS_HAVE_CHANGED 0x3Fu
#define USBH_SCSI_ASC_RAM_FAIL 0x40u
#define USBH_SCSI_ASC_DATA_PATH_FAIL 0x41u
#define USBH_SCSI_ASC_POWER_ON_SELF_TEST_FAIL 0x42u
#define USBH_SCSI_ASC_MSG_ERR 0x43u
#define USBH_SCSI_ASC_INTERNAL_TARGET_FAIL 0x44u
#define USBH_SCSI_ASC_SELECT_OR_RESELECT_FAIL 0x45u
#define USBH_SCSI_ASC_UNSUCCESSFUL_SOFT_RESET 0x46u
#define USBH_SCSI_ASC_SCSI_PARITY_ERR 0x47u
#define USBH_SCSI_ASC_INIT_DETECTED_ERR_MSG_RECEIVED 0x48u
#define USBH_SCSI_ASC_INVALID_MSG_ERR 0x49u
#define USBH_SCSI_ASC_CMD_PHASE_ERR 0x4Au
#define USBH_SCSI_ASC_DATA_PHASE_ERR 0x4Bu
#define USBH_SCSI_ASC_LOG_UNIT_FAILED_SELF_CFG 0x4Cu
#define USBH_SCSI_ASC_OVERLAPPED_CMDS_ATTEMPTED 0x4Eu
#define USBH_SCSI_ASC_WR_APPEND_ERR 0x50u
#define USBH_SCSI_ASC_ERASE_FAIL 0x51u
#define USBH_SCSI_ASC_CARTRIDGE_FAULT 0x52u
#define USBH_SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED 0x53u
#define USBH_SCSI_ASC_SCSI_TO_HOST_SYSTEM_IF_FAIL 0x54u
#define USBH_SCSI_ASC_SYSTEM_RESOURCE_FAIL 0x55u
#define USBH_SCSI_ASC_UNABLE_TO_RECOVER_TOC 0x57u
#define USBH_SCSI_ASC_GENERATION_DOES_NOT_EXIST 0x58u
#define USBH_SCSI_ASC_UPDATED_BLOCK_RD 0x59u
#define USBH_SCSI_ASC_OP_REQUEST_OR_STATE_CHANGE_INPUT 0x5Au
#define USBH_SCSI_ASC_LOG_EXCEPT 0x5Bu
#define USBH_SCSI_ASC_RPL_STATUS_CHANGE 0x5Cu
#define USBH_SCSI_ASC_FAIL_PREDICTION_TH_EXCEEDED 0x5Du
#define USBH_SCSI_ASC_LOW_POWER_CONDITION_ON 0x5Eu
#define USBH_SCSI_ASC_LAMP_FAIL 0x60u
#define USBH_SCSI_ASC_VIDEO_ACQUISITION_ERR 0x61u
#define USBH_SCSI_ASC_SCAN_HEAD_POSITIONING_ERR 0x62u
#define USBH_SCSI_ASC_END_OF_USER_AREA_ENCOUNTERED 0x63u
#define USBH_SCSI_ASC_ILLEGAL_MODE_FOR_THIS_TRACK 0x64u
#define USBH_SCSI_ASC_VOLTAGE_FAULT 0x65u
#define USBH_SCSI_ASC_AUTO_DOCUMENT_FEEDER_COVER_UP 0x66u
#define USBH_SCSI_ASC_CONFIGURATION_FAIL 0x67u
#define USBH_SCSI_ASC_LOG_UNIT_NOT_CONFIGURED 0x68u
#define USBH_SCSI_ASC_DATA_LOSS_ON_LOG_UNIT 0x69u
#define USBH_SCSI_ASC_INFORMATIONAL_REFER_TO_LOG 0x6Au
#define USBH_SCSI_ASC_STATE_CHANGE_HAS_OCCURRED 0x6Bu
#define USBH_SCSI_ASC_REBUILD_FAIL_OCCURRED 0x6Cu
#define USBH_SCSI_ASC_RECALCULATE_FAIL_OCCURRED 0x6Du
#define USBH_SCSI_ASC_CMD_TO_LOG_UNIT_FAILED 0x6Eu
#define USBH_SCSI_ASC_COPY_PROTECTION_KEY_EXCHANGE_FAIL 0x6Fu
#define USBH_SCSI_ASC_DECOMPRESSION_EXCEPT_LONG_ALGO_ID 0x71u
#define USBH_SCSI_ASC_SESSION_FIXATION_ERR 0x72u
#define USBH_SCSI_ASC_CD_CONTROL_ERR 0x73u
#define USBH_SCSI_ASC_SECURITY_ERR 0x74u
/* ----------------- SCSI PAGE PARAMETERS ----------------- */
#define USBH_SCSI_PAGE_CODE_READ_WRITE_ERROR_RECOVERY 0x01u
#define USBH_SCSI_PAGE_CODE_FORMAT_DEVICE 0x03u
#define USBH_SCSI_PAGE_CODE_FLEXIBLE_DISK 0x05u
#define USBH_SCSI_PAGE_CODE_INFORMATIONAL_EXCEPTIONS 0x1Cu
#define USBH_SCSI_PAGE_CODE_ALL 0x3Fu
#define USBH_SCSI_PAGE_LENGTH_INFORMATIONAL_EXCEPTIONS 0x0Au
#define USBH_SCSI_PAGE_LENGTH_READ_WRITE_ERROR_RECOVERY 0x0Au
#define USBH_SCSI_PAGE_LENGTH_FLEXIBLE_DISK 0x1Eu
#define USBH_SCSI_PAGE_LENGTH_FORMAT_DEVICE 0x16u
/*
*********************************************************************************************************
* LOCAL CONSTANTS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* COMMAND BLOCK WRAPPER DATA TYPE
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 5.1.
*
* (2) The 'bmCBWFlags' field is a bit-mapped datum with three subfields :
*
* (a) Bit 7 : Data transfer direction :
*
* (1) 0 = Data-out from host to device.
* (2) 1 = Data-in from device to host.
*
* (b) Bit 6 : Obsolete. Should be set to zero.
* (c) Bits 5-0: Reserved. Should be set to zero.
*********************************************************************************************************
*/
typedef struct usbh_msc_cbw {
CPU_INT32U dCBWSignature; /* Signature to identify this data pkt as CBW. */
CPU_INT32U dCBWTag; /* Command block tag sent by host. */
CPU_INT32U dCBWDataTransferLength; /* Number of bytes of data that host expects to xfer. */
CPU_INT08U bmCBWFlags; /* Flags (see Notes #2). */
CPU_INT08U bCBWLUN; /* LUN to which the command block is being sent. */
CPU_INT08U bCBWCBLength; /* Length of CBWCB in bytes. */
CPU_INT08U CBWCB[16]; /* Command block to be executed by device. */
} USBH_MSC_CBW;
/*
*********************************************************************************************************
* COMMAND STATUS WRAPPER DATA TYPE
*
* Note(s) : (1) See 'USB Mass Storage Class - Bulk Only Transport', Section 5.2.
*********************************************************************************************************
*/
typedef struct usbh_msc_csw {
CPU_INT32U dCSWSignature; /* Signature to identify this data pkt as CSW. */
CPU_INT32U dCSWTag; /* Device shall set this to value in CBW's dCBWTag. */
CPU_INT32U dCSWDataResidue; /* Difference between expected & actual nbr data bytes. */
CPU_INT08U bCSWStatus; /* Indicates success or failure of command. */
} USBH_MSC_CSW;
/*
*********************************************************************************************************
* LOCAL TABLES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
static USBH_MSC_DEV USBH_MSC_DevArr[USBH_MSC_CFG_MAX_DEV];
static MEM_POOL USBH_MSC_DevPool;
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void USBH_MSC_GlobalInit (USBH_ERR *p_err);
static void *USBH_MSC_ProbeIF (USBH_DEV *p_dev,
USBH_IF *p_if,
USBH_ERR *p_err);
static void USBH_MSC_Disconn (void *p_class_dev);
static void USBH_MSC_Suspend (void *p_class_dev);
static void USBH_MSC_Resume (void *p_class_dev);
static void USBH_MSC_DevClr (USBH_MSC_DEV *p_msc_dev);
static USBH_ERR USBH_MSC_EP_Open (USBH_MSC_DEV *p_msc_dev);
static void USBH_MSC_EP_Close (USBH_MSC_DEV *p_msc_dev);
static CPU_INT32U USBH_MSC_XferCmd (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
USBH_MSC_DATA_DIR dir,
void *p_cb,
CPU_INT08U cb_len,
void *p_arg,
CPU_INT32U data_len,
USBH_ERR *p_err);
static USBH_ERR USBH_MSC_TxCBW (USBH_MSC_DEV *p_msc_dev,
USBH_MSC_CBW *p_msc_cbw);
static USBH_ERR USBH_MSC_RxCSW (USBH_MSC_DEV *p_msc_dev,
USBH_MSC_CSW *p_msc_csw);
static USBH_ERR USBH_MSC_TxData (USBH_MSC_DEV *p_msc_dev,
void *p_arg,
CPU_INT32U data_len);
static USBH_ERR USBH_MSC_RxData (USBH_MSC_DEV *p_msc_dev,
void *p_arg,
CPU_INT32U data_len);
static USBH_ERR USBH_MSC_ResetRecovery (USBH_MSC_DEV *p_msc_dev);
static USBH_ERR USBH_MSC_BulkOnlyReset (USBH_MSC_DEV *p_msc_dev);
static void USBH_MSC_FmtCBW (USBH_MSC_CBW *p_cbw,
void *p_buf_dest);
static void USBH_MSC_ParseCSW (USBH_MSC_CSW *p_csw,
void *p_buf_src);
static USBH_ERR USBH_SCSI_CMD_TestUnitReady (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun);
static USBH_ERR USBH_SCSI_CMD_StdInquiry (USBH_MSC_DEV *p_msc_dev,
USBH_MSC_INQUIRY_INFO *p_msc_inquiry_info,
CPU_INT08U lun);
static CPU_INT32U USBH_SCSI_CMD_ReqSense (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT08U *p_arg,
CPU_INT32U data_len,
USBH_ERR *p_err);
static USBH_ERR USBH_SCSI_GetSenseInfo (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT08U *p_sense_key,
CPU_INT08U *p_asc,
CPU_INT08U *p_ascq);
static USBH_ERR USBH_SCSI_CMD_CapacityRd (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U *p_nbr_blks,
CPU_INT32U *p_blk_size);
static CPU_INT32U USBH_SCSI_Rd (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U blk_addr,
CPU_INT16U nbr_blks,
CPU_INT32U blk_size,
void *p_arg,
USBH_ERR *p_err);
static CPU_INT32U USBH_SCSI_Wr (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U blk_addr,
CPU_INT16U nbr_blks,
CPU_INT32U blk_size,
const void *p_arg,
USBH_ERR *p_err);
/*
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INITIALIZED GLOBAL VARIABLES ACCESSES BY OTHER MODULES/OBJECTS
*********************************************************************************************************
*/
USBH_CLASS_DRV USBH_MSC_ClassDrv = {
(CPU_INT08U *)"MASS STORAGE",
USBH_MSC_GlobalInit,
0,
USBH_MSC_ProbeIF,
USBH_MSC_Suspend,
USBH_MSC_Resume,
USBH_MSC_Disconn
};
/*
*********************************************************************************************************
*********************************************************************************************************
* GLOBAL FUNCTION
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBH_MSC_Init()
*
* Description : Initialize a mass storage device instance.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Device logical unit number to initialize.
*
* Return(s) : USBH_ERR_NONE, if MSC device initialization is successful.
* USBH_ERR_INVALID_ARG, if invalid argument passed to 'p_msc_dev'.
* USBH_ERR_DEV_NOT_READY, if MSC device not ready for communication.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* ----- RETURNED BY USBH_SCSI_CMD_TestUnitReady -----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_SCSI_GetSenseInfo -----
* USBH_ERR_UNKNOWN, if unknown error occured.
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Note(s) : (1) This block of code will send TEST_UNIT_READY SCSI command to know if MSC device is
* ready for communication. Host will try up to 20 times in case of MSC device not ready.
* If MSC device is not ready, host sends REQUEST_SENSE SCSI command to know
* exactly why device is not ready.
*
* (2) SENSE KEY, ADDITIONAL SENSE CODE (ASC) and ADDITIONAL SENSE CODE QUALIFIER (ASCQ)
* fields provide a hierarchy of information.
* See "SCSI Primary Commands - 4 (SPC-4)", section "4.5.2.1 Descriptor format sense
* data overview" for more details about REQUEST_SENSE response. Document is
* available at http://www.t10.org/drafts.htm#SPC_Family.
*********************************************************************************************************
*/
USBH_ERR USBH_MSC_Init (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun)
{
CPU_INT08U retry;
CPU_INT08U sense_key;
CPU_INT08U asc;
CPU_INT08U ascq;
CPU_BOOLEAN unit_ready;
USBH_ERR err;
/* ------------------- VALIDATE ARG ------------------- */
if (p_msc_dev == (USBH_MSC_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_OS_MutexLock(p_msc_dev->HMutex); /* Acquire MSC dev lock to avoid multiple access. */
if (err != USBH_ERR_NONE) {
return (err);
}
/* See Note #1. */
if ((p_msc_dev->State == USBH_CLASS_DEV_STATE_CONN) &&
(p_msc_dev->RefCnt > 0u )) {
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("Mass Storage device (LUN %d) is initializing ...\r\n", lun);
#endif
retry = 40u; /* Host attempts 20 times to know if MSC dev ready. */
unit_ready = DEF_FALSE;
while (retry > 0u) {
/* --------------- TEST_UNIT_READY CMD ---------------- */
err = USBH_SCSI_CMD_TestUnitReady(p_msc_dev, lun);
if (err == USBH_ERR_NONE) { /* MSC dev ready for comm. */
unit_ready = DEF_TRUE;
break;
} else if (err == USBH_ERR_MSC_IO) { /* Bulk xfers for the BOT protocol failed. */
USBH_PRINT_ERR(err);
break;
} else {
if (err != USBH_ERR_MSC_CMD_FAILED) { /* MSC dev not ready ... */
USBH_PRINT_ERR(err);
}
err = USBH_SCSI_GetSenseInfo(p_msc_dev, /* ...determine reason of failure. */
lun,
&sense_key,
&asc,
&ascq);
if (err == USBH_ERR_NONE) {
switch (sense_key) { /* See Note #2. */
case USBH_SCSI_SENSE_KEY_UNIT_ATTENTION:/* MSC dev is initializing internally but not rdy. */
switch (asc) {
case USBH_SCSI_ASC_MEDIUM_NOT_PRESENT:
USBH_OS_DlyMS(500u);
break;
/* MSC dev changed its internal state to ready. */
case USBH_SCSI_ASC_NOT_RDY_TO_RDY_CHANGE:
USBH_OS_DlyMS(500u);
break;
default: /* Other Additional Sense Code values not supported. */
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("SENSE KEY1 : %02X, ASC : %02X, ASCQ : %02X\r\n", sense_key, asc, ascq);
#endif
break;
}
break;
case USBH_SCSI_SENSE_KEY_NOT_RDY: /* MSC dev not ready. */
USBH_OS_DlyMS(500u);
break;
default: /* Other Sense Key values not supported. */
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("SENSE KEY2 : %02X, ASC : %02X, ASCQ : %02X\r\n", sense_key, asc, ascq);
#endif
break;
}
retry--;
} else {
USBH_PRINT_ERR(err);
break;
}
}
}
if (unit_ready == DEF_FALSE) {
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("Device is not ready\r\n");
#endif
err = USBH_ERR_DEV_NOT_READY;
}
} else {
err = USBH_ERR_DEV_NOT_READY; /* MSC dev enum not completed by host. */
}
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex); /* Unlock access to MSC dev. */
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_MaxLUN_Get()
*
* Description : Get maximum logical unit number (LUN) supported by MSC device.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBH_ERR_NONE Maximum LUN successfully retrieved.
* USBH_ERR_INVALID_ARG If invalid argument passed to 'p_msc_dev'.
* USBH_ERR_DEV_NOT_READY If device enumeration not completed.
*
* ----- RETURNED BY USBH_CtrlRx() : -----
* USBH_ERR_UNKNOWN, Unknown error occurred.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE, Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* Return(s) : Maximum number of logical unit.
*
* Note(s) : (1) The "GET MAX LUN" is an MSC specific command and is documented in "USB MSC Bulk Only
* Transport specification", Section 3.2.
*********************************************************************************************************
*/
CPU_INT08U USBH_MSC_MaxLUN_Get (USBH_MSC_DEV *p_msc_dev,
USBH_ERR *p_err)
{
CPU_INT08U if_nbr;
ALLOC_CACHE_ALIGN_BUFFER(CPU_INT08U, lun_nbr, 1);
/* ------------------ VALIDATE ARG -------------------- */
if (p_msc_dev == (USBH_MSC_DEV *)0) {
*p_err = USBH_ERR_INVALID_ARG;
return (0u);
}
*p_err = USBH_OS_MutexLock(p_msc_dev->HMutex);
if (*p_err != USBH_ERR_NONE) {
return (0u);
}
/* --------------- GET_MAX_LUN REQUEST ---------------- */
if ((p_msc_dev->State == USBH_CLASS_DEV_STATE_CONN) &&
(p_msc_dev->RefCnt > 0u )) {
if_nbr = USBH_IF_NbrGet(p_msc_dev->IF_Ptr); /* Get IF nbr matching to MSC dev. */
USBH_CtrlRx( p_msc_dev->DevPtr, /* Send GET_MAX_LUN request via a Ctrl xfer. */
USBH_MSC_REQ_GET_MAX_LUN, /* See Note #1. */
(USBH_REQ_DIR_DEV_TO_HOST | USBH_REQ_TYPE_CLASS | USBH_REQ_RECIPIENT_IF),
0u,
if_nbr,
(void *) lun_nbr,
1u,
USBH_MSC_TIMEOUT,
p_err);
if (*p_err != USBH_ERR_NONE) {
(void)USBH_EP_Reset( p_msc_dev->DevPtr, /* Reset dflt EP if ctrl xfer failed. */
(USBH_EP *)0);
if (*p_err == USBH_ERR_EP_STALL) { /* Device may stall if no multiple LUNs support. */
lun_nbr[0] = 0u;
*p_err = USBH_ERR_NONE;
}
}
} else { /* MSC dev enumeration not completed by host. */
*p_err = USBH_ERR_DEV_NOT_READY;
}
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
if (*p_err == USBH_ERR_NONE)
return (lun_nbr[0]);
else
return (0u);
}
/*
*********************************************************************************************************
* USBH_MSC_UnitRdyTest()
*
* Description : Test if a certain logical unit within the MSC device is ready for communication.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical unit number.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBH_ERR_NONE Maximum LUN successfully retrieved.
* USBH_ERR_INVALID_ARG If invalid argument passed to 'p_msc_dev'.
* USBH_ERR_DEV_NOT_READY If device enumeration not completed.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* ---- RETURNED BY USBH_SCSI_CMD_TestUnitReady() ----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Return(s) : DEF_YES, if logical unit ready.
*
* DEF_NO, otherwise.
*
* Note(s) : none.
*********************************************************************************************************
*/
CPU_BOOLEAN USBH_MSC_UnitRdyTest (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
USBH_ERR *p_err)
{
CPU_BOOLEAN unit_rdy = DEF_NO;
/* ------------------- VALIDATE PTR ------------------- */
if (p_msc_dev == (USBH_MSC_DEV *)0) {
*p_err = USBH_ERR_INVALID_ARG;
return (unit_rdy);
}
*p_err = USBH_OS_MutexLock(p_msc_dev->HMutex);
if (*p_err != USBH_ERR_NONE) {
return (unit_rdy);
}
/* --------------- TEST_UNIT_READY REQ ---------------- */
if ((p_msc_dev->State == USBH_CLASS_DEV_STATE_CONN) &&
(p_msc_dev->RefCnt > 0u )) {
*p_err = USBH_SCSI_CMD_TestUnitReady(p_msc_dev, 0u);
if (*p_err == USBH_ERR_NONE) {
unit_rdy = DEF_YES;
} else if (*p_err == USBH_ERR_MSC_CMD_FAILED) {
*p_err = USBH_ERR_NONE; /* CSW reporting cmd failed for this req NOT an err. */
unit_rdy = DEF_NO;
} else {
/* Empty Else Statement */
}
} else { /* MSC dev enumeration not completed by host. */
*p_err = USBH_ERR_DEV_NOT_READY;
}
USBH_OS_MutexUnlock(p_msc_dev->HMutex);
return (unit_rdy);
}
/*
*********************************************************************************************************
* USBH_MSC_CapacityRd()
*
* Description : Read mass storage device capacity (i.e number of blocks and block size) of specified LUN
* by sending READ_CAPACITY SCSI command.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical unit number.
*
* p_nbr_blks Pointer to variable that receives number of blocks.
*
* p_blk_size Pointer to variable that receives block size.
*
* Return(s) : USBH_ERR_NONE, If capacity of MSC device retrieved successfully.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_msc_dev' / 'p_nbr_blks' /
* 'p_blk_size'.
* USBH_ERR_DEV_NOT_READY, If device enumeration not completed.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
* List error codes from USBH_SCSI_CMD_CapacityRd
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_MSC_CapacityRd (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U *p_nbr_blks,
CPU_INT32U *p_blk_size)
{
USBH_ERR err;
/* ------------------- VALIDATE PTR ------------------- */
if ((p_msc_dev == (USBH_MSC_DEV *)0) ||
(p_nbr_blks == (CPU_INT32U *)0) ||
(p_blk_size == (CPU_INT32U *)0)) {
err = USBH_ERR_INVALID_ARG;
return (err);
}
err = USBH_OS_MutexLock(p_msc_dev->HMutex);
if (err != USBH_ERR_NONE) {
return (err);
}
/* -------------- READ_CAPACITY REQUEST --------------- */
if ((p_msc_dev->State == USBH_CLASS_DEV_STATE_CONN) &&
(p_msc_dev->RefCnt > 0u )) {
err = USBH_SCSI_CMD_CapacityRd(p_msc_dev, /* Issue READ_CAPACITY SCSI cmd using bulk xfers. */
lun,
p_nbr_blks,
p_blk_size);
} else { /* MSC dev enumeration not completed by host. */
err = USBH_ERR_DEV_NOT_READY;
}
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex); /* Unlock access to MSC dev. */
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_StdInquiry()
*
* Description : Retrieve various information about a specific logical unit inside mass storage device
* such as device type, if device is removable, vendor/product identification, etc.
* INQUIRY SCSI command is used.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* p_msc_inquiry_info Pointer to structure that will receive the informations.
*
* lun Logical unit number.
*
* Return(s) : USBH_ERR_NONE, If information about logical unit retrieved successfully.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_msc_dev' / 'p_msc_inquiry_info'.
* USBH_ERR_NOT_SUPPORTED, If INQUIRY command has failed.
* USBH_ERR_DEV_NOT_READY, If device enumeration not completed.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_MSC_StdInquiry (USBH_MSC_DEV *p_msc_dev,
USBH_MSC_INQUIRY_INFO *p_msc_inquiry_info,
CPU_INT08U lun)
{
USBH_ERR err;
/* ------------------- VALIDATE PTR ------------------- */
if ((p_msc_dev == (USBH_MSC_DEV *)0) ||
(p_msc_inquiry_info == (USBH_MSC_INQUIRY_INFO *)0)) {
err = USBH_ERR_INVALID_ARG;
return (err);
}
err = USBH_OS_MutexLock(p_msc_dev->HMutex);
if (err != USBH_ERR_NONE) {
return (err);
}
/* ----------------- INQUIRY REQUEST ------------------ */
if ((p_msc_dev->State == USBH_CLASS_DEV_STATE_CONN) &&
(p_msc_dev->RefCnt > 0u )) {
err = USBH_SCSI_CMD_StdInquiry(p_msc_dev, /* Issue INQUIRY SCSI command using Bulk xfers. */
p_msc_inquiry_info,
lun);
if (err == USBH_ERR_NONE) {
err = USBH_ERR_NONE;
} else {
err = USBH_ERR_NOT_SUPPORTED;
}
} else {
err = USBH_ERR_DEV_NOT_READY;
}
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_RefAdd()
*
* Description : Increment counter of connected mass storage devices.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : USBH_ERR_NONE, If counter successfully incremented.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_msc_dev'.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_MSC_RefAdd (USBH_MSC_DEV *p_msc_dev)
{
USBH_ERR err;
/* ------------------- VALIDATE ARG ------------------- */
if (p_msc_dev == (USBH_MSC_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_OS_MutexLock(p_msc_dev->HMutex);
if (err != USBH_ERR_NONE) {
return (err);
}
p_msc_dev->RefCnt++;
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_RefRel()
*
* Description : Decrement counter of connected mass storage devices and free device if counter = 0.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : USBH_ERR_NONE, If counter successfully decremented.
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'p_msc_dev'.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
USBH_ERR USBH_MSC_RefRel (USBH_MSC_DEV *p_msc_dev)
{
USBH_ERR err;
LIB_ERR err_lib;
/* ------------------- VALIDATE PTR ------------------- */
if (p_msc_dev == (USBH_MSC_DEV *)0) {
return (USBH_ERR_INVALID_ARG);
}
err = USBH_OS_MutexLock(p_msc_dev->HMutex);
if (err != USBH_ERR_NONE) {
return (err);
}
if (p_msc_dev->RefCnt > 0u) {
p_msc_dev->RefCnt--;
if ((p_msc_dev->RefCnt == 0u) &&
(p_msc_dev->State == USBH_CLASS_DEV_STATE_DISCONN)) {
/* Release MSC dev if no more ref on it. */
Mem_PoolBlkFree( &USBH_MSC_DevPool,
(void *)p_msc_dev,
&err_lib);
}
}
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_Rd()
*
* Description : Read specified number of blocks from device using READ_10 SCSI command.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical unit number.
*
* blk_addr Block address.
*
* nbr_blks Number of blocks to read.
*
* blk_size Block size.
*
* p_arg Pointer to data buffer.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBH_ERR_NONE Block(s) read successfully.
* USBH_ERR_INVALID_ARG If invalid argument passed to 'p_msc_dev'.
* USBH_ERR_DEV_NOT_READY If device enumeration not completed.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* ----- RETURNED BY USBH_SCSI_Rd -----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Return(s) : Number of octets read.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_INT32U USBH_MSC_Rd (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U blk_addr,
CPU_INT16U nbr_blks,
CPU_INT32U blk_size,
void *p_arg,
USBH_ERR *p_err)
{
CPU_INT32U xfer_len;
if (p_msc_dev == (USBH_MSC_DEV *)0) {
*p_err = USBH_ERR_INVALID_ARG;
return (0u);
}
*p_err = USBH_OS_MutexLock(p_msc_dev->HMutex);
if (*p_err != USBH_ERR_NONE) {
return (0u);
}
if ((p_msc_dev->State == USBH_CLASS_DEV_STATE_CONN) &&
(p_msc_dev->RefCnt > 0u )) {
xfer_len = USBH_SCSI_Rd(p_msc_dev,
lun,
blk_addr,
nbr_blks,
blk_size,
p_arg,
p_err);
} else {
xfer_len = 0u;
*p_err = USBH_ERR_DEV_NOT_READY;
}
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_MSC_Wr()
*
* Description : Write specified number of blocks to the device.
*
* Argument(s) : p_msc_dev Pointer to mass storage device.
*
* lun Logical unit number.
*
* blk_addr Block address.
*
* nbr_blks Number of blocks to write.
*
* blk_size Block size.
*
* p_arg Pointer to data buffer.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBH_ERR_NONE Block(s) write successfully.
* USBH_ERR_INVALID_ARG If invalid argument passed to 'p_msc_dev'.
* USBH_ERR_DEV_NOT_READY If device enumeration not completed.
*
* ----- RETURNED BY USBH_OS_MutexLock() : -----
* USBH_ERR_INVALID_ARG, If invalid argument passed to 'mutex'.
* USBH_ERR_OS_ABORT, If mutex wait aborted.
* USBH_ERR_OS_FAIL, Otherwise.
*
* ----- RETURNED BY USBH_SCSI_Rd -----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Return(s) : Number of octets written.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_INT32U USBH_MSC_Wr ( USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U blk_addr,
CPU_INT16U nbr_blks,
CPU_INT32U blk_size,
const void *p_arg,
USBH_ERR *p_err)
{
CPU_INT32U xfer_len;
if (p_msc_dev == (USBH_MSC_DEV *)0) {
*p_err = USBH_ERR_INVALID_ARG;
return (0u);
}
*p_err = USBH_OS_MutexLock(p_msc_dev->HMutex);
if (*p_err != USBH_ERR_NONE) {
return (0u);
}
if ((p_msc_dev->State == USBH_CLASS_DEV_STATE_CONN ) &&
(p_msc_dev->RefCnt > 0 )) {
xfer_len = USBH_SCSI_Wr(p_msc_dev,
lun,
blk_addr,
nbr_blks,
blk_size,
p_arg,
p_err);
} else {
xfer_len = 0u;
*p_err = USBH_ERR_DEV_NOT_READY;
}
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
return (xfer_len);
}
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBH_MSC_GlobalInit()
*
* Description : Initialize MSC.
*
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
*
* USBH_ERR_NONE MSC successfully initalized.
* USBH_ERR_ALLOC If MSC device cannot be allocated.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_MSC_GlobalInit (USBH_ERR *p_err)
{
CPU_INT08U ix;
CPU_SIZE_T octets_reqd;
LIB_ERR err_lib;
/* --------------- INIT MSC DEV STRUCT ---------------- */
for (ix = 0u; ix < USBH_MSC_CFG_MAX_DEV; ix++) {
USBH_MSC_DevClr(&USBH_MSC_DevArr[ix]);
USBH_OS_MutexCreate(&USBH_MSC_DevArr[ix].HMutex);
}
Mem_PoolCreate ( &USBH_MSC_DevPool, /* POOL for managing MSC dev struct. */
(void *)USBH_MSC_DevArr,
(sizeof(USBH_MSC_DEV) * USBH_MSC_CFG_MAX_DEV),
USBH_MSC_CFG_MAX_DEV,
sizeof(USBH_MSC_DEV),
sizeof(CPU_ALIGN),
&octets_reqd,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("%d octets required\r\n", octets_reqd);
#endif
*p_err = USBH_ERR_ALLOC;
} else {
*p_err = USBH_ERR_NONE;
}
}
/*
*********************************************************************************************************
* USBH_MSC_ProbeIF()
*
* Description : Determine if interface is mass storage class interface.
*
* Argument(s) : p_dev Pointer to USB device.
*
* p_if Pointer to interface.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBH_ERR_NONE MSC successfully initalized.
* USBH_ERR_DEV_ALLOC MSC device cannot be allocated.
* USBH_ERR_CLASS_DRV_NOT_FOUND IF type is not MSC.
*
* ----- RETURNED BY USBH_IF_DescGet() : -----
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'alt_ix'.
*
* ----- RETURNED BY USBH_MSC_EP_Open -----
* USBH_ERR_EP_ALLOC, If USBH_CFG_MAX_NBR_EPS reached.
* USBH_ERR_EP_NOT_FOUND, If endpoint with given type and direction not found.
* USBH_ERR_OS_SIGNAL_CREATE, if mutex or semaphore creation failed.
* Host controller drivers error, Otherwise.
*
* Return(s) : p_msc_dev, if device has a mass storage class interface.
* 0, otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void *USBH_MSC_ProbeIF (USBH_DEV *p_dev,
USBH_IF *p_if,
USBH_ERR *p_err)
{
USBH_IF_DESC p_if_desc;
USBH_MSC_DEV *p_msc_dev;
LIB_ERR err_lib;
p_msc_dev = (USBH_MSC_DEV *)0;
*p_err = USBH_IF_DescGet(p_if, 0u, &p_if_desc);
if (*p_err != USBH_ERR_NONE) {
return ((void *)0);
}
/* Chk for class, sub class and protocol. */
if ((p_if_desc.bInterfaceClass == USBH_CLASS_CODE_MASS_STORAGE) &&
((p_if_desc.bInterfaceSubClass == USBH_MSC_SUBCLASS_CODE_SCSI) ||
(p_if_desc.bInterfaceSubClass == USBH_MSC_SUBCLASS_CODE_SFF_8070i)) &&
(p_if_desc.bInterfaceProtocol == USBH_MSC_PROTOCOL_CODE_BULK_ONLY)) {
/* Alloc dev from MSC dev pool. */
p_msc_dev = (USBH_MSC_DEV *)Mem_PoolBlkGet(&USBH_MSC_DevPool,
sizeof(USBH_MSC_DEV),
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBH_ERR_DEV_ALLOC;
return ((void *)0);
}
USBH_MSC_DevClr(p_msc_dev);
p_msc_dev->RefCnt = (CPU_INT08U )0;
p_msc_dev->State = USBH_CLASS_DEV_STATE_CONN;
p_msc_dev->DevPtr = p_dev;
p_msc_dev->IF_Ptr = p_if;
*p_err = USBH_MSC_EP_Open(p_msc_dev); /* Open Bulk in/out EPs. */
if (*p_err != USBH_ERR_NONE) {
Mem_PoolBlkFree( &USBH_MSC_DevPool,
(void *)p_msc_dev,
&err_lib);
}
} else {
*p_err = USBH_ERR_CLASS_DRV_NOT_FOUND;
}
if (*p_err != USBH_ERR_NONE) {
p_msc_dev = (void *)0;
}
return ((void *)p_msc_dev);
}
/*
*********************************************************************************************************
* USBH_MSC_Disconn()
*
* Description : Handle disconnection of mass storage device.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_MSC_Disconn (void *p_class_dev)
{
LIB_ERR err_lib;
USBH_MSC_DEV *p_msc_dev;
p_msc_dev = (USBH_MSC_DEV *)p_class_dev;
(void)USBH_OS_MutexLock(p_msc_dev->HMutex);
p_msc_dev->State = USBH_CLASS_DEV_STATE_DISCONN;
USBH_MSC_EP_Close(p_msc_dev); /* Close bulk in/out EPs. */
if (p_msc_dev->RefCnt == 0u) { /* Release MSC dev. */
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
Mem_PoolBlkFree( &USBH_MSC_DevPool,
(void *)p_msc_dev,
&err_lib);
return;
}
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
}
/*
*********************************************************************************************************
* USBH_MSC_Suspend()
*
* Description : Suspend MSC device. Waits for completion of any pending I/O.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_MSC_Suspend (void *p_class_dev)
{
USBH_MSC_DEV *p_msc_dev;
p_msc_dev = (USBH_MSC_DEV *)p_class_dev;
(void)USBH_OS_MutexLock(p_msc_dev->HMutex);
p_msc_dev->State = USBH_CLASS_DEV_STATE_SUSPEND;
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
}
/*
*********************************************************************************************************
* USBH_MSC_Resume()
*
* Description : Resume MSC device.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_MSC_Resume (void *p_class_dev)
{
USBH_MSC_DEV *p_msc_dev;
p_msc_dev = (USBH_MSC_DEV *)p_class_dev;
(void)USBH_OS_MutexLock(p_msc_dev->HMutex);
p_msc_dev->State = USBH_CLASS_DEV_STATE_CONN;
(void)USBH_OS_MutexUnlock(p_msc_dev->HMutex);
}
/*
*********************************************************************************************************
* USBH_MSC_DevClr()
*
* Description : Clear USBH_MSC_DEV structure.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_MSC_DevClr (USBH_MSC_DEV *p_msc_dev)
{
p_msc_dev->DevPtr = (USBH_DEV *)0;
p_msc_dev->IF_Ptr = (USBH_IF *)0;
p_msc_dev->State = USBH_CLASS_DEV_STATE_NONE;
p_msc_dev->RefCnt = 0u;
}
/*
*********************************************************************************************************
* USBH_MSC_EP_Open()
*
* Description : Open bulk IN & OUT endpoints.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : USBH_ERR_NONE, if endpoints successfully opened.
*
* ---- RETURNED BY USBH_BulkInOpen ----
* USBH_ERR_EP_ALLOC, If USBH_CFG_MAX_NBR_EPS reached.
* USBH_ERR_EP_NOT_FOUND, If endpoint with given type and direction not found.
* USBH_ERR_OS_SIGNAL_CREATE, if mutex or semaphore creation failed.
* Host controller drivers error, Otherwise.
*
* ---- RETURNED BY USBH_BulkOutOpen ----
* USBH_ERR_EP_ALLOC, If USBH_CFG_MAX_NBR_EPS reached.
* USBH_ERR_EP_NOT_FOUND, If endpoint with given type and direction not found.
* USBH_ERR_OS_SIGNAL_CREATE, if mutex or semaphore creation failed.
* Host controller drivers error, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_MSC_EP_Open (USBH_MSC_DEV *p_msc_dev)
{
USBH_ERR err;
err = USBH_BulkInOpen(p_msc_dev->DevPtr,
p_msc_dev->IF_Ptr,
&p_msc_dev->BulkInEP);
if (err != USBH_ERR_NONE) {
return (err);
}
err = USBH_BulkOutOpen(p_msc_dev->DevPtr,
p_msc_dev->IF_Ptr,
&p_msc_dev->BulkOutEP);
if (err != USBH_ERR_NONE) {
USBH_MSC_EP_Close(p_msc_dev);
}
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_EP_Close()
*
* Description : Close bulk IN & OUT endpoints.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_MSC_EP_Close (USBH_MSC_DEV *p_msc_dev)
{
USBH_EP_Close(&p_msc_dev->BulkInEP);
USBH_EP_Close(&p_msc_dev->BulkOutEP);
}
/*
*********************************************************************************************************
* USBH_MSC_XferCmd()
*
* Description : Executes MSC command cycle. Sends command (CBW), followed by data stage (if present),
* and then receive status (CSW).
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical Unit Number.
*
* dir Direction of data transfer, if present.
*
* p_cb Pointer to command block.
*
* cb_len Command block length, in octets.
*
* p_arg Pointer to data buffer, if data stage present.
*
* data_len Length of data buffer in octets, if data stage present.
*
* p_err Pointer to variable that will receive the return error code from this function :
* USBH_ERR_NONE Command successfully transmitted.
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
*
* ----- RETURNED BY USBH_MSC_RxCSW() : -----
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not Bulk or direction is not IN.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_MSC_RxData() : -----
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not Bulk or direction is not IN.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_MSC_TxCBW() : -----
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not Bulk or direction is not OUT.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_MSC_TxData() : -----
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not Bulk or direction is not OUT.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Return(s) : Number of octets transferred.
*
* Note(s) : None.
*********************************************************************************************************
*/
static CPU_INT32U USBH_MSC_XferCmd (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
USBH_MSC_DATA_DIR dir,
void *p_cb,
CPU_INT08U cb_len,
void *p_arg,
CPU_INT32U data_len,
USBH_ERR *p_err)
{
CPU_INT32U xfer_len;
USBH_MSC_CBW msc_cbw;
USBH_MSC_CSW msc_csw;
/* Prepare CBW. */
msc_cbw.dCBWSignature = USBH_MSC_SIG_CBW;
msc_cbw.dCBWTag = 0u;
msc_cbw.dCBWDataTransferLength = data_len;
msc_cbw.bmCBWFlags = (dir == USBH_MSC_DATA_DIR_NONE) ? 0u : dir;
msc_cbw.bCBWLUN = lun;
msc_cbw.bCBWCBLength = cb_len;
Mem_Copy((void *)msc_cbw.CBWCB,
p_cb,
cb_len);
*p_err = USBH_MSC_TxCBW(p_msc_dev, &msc_cbw); /* Send CBW to dev. */
if (*p_err != USBH_ERR_NONE) {
return (0u);
}
switch (dir) {
case USBH_MSC_DATA_DIR_OUT:
*p_err = USBH_MSC_TxData(p_msc_dev,
p_arg,
data_len); /* Send data to dev. */
break;
case USBH_MSC_DATA_DIR_IN:
*p_err = USBH_MSC_RxData(p_msc_dev,
p_arg,
data_len); /* Receive data from dev. */
break;
default:
*p_err = USBH_ERR_NONE;
break;
}
if (*p_err != USBH_ERR_NONE) {
return (0u);
}
Mem_Set((void *)&msc_csw,
0u,
USBH_MSC_LEN_CSW);
*p_err = USBH_MSC_RxCSW(p_msc_dev, &msc_csw); /* Receive CSW. */
if ((msc_csw.dCSWSignature != USBH_MSC_SIG_CSW ) ||
(msc_csw.bCSWStatus == USBH_MSC_BCSWSTATUS_PHASE_ERROR) ||
(msc_csw.dCSWTag != msc_cbw.dCBWTag )) {
USBH_MSC_ResetRecovery(p_msc_dev); /* Invalid CSW, issue reset recovery. */
}
if (*p_err == USBH_ERR_NONE) {
switch (msc_csw.bCSWStatus) {
case USBH_MSC_BCSWSTATUS_CMD_PASSED:
*p_err = USBH_ERR_NONE;
break;
case USBH_MSC_BCSWSTATUS_CMD_FAILED:
*p_err = USBH_ERR_MSC_CMD_FAILED;
break;
case USBH_MSC_BCSWSTATUS_PHASE_ERROR:
*p_err = USBH_ERR_MSC_CMD_PHASE;
break;
default:
*p_err = USBH_ERR_MSC_CMD_FAILED;
break;
}
} else {
*p_err = USBH_ERR_MSC_IO;
}
/* Actual len of data xfered to dev. */
xfer_len = msc_cbw.dCBWDataTransferLength - msc_csw.dCSWDataResidue;
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_MSC_TxCBW()
*
* Description : Send Command Block Wrapper (CBW) to device through bulk OUT endpoint.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* p_msc_cbw Pointer to Command Block Wrapper (CBW).
*
* Return(s) : USBH_ERR_NONE If CBW successfully transmitted.
*
* ----- RETURNED BY USBH_BulkTx() : -----
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not Bulk or direction is not OUT.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_MSC_TxCBW (USBH_MSC_DEV *p_msc_dev,
USBH_MSC_CBW *p_msc_cbw)
{
ALLOC_CACHE_ALIGN_BUFFER(CPU_INT08U, cmd_buf, USBH_MSC_LEN_CBW);
USBH_ERR err;
CPU_INT32U len;
Mem_Clr((void *)cmd_buf,
USBH_MSC_LEN_CBW);
USBH_MSC_FmtCBW(p_msc_cbw, cmd_buf); /* Format CBW. */
len = USBH_BulkTx( &p_msc_dev->BulkOutEP, /* Send CBW through bulk OUT EP. */
(void *)&cmd_buf[0],
USBH_MSC_LEN_CBW,
USBH_MSC_TIMEOUT,
&err);
if (len != USBH_MSC_LEN_CBW) {
if (err != USBH_ERR_NONE) {
USBH_PRINT_ERR(err);
USBH_EP_Reset(p_msc_dev->DevPtr,
&p_msc_dev->BulkOutEP); /* Clear EP err on host side. */
}
if (err == USBH_ERR_EP_STALL) {
USBH_MSC_ResetRecovery(p_msc_dev);
}
} else {
err = USBH_ERR_NONE;
}
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_RxCSW()
*
* Description : Receive Command Status Word (CSW) from device through bulk IN endpoint.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* p_msc_csw Pointer to Command Status Word (CSW).
*
* Return(s) : USBH_ERR_NONE, If CSW successfully received.
*
* ----- RETURNED BY USBH_BulkRx() : -----
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not Bulk or direction is not IN.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_MSC_RxCSW (USBH_MSC_DEV *p_msc_dev,
USBH_MSC_CSW *p_msc_csw)
{
CPU_INT32U retry;
ALLOC_CACHE_ALIGN_BUFFER(CPU_INT08U, status_buf, USBH_MSC_LEN_CSW);
USBH_ERR err;
CPU_INT32U len;
err = USBH_ERR_NONE;
retry = 2u;
/* Receive CSW from dev through bulk IN EP. */
while (retry > 0u) {
len = USBH_BulkRx( &p_msc_dev->BulkInEP,
(void *)&status_buf[0],
USBH_MSC_LEN_CSW,
USBH_MSC_TIMEOUT,
&err);
if (len != USBH_MSC_LEN_CSW) {
USBH_EP_Reset(p_msc_dev->DevPtr,
&p_msc_dev->BulkInEP); /* Clear err on host side. */
if (err == USBH_ERR_EP_STALL) {
(void)USBH_EP_StallClr(&p_msc_dev->BulkInEP);
retry--;
} else {
break;
}
} else {
err = USBH_ERR_NONE;
USBH_MSC_ParseCSW(p_msc_csw, status_buf);
break;
}
}
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_TxData()
*
* Description : Send data to device through bulk OUT endpoint.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* p_arg Pointer to data buffer.
*
* data_len Length of data buffer in octets.
*
* Return(s) : USBH_ERR_NONE, if data successfully transmitted.
*
* ----- RETURNED BY USBH_BulkTx() : -----
* USBH_ERR_NONE Bulk transfer successfully transmitted.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not Bulk or direction is not OUT.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_NONE, URB is successfully submitted to host controller.
* Host controller drivers error code, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_MSC_TxData (USBH_MSC_DEV *p_msc_dev,
void *p_arg,
CPU_INT32U data_len)
{
USBH_ERR err;
CPU_BOOLEAN retry;
CPU_INT08U *p_data_08;
CPU_INT16U retry_cnt;
CPU_INT32U p_data_ix;
CPU_INT32U data_len_rem;
CPU_INT32U data_len_tx;
err = USBH_ERR_NONE;
p_data_ix = 0u;
p_data_08 = (CPU_INT08U *)p_arg;
data_len_rem = data_len;
retry_cnt = 0u;
retry = DEF_TRUE;
while ((data_len_rem > 0u ) &&
(retry == DEF_TRUE)) {
data_len_tx = USBH_BulkTx( &p_msc_dev->BulkOutEP,
(void *)(p_data_08 + p_data_ix),
data_len_rem,
USBH_MSC_TIMEOUT,
&err);
switch (err) {
case USBH_ERR_DEV_NOT_RESPONDING:
case USBH_ERR_HC_IO:
retry_cnt++;
if (retry_cnt >= USBH_MSC_MAX_TRANSFER_RETRY) {
retry = DEF_FALSE;
}
break;
case USBH_ERR_NONE:
if (data_len_tx < data_len_rem) {
data_len_rem -= data_len_tx;
p_data_ix += data_len_tx;
} else {
data_len_rem = 0u;
}
break;
default:
retry = DEF_FALSE;
break;
}
}
if (err != USBH_ERR_NONE) {
USBH_PRINT_ERR(err);
USBH_EP_Reset(p_msc_dev->DevPtr,
&p_msc_dev->BulkOutEP);
if (err == USBH_ERR_EP_STALL) {
(void)USBH_EP_StallClr(&p_msc_dev->BulkOutEP);
} else {
(void)USBH_MSC_ResetRecovery(p_msc_dev);
}
}
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_RxData()
*
* Description : Receive data from device through bulk IN endpoint.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* p_arg Pointer to data buffer.
*
* data_len Length of data buffer in octets.
*
* Return(s) : USBH_ERR_NONE, if data successfully received.
*
* ----- RETURNED BY USBH_BulkRx() : -----
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type is not Bulk or direction is not IN.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_MSC_RxData (USBH_MSC_DEV *p_msc_dev,
void *p_arg,
CPU_INT32U data_len)
{
USBH_ERR err;
CPU_BOOLEAN retry;
CPU_INT08U *p_data_08;
CPU_INT16U retry_cnt;
CPU_INT32U p_data_ix;
CPU_INT32U data_len_rem;
CPU_INT32U data_len_rx;
err = USBH_ERR_NONE;
p_data_ix = 0u;
p_data_08 = (CPU_INT08U *)p_arg;
data_len_rem = data_len;
retry_cnt = 0u;
retry = DEF_TRUE;
while ((data_len_rem > 0u ) &&
(retry == DEF_TRUE)) {
data_len_rx = USBH_BulkRx( &p_msc_dev->BulkInEP,
(void *)(p_data_08 + p_data_ix),
data_len_rem,
USBH_MSC_TIMEOUT,
&err);
switch (err) {
case USBH_ERR_DEV_NOT_RESPONDING:
case USBH_ERR_HC_IO:
retry_cnt++;
if (retry_cnt >= USBH_MSC_MAX_TRANSFER_RETRY) {
retry = DEF_FALSE;
}
break;
case USBH_ERR_NONE:
if (data_len_rx < data_len_rem) {
data_len_rem -= data_len_rx; /* Update remaining nbr of octets to read. */
p_data_ix += data_len_rx; /* Update buf ix. */
} else {
data_len_rem = 0u;
}
break;
default:
retry = DEF_FALSE;
break;
}
}
if (err != USBH_ERR_NONE) {
USBH_PRINT_ERR(err);
(void)USBH_EP_Reset(p_msc_dev->DevPtr,
&p_msc_dev->BulkInEP); /* Clr err on host side EP. */
if (err == USBH_ERR_EP_STALL) {
(void)USBH_EP_StallClr(&p_msc_dev->BulkInEP);
err = USBH_ERR_NONE;
} else {
(void)USBH_MSC_ResetRecovery(p_msc_dev);
err = USBH_ERR_MSC_IO;
}
}
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_ResetRecovery()
*
* Description : Apply bulk-only reset recovery to device on phase error & clear stalled endpoints.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : USBH_ERR_NONE, If reset recovery procedure is successful.
*
* ----- RETURNED BY USBH_MSC_BulkOnlyReset -----
* USBH_ERR_UNKNOWN, Unknown error occurred.
* USBH_ERR_INVALID_ARG, Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE, Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* ----- RETURNED BY USBH_EP_StallClr -----
* USBH_ERR_UNKNOWN Unknown error occurred.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* USBH_ERR_HC_IO, Root hub input/output error.
* USBH_ERR_EP_STALL, Root hub does not support request.
* Host controller drivers error code, Otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
static USBH_ERR USBH_MSC_ResetRecovery (USBH_MSC_DEV *p_msc_dev)
{
USBH_ERR err;
err = USBH_MSC_BulkOnlyReset(p_msc_dev);
if (err != USBH_ERR_NONE) {
return (err);
}
err = USBH_EP_StallClr(&p_msc_dev->BulkInEP);
if (err != USBH_ERR_NONE) {
return (err);
}
err = USBH_EP_StallClr(&p_msc_dev->BulkOutEP);
return (err);
}
/*
*********************************************************************************************************
* USBH_MSC_BulkOnlyReset()
*
* Description : Issue bulk-only reset.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* Return(s) : Specific error code.
*
* Note(s) : (1)The "BULK ONLY MASS STORAGE RESET" is an MSC specific request is documented in USB
MSC Bulk only transport specification, section 3.1
*********************************************************************************************************
*/
static USBH_ERR USBH_MSC_BulkOnlyReset (USBH_MSC_DEV *p_msc_dev)
{
USBH_ERR err;
CPU_INT08U if_nbr;
err = USBH_ERR_NONE;
if_nbr = USBH_IF_NbrGet(p_msc_dev->IF_Ptr);
USBH_CtrlTx( p_msc_dev->DevPtr,
USBH_MSC_REQ_MASS_STORAGE_RESET, /* See Note(s) #1 */
(USBH_REQ_DIR_HOST_TO_DEV | USBH_REQ_TYPE_CLASS | USBH_REQ_RECIPIENT_IF),
0u,
if_nbr,
(void *)0,
0u,
USBH_MSC_TIMEOUT,
&err);
if (err != USBH_ERR_NONE) {
USBH_PRINT_ERR(err);
USBH_EP_Reset( p_msc_dev->DevPtr,
(USBH_EP *)0);
}
return (err);
}
/*
*********************************************************************************************************
* USBH_SCSI_CMD_StdInquiry()
*
* Description : Read inquiry data of device.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* p_msc_inquiry_info Pointer to inquiry info structure.
*
* lun Logical unit number.
*
* Return(s) : USBH_ERR_NONE, if command is successful.
*
* ---- RETURNED BY USBH_MSC_XferCmd ----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Note(s) : (1) The SCSI "INQUIRY" command is documented in 'SCSI Primary Commands - 3
* (SPC-3)', Section 6.33.
*********************************************************************************************************
*/
static USBH_ERR USBH_SCSI_CMD_StdInquiry (USBH_MSC_DEV *p_msc_dev,
USBH_MSC_INQUIRY_INFO *p_msc_inquiry_info,
CPU_INT08U lun)
{
USBH_ERR err;
CPU_INT08U cmd[6];
ALLOC_CACHE_ALIGN_BUFFER(CPU_INT08U, inq_data, 0x24);
/* ------- PREPARE SCSI CMD BLOCK (SEE NOTE #1) ------- */
cmd[0] = USBH_SCSI_CMD_INQUIRY; /* Operation code (0x12). */
cmd[1] = 0u; /* Std inquiry data. */
cmd[2] = 0u; /* Page code. */
cmd[3] = 0u; /* Alloc len. */
cmd[4] = 0x24u; /* Alloc len. */
cmd[5] = 0u; /* Ctrl. */
/* ------------------ SEND SCSI CMD ------------------- */
USBH_MSC_XferCmd( p_msc_dev,
lun,
USBH_MSC_DATA_DIR_IN,
(void *)&cmd[0],
6u,
(void *) inq_data,
0x24u,
&err);
if (err == USBH_ERR_NONE) {
p_msc_inquiry_info->DevType = inq_data[0] & 0x1Fu;
p_msc_inquiry_info->IsRemovable = inq_data[1] >> 7u;
Mem_Copy((void *) p_msc_inquiry_info->Vendor_ID,
(void *)&inq_data[8],
8u);
Mem_Copy((void *) p_msc_inquiry_info->Product_ID,
(void *)&inq_data[16],
16u);
p_msc_inquiry_info->ProductRevisionLevel = MEM_VAL_GET_INT32U(&inq_data[32]);
}
return (err);
}
/*
*********************************************************************************************************
* USBH_SCSI_CMD_TestUnitReady()
*
* Description : Read number of sectors & sector size.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical unit number.
*
* Return(s) : USBH_ERR_NONE, if the command is successful.
*
* ---- RETURNED BY USBH_MSC_XferCmd ----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Note(s) : (1) The SCSI "TEST UNIT READY" command is documented in 'SCSI Primary Commands - 3
* (SPC-3)', Section 6.33.
*********************************************************************************************************
*/
static USBH_ERR USBH_SCSI_CMD_TestUnitReady (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun)
{
USBH_ERR err;
CPU_INT08U cmd[6];
/* See Note #1. */
/* ------------ PREPARE SCSI COMMAND BLOCK ------------ */
cmd[0] = USBH_SCSI_CMD_TEST_UNIT_READY; /* Operation code (0x00). */
cmd[1] = 0u; /* Reserved. */
cmd[2] = 0u; /* Reserved. */
cmd[3] = 0u; /* Reserved. */
cmd[4] = 0u; /* Reserved. */
cmd[5] = 0u; /* Control. */
USBH_MSC_XferCmd( p_msc_dev, /* ---------------- SEND SCSI COMMAND ----------------- */
lun,
USBH_MSC_DATA_DIR_NONE,
(void *)&cmd[0],
6u,
(void *) 0,
0u,
&err);
return (err);
}
/*
*********************************************************************************************************
* USBH_SCSI_CMD_ReqSense()
*
* Description : Issue command to obtain sense data.
*
* Argument(s) : p_msc_dev Pointer to USBH_MSC_DEV device.
*
* lun Logical unit number
*
* p_arg Pointer to data buffer.
*
* data_len Number of data bytes to receive.
*
* p_err Pointer to variable that will receive the return error code from this function :
* USBH_ERR_NONE Command successfully transmitted.
*
* ---- RETURNED BY USBH_MSC_XferCmd ----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Return(s) : Number of octets sent.
*
* Note(s) : (1) The SCSI "REQUEST SENSE" command is documented in 'SCSI Primary Commands - 3
* (SPC-3)', Section 6.27.
*********************************************************************************************************
*/
static CPU_INT32U USBH_SCSI_CMD_ReqSense (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT08U *p_arg,
CPU_INT32U data_len,
USBH_ERR *p_err)
{
CPU_INT08U cmd[6];
CPU_INT32U xfer_len;
/* See Note(s) #1 */
/* ------------ PREPARE SCSI COMMAND BLOCK ------------ */
cmd[0] = USBH_SCSI_CMD_REQUEST_SENSE; /* Operation code (0x03). */
cmd[1] = 0u; /* Reserved. */
cmd[2] = 0u; /* Reserved. */
cmd[3] = 0u; /* Reserved. */
cmd[4] = (CPU_INT08U)data_len; /* Allocation length. */
cmd[5] = 0u; /* Ctrl. */
/* ------------------ SEND SCSI CMD ------------------- */
xfer_len = USBH_MSC_XferCmd( p_msc_dev,
lun,
USBH_MSC_DATA_DIR_IN,
(void *)cmd,
6u,
(void *)p_arg,
data_len,
p_err);
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_SCSI_GetSenseInfo()
*
* Description : Obtain sense data.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical unit number.
*
* p_sense_key Pointer to variable that will receive sense key.
*
* p_asc Pointer to variable that will receive additional sense code.
*
* p_ascq Pointer to variable that will receive additional sense code qualifier.
*
* Return(s) : USBH_ERR_NONE, if command is successful.
* USBH_ERR_UNKNOWN, if unknown error occured.
*
* ---- RETURNED BY USBH_SCSI_CMD_ReqSense ----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Note(s) : (1) The SCSI "REQUEST SENSE" command is documented in 'SCSI Primary Commands - 3
* (SPC-3)', Section 6.27.
*
* (2) The format of the sense data is documented in 'SCSI Primary Commands - 3 (SPC-3)',
* Section 4.5.3. :
* (a) The response code in the 0th byte of the sense data should be either 0x70 or 0x71.
* (b) The lower nibble of the 2nd byte of the sense data is the sense key.
* (c) The 12th byte of the sense data is the additional sense code (ASC).
* (d) The 13th byte of the sense data is the additional sense code (ASC) qualifier.
*********************************************************************************************************
*/
static USBH_ERR USBH_SCSI_GetSenseInfo (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT08U *p_sense_key,
CPU_INT08U *p_asc,
CPU_INT08U *p_ascq)
{
USBH_ERR err;
CPU_INT32U xfer_len;
ALLOC_CACHE_ALIGN_BUFFER(CPU_INT08U, sense_data, 18);
CPU_INT08U resp_code;
xfer_len = USBH_SCSI_CMD_ReqSense(p_msc_dev, /* Issue SCSI request sense cmd. */
lun,
sense_data,
18u,
&err);
if (err == USBH_ERR_NONE) {
resp_code = sense_data[0] & 0x7Fu;
if ( (xfer_len >= 13u) &&
((resp_code == 0x70u) || (resp_code == 0x71u))) {
*p_sense_key = sense_data[2] & 0x0Fu;
*p_asc = sense_data[12];
*p_ascq = sense_data[13];
err = USBH_ERR_NONE;
} else {
*p_sense_key = 0u;
*p_asc = 0u;
*p_ascq = 0u;
err = USBH_ERR_UNKNOWN;
#if (USBH_CFG_PRINT_LOG == DEF_ENABLED)
USBH_PRINT_LOG("ERROR Invalid SENSE response from device - ");
USBH_PRINT_LOG("xfer_len : %d, sense_data[0] : %02X\r\n", xfer_len, sense_data[0]);
#endif
}
} else {
*p_sense_key = 0u;
*p_asc = 0u;
*p_ascq = 0u;
USBH_PRINT_ERR(err);
}
return (err);
}
/*
*********************************************************************************************************
* USBH_SCSI_CMD_CapacityRd()
*
* Description : Read number of sectors & sector size.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical unit number.
*
* p_nbr_blks Pointer to variable that will receive number of blocks.
*
* p_blk_size Pointer to variable that will receive block size.
*
* Return(s) : USBH_ERR_NONE, if command is successful.
*
* ---- RETURNED BY USBH_MSC_XferCmd ----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Note(s) : (1) The SCSI "READ CAPAPITY (10)" command is documented in 'SCSI Block Commands - 2
* (SBC-2)', Section 5.10.
*********************************************************************************************************
*/
static USBH_ERR USBH_SCSI_CMD_CapacityRd (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U *p_nbr_blks,
CPU_INT32U *p_blk_size)
{
CPU_INT08U cmd[10];
ALLOC_CACHE_ALIGN_BUFFER(CPU_INT08U, cap_data, 8);
USBH_ERR err;
/* See Note #1. */
/* -------------- PREPARE SCSI CMD BLOCK -------------- */
cmd[0] = USBH_SCSI_CMD_READ_CAPACITY; /* Operation code (0x25). */
cmd[1] = 0u; /* Reserved. */
cmd[2] = 0u; /* Logical Block Address (MSB). */
cmd[3] = 0u; /* Logical Block Address. */
cmd[4] = 0u; /* Logical Block Address. */
cmd[5] = 0u; /* Logical Block Address (LSB). */
cmd[6] = 0u; /* Reserved. */
cmd[7] = 0u; /* Reserved. */
cmd[8] = 0u; /* ----------------------- PMI. ----------------------- */
cmd[9] = 0u; /* Control. */
USBH_MSC_XferCmd ( p_msc_dev, /* ------------------ SEND SCSI CMD ------------------- */
lun,
USBH_MSC_DATA_DIR_IN,
(void *)cmd,
10u,
(void *)cap_data,
8u,
&err);
if (err == USBH_ERR_NONE) { /* -------------- HANDLE DEVICE RESPONSE -------------- */
MEM_VAL_COPY_GET_INT32U_BIG(p_nbr_blks, &cap_data[0]);
*p_nbr_blks += 1u;
MEM_VAL_COPY_GET_INT32U_BIG(p_blk_size, &cap_data[4]);
}
return (err);
}
/*
*********************************************************************************************************
* USBH_SCSI_Rd()
*
* Description : Read specified number of blocks from device.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical unit number.
*
* blk_addr Block address.
*
* nbr_blks Number of blocks to read.
*
* blk_size Block size.
*
* p_arg Pointer to data buffer.
*
* p_err Pointer to variable that will receive the return error code from this function :
* USBH_ERR_NONE Data successfully received.
*
* ---- RETURNED BY USBH_MSC_XferCmd ----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Return(s) : Number of bytes read.
*
* Note(s) : (1) The SCSI "READ (10): command is documented in 'SCSI Block Commands - 2 (SBC-2)',
* Section 5.6.
*********************************************************************************************************
*/
static CPU_INT32U USBH_SCSI_Rd (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U blk_addr,
CPU_INT16U nbr_blks,
CPU_INT32U blk_size,
void *p_arg,
USBH_ERR *p_err)
{
CPU_INT08U cmd[10];
CPU_INT32U data_len;
CPU_INT32U xfer_len;
data_len = nbr_blks * blk_size;
/* See Note #1. */
/* -------------- PREPARE SCSI CMD BLOCK -------------- */
cmd[0] = USBH_SCSI_CMD_READ_10; /* Operation code (0x28). */
cmd[1] = 0u; /* Reserved. */
MEM_VAL_COPY_SET_INT32U_BIG(&cmd[2], &blk_addr); /* Logcal Block Address (LBA). */
cmd[6] = 0u; /* Reserved. */
MEM_VAL_COPY_SET_INT16U_BIG(&cmd[7], &nbr_blks); /* Transfer length (number of logical blocks). */
cmd[9] = 0u; /* Control. */
xfer_len = USBH_MSC_XferCmd( p_msc_dev, /* ---------------- SEND SCSI COMMAND ----------------- */
lun,
USBH_MSC_DATA_DIR_IN,
(void *)&cmd[0],
10u,
p_arg,
data_len,
p_err);
if (*p_err != USBH_ERR_NONE) {
xfer_len = (CPU_INT32U)0;
}
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_SCSI_Wr()
*
* Description : Write specified number of blocks to device.
*
* Argument(s) : p_msc_dev Pointer to MSC device.
*
* lun Logical unit number.
*
* blk_addr Block address.
*
* nbr_blks Number of blocks to write.
*
* blk_size Block size.
*
* p_arg Pointer to data buffer.
*
* p_err Pointer to variable that will receive the return error code from this function :
* USBH_ERR_NONE Data successfully transmitted.
*
* ---- RETURNED BY USBH_MSC_XferCmd ----
* USBH_ERR_MSC_CMD_FAILED Device reports command failed.
* USBH_ERR_MSC_CMD_PHASE Device reports command phase error.
* USBH_ERR_MSC_IO Unable to receive CSW.
* USBH_ERR_INVALID_ARG Invalid argument passed to 'p_ep'.
* USBH_ERR_EP_INVALID_TYPE Endpoint type or direction is incorrect.
* USBH_ERR_EP_INVALID_STATE Endpoint is not opened.
* Host controller drivers error code, Otherwise.
*
* Return(s) : Number of octets written.
*
* Note(s) : (1) The SCSI "WRITE (10)" command is documented in 'SCSI Block Commands - 2 (SBC-2)',
* Section 5.25.
*********************************************************************************************************
*/
static CPU_INT32U USBH_SCSI_Wr (USBH_MSC_DEV *p_msc_dev,
CPU_INT08U lun,
CPU_INT32U blk_addr,
CPU_INT16U nbr_blks,
CPU_INT32U blk_size,
const void *p_arg,
USBH_ERR *p_err)
{
CPU_INT08U cmd[10];
CPU_INT32U data_len;
CPU_INT32U xfer_len;
data_len = nbr_blks * blk_size;
/* See Note #1. */
/* -------------- PREPARE SCSI CMD BLOCK -------------- */
cmd[0] = USBH_SCSI_CMD_WRITE_10; /* Operation code (0x2A). */
cmd[1] = 0u; /* Reserved. */
MEM_VAL_COPY_SET_INT32U_BIG(&cmd[2], &blk_addr); /* Logical Block Address (LBA). */
cmd[6] = 0u; /* Reserved. */
MEM_VAL_COPY_SET_INT16U_BIG(&cmd[7], &nbr_blks); /* Transfer length (number of logical blocks). */
cmd[9] = 0u; /* Control. */
xfer_len = USBH_MSC_XferCmd( p_msc_dev, /* ------------------ SEND SCSI CMD ------------------- */
lun,
USBH_MSC_DATA_DIR_OUT,
(void *)&cmd[0],
10u,
(void *) p_arg,
data_len,
p_err);
if (*p_err != USBH_ERR_NONE) {
xfer_len = 0u;
}
return (xfer_len);
}
/*
*********************************************************************************************************
* USBH_MSC_FmtCBW()
*
* Description : Format CBW from CBW structure.
*
* Argument(s) : p_cbw Variable that contains CBW information.
*
* p_buf_dest Pointer to buffer that will receive CBW content.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_MSC_FmtCBW (USBH_MSC_CBW *p_cbw,
void *p_buf_dest)
{
CPU_INT08U i;
USBH_MSC_CBW *p_buf_dest_cbw;
p_buf_dest_cbw = (USBH_MSC_CBW *)p_buf_dest;
p_buf_dest_cbw->dCBWSignature = MEM_VAL_GET_INT32U_LITTLE(&p_cbw->dCBWSignature);
p_buf_dest_cbw->dCBWTag = MEM_VAL_GET_INT32U_LITTLE(&p_cbw->dCBWTag);
p_buf_dest_cbw->dCBWDataTransferLength = MEM_VAL_GET_INT32U_LITTLE(&p_cbw->dCBWDataTransferLength);
p_buf_dest_cbw->bmCBWFlags = p_cbw->bmCBWFlags;
p_buf_dest_cbw->bCBWLUN = p_cbw->bCBWLUN;
p_buf_dest_cbw->bCBWCBLength = p_cbw->bCBWCBLength;
for (i = 0u; i < 16u; i++) {
p_buf_dest_cbw->CBWCB[i] = p_cbw->CBWCB[i];
}
}
/*
*********************************************************************************************************
* USBH_MSC_ParseCSW()
*
* Description : Parse CSW into CSW structure.
*
* Argument(s) : p_csw Variable that will receive CSW parsed in this function.
*
* p_buf_src Pointer to buffer that contains CSW.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBH_MSC_ParseCSW (USBH_MSC_CSW *p_csw,
void *p_buf_src)
{
USBH_MSC_CSW *p_buf_src_cbw;
p_buf_src_cbw = (USBH_MSC_CSW *)p_buf_src;
p_csw->dCSWSignature = MEM_VAL_GET_INT32U_LITTLE(&p_buf_src_cbw->dCSWSignature);
p_csw->dCSWTag = MEM_VAL_GET_INT32U_LITTLE(&p_buf_src_cbw->dCSWTag);
p_csw->dCSWDataResidue = MEM_VAL_GET_INT32U_LITTLE(&p_buf_src_cbw->dCSWDataResidue);
p_csw->bCSWStatus = p_buf_src_cbw->bCSWStatus;
}