rename subfolder source to src

This commit is contained in:
hathach
2018-06-18 14:05:24 +07:00
parent b96e8c6cef
commit 424735d440
70 changed files with 0 additions and 0 deletions

376
src/class/msc/msc.h Normal file
View File

@@ -0,0 +1,376 @@
/**************************************************************************/
/*!
@file msc.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
/** \ingroup group_class
* \defgroup ClassDriver_MSC MassStorage (MSC)
* @{ */
/** \defgroup ClassDriver_MSC_Common Common Definitions
* @{ */
#ifndef _TUSB_MSC_H_
#define _TUSB_MSC_H_
#include <common/tusb_common.h>
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Mass Storage Class Constant
//--------------------------------------------------------------------+
/// MassStorage Subclass
typedef enum
{
MSC_SUBCLASS_RBC = 1 , ///< Reduced Block Commands (RBC) T10 Project 1240-D
MSC_SUBCLASS_SFF_MMC , ///< SFF-8020i, MMC-2 (ATAPI). Typically used by a CD/DVD device
MSC_SUBCLASS_QIC , ///< QIC-157. Typically used by a tape device
MSC_SUBCLASS_UFI , ///< UFI. Typically used by Floppy Disk Drive (FDD) device
MSC_SUBCLASS_SFF , ///< SFF-8070i. Can be used by Floppy Disk Drive (FDD) device
MSC_SUBCLASS_SCSI ///< SCSI transparent command set
}msc_subclass_type_t;
enum {
MSC_CBW_SIGNATURE = 0x43425355, ///< Constant value of 43425355h (little endian)
MSC_CSW_SIGNATURE = 0x53425355 ///< Constant value of 53425355h (little endian)
};
/// \brief MassStorage Protocol.
/// \details CBI only approved to use with full-speed floopy disk & should not used with highspeed or device other than floopy
typedef enum
{
MSC_PROTOCOL_CBI = 0 , ///< Control/Bulk/Interrupt protocol (with command completion interrupt)
MSC_PROTOCOL_CBI_NO_INTERRUPT = 1 , ///< Control/Bulk/Interrupt protocol (without command completion interrupt)
MSC_PROTOCOL_BOT = 0x50 ///< Bulk-Only Transport
}msc_protocol_type_t;
/// MassStorage Class-Specific Control Request
typedef enum
{
MSC_REQUEST_GET_MAX_LUN = 254, ///< The Get Max LUN device request is used to determine the number of logical units supported by the device. Logical Unit Numbers on the device shall be numbered contiguously starting from LUN 0 to a maximum LUN of 15
MSC_REQUEST_RESET = 255 ///< This request is used to reset the mass storage device and its associated interface. This class-specific request shall ready the device for the next CBW from the host.
}msc_request_type_t;
/// \brief Command Block Status Values
/// \details Indicates the success or failure of the command. The device shall set this byte to zero if the command completed
/// successfully. A non-zero value shall indicate a failure during command execution according to the following
typedef enum
{
MSC_CSW_STATUS_PASSED = 0 , ///< MSC_CSW_STATUS_PASSED
MSC_CSW_STATUS_FAILED , ///< MSC_CSW_STATUS_FAILED
MSC_CSW_STATUS_PHASE_ERROR ///< MSC_CSW_STATUS_PHASE_ERROR
}msc_csw_status_t;
/// Command Block Wrapper
typedef struct ATTR_PACKED
{
uint32_t signature ; ///< Signature that helps identify this data packet as a CBW. The signature field shall contain the value 43425355h (little endian), indicating a CBW.
uint32_t tag ; ///< Tag sent by the host. The device shall echo the contents of this field back to the host in the dCSWTagfield of the associated CSW. The dCSWTagpositively associates a CSW with the corresponding CBW.
uint32_t xfer_bytes ; ///< The number of bytes of data that the host expects to transfer on the Bulk-In or Bulk-Out endpoint (as indicated by the Direction bit) during the execution of this command. If this field is zero, the device and the host shall transfer no data between the CBW and the associated CSW, and the device shall ignore the value of the Direction bit in bmCBWFlags.
uint8_t dir ; ///< Bit 7 of this field define transfer direction \n - 0 : Data-Out from host to the device. \n - 1 : Data-In from the device to the host.
uint8_t lun ; ///< The device Logical Unit Number (LUN) to which the command block is being sent. For devices that support multiple LUNs, the host shall place into this field the LUN to which this command block is addressed. Otherwise, the host shall set this field to zero.
uint8_t cmd_len ; ///< The valid length of the CBWCBin bytes. This defines the valid length of the command block. The only legal values are 1 through 16
uint8_t command[16] ; ///< The command block to be executed by the device. The device shall interpret the first cmd_len bytes in this field as a command block
}msc_cbw_t;
VERIFY_STATIC(sizeof(msc_cbw_t) == 31, "size is not correct");
/// Command Status Wrapper
typedef struct ATTR_PACKED
{
uint32_t signature ; ///< Signature that helps identify this data packet as a CSW. The signature field shall contain the value 53425355h (little endian), indicating CSW.
uint32_t tag ; ///< The device shall set this field to the value received in the dCBWTag of the associated CBW.
uint32_t data_residue ; ///< For Data-Out the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLength, and the actual amount of data processed by the device. For Data-In the device shall report in the dCSWDataResiduethe difference between the amount of data expected as stated in the dCBWDataTransferLengthand the actual amount of relevant data sent by the device
uint8_t status ; ///< indicates the success or failure of the command. Values from \ref msc_csw_status_t
}msc_csw_t;
VERIFY_STATIC(sizeof(msc_csw_t) == 13, "size is not correct");
//--------------------------------------------------------------------+
// SCSI Constant
//--------------------------------------------------------------------+
/// SCSI Command Operation Code
typedef enum
{
SCSI_CMD_TEST_UNIT_READY = 0x00, ///< The SCSI Test Unit Ready command is used to determine if a device is ready to transfer data (read/write), i.e. if a disk has spun up, if a tape is loaded and ready etc. The device does not perform a self-test operation.
SCSI_CMD_INQUIRY = 0x12, ///< The SCSI Inquiry command is used to obtain basic information from a target device.
SCSI_CMD_MODE_SELECT_6 = 0x15, ///< provides a means for the application client to specify medium, logical unit, or peripheral device parameters to the device server. Device servers that implement the MODE SELECT(6) command shall also implement the MODE SENSE(6) command. Application clients should issue MODE SENSE(6) prior to each MODE SELECT(6) to determine supported mode pages, page lengths, and other parameters.
SCSI_CMD_MODE_SENSE_6 = 0x1A, ///< provides a means for a device server to report parameters to an application client. It is a complementary command to the MODE SELECT(6) command. Device servers that implement the MODE SENSE(6) command shall also implement the MODE SELECT(6) command.
SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1E,
SCSI_CMD_READ_CAPACITY_10 = 0x25, ///< The SCSI Read Capacity command is used to obtain data capacity information from a target device.
SCSI_CMD_REQUEST_SENSE = 0x03, ///< The SCSI Request Sense command is part of the SCSI computer protocol standard. This command is used to obtain sense data -- status/error information -- from a target device.
SCSI_CMD_READ_FORMAT_CAPACITY = 0x23, ///< The command allows the Host to request a list of the possible format capacities for an installed writable media. This command also has the capability to report the writable capacity for a media when it is installed
SCSI_CMD_READ_10 = 0x28, ///< The READ (10) command requests that the device server read the specified logical block(s) and transfer them to the data-in buffer.
SCSI_CMD_WRITE_10 = 0x2A, ///< The WRITE (10) command requests thatthe device server transfer the specified logical block(s) from the data-out buffer and write them.
}scsi_cmd_type_t;
/// SCSI Sense Key
typedef enum
{
SCSI_SENSEKEY_NONE = 0x00, ///< no specific Sense Key. This would be the case for a successful command
SCSI_SENSEKEY_RECOVERED_ERROR = 0x01, ///< ndicates the last command completed successfully with some recovery action performed by the disc drive.
SCSI_SENSEKEY_NOT_READY = 0x02, ///< Indicates the logical unit addressed cannot be accessed.
SCSI_SENSEKEY_MEDIUM_ERROR = 0x03, ///< Indicates the command terminated with a non-recovered error condition.
SCSI_SENSEKEY_HARDWARE_ERROR = 0x04, ///< Indicates the disc drive detected a nonrecoverable hardware failure while performing the command or during a self test.
SCSI_SENSEKEY_ILLEGLA_REQUEST = 0x05, ///< Indicates an illegal parameter in the command descriptor block or in the additional parameters
SCSI_SENSEKEY_UNIT_ATTENTION = 0x06, ///< Indicates the disc drive may have been reset.
SCSI_SENSEKEY_DATA_PROTECT = 0x07, ///< Indicates that a command that reads or writes the medium was attempted on a block that is protected from this operation. The read or write operation is not performed.
SCSI_SENSEKEY_FIRMWARE_ERROR = 0x08, ///< Vendor specific sense key.
SCSI_SENSEKEY_ABORTED_COMMAND = 0x0b, ///< Indicates the disc drive aborted the command.
SCSI_SENSEKEY_EQUAL = 0x0c, ///< Indicates a SEARCH DATA command has satisfied an equal comparison.
SCSI_SENSEKEY_VOLUME_OVERFLOW = 0x0d, ///< Indicates a buffered peripheral device has reached the end of medium partition and data remains in the buffer that has not been written to the medium.
SCSI_SENSEKEY_MISCOMPARE = 0x0e ///< ndicates that the source data did not match the data read from the medium.
}scsi_sense_key_type_t;
//--------------------------------------------------------------------+
// SCSI Primary Command (SPC-4)
//--------------------------------------------------------------------+
/// SCSI Test Unit Ready Command
typedef struct ATTR_PACKED
{
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY
uint8_t lun ; ///< Logical Unit
uint8_t reserved[3] ;
uint8_t control ;
} scsi_test_unit_ready_t;
VERIFY_STATIC(sizeof(scsi_test_unit_ready_t) == 6, "size is not correct");
/// SCSI Inquiry Command
typedef struct ATTR_PACKED
{
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY
uint8_t reserved1 ;
uint8_t page_code ;
uint8_t reserved2 ;
uint8_t alloc_length ; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred.
uint8_t control ;
} scsi_inquiry_t, scsi_request_sense_t;
VERIFY_STATIC(sizeof(scsi_inquiry_t) == 6, "size is not correct");
/// SCSI Inquiry Response Data
typedef struct ATTR_PACKED
{
uint8_t peripheral_device_type : 5;
uint8_t peripheral_qualifier : 3;
uint8_t : 7;
uint8_t is_removable : 1;
uint8_t version;
uint8_t response_data_format : 4;
uint8_t hierarchical_support : 1;
uint8_t normal_aca : 1;
uint8_t : 2;
uint8_t additional_length;
uint8_t protect : 1;
uint8_t : 2;
uint8_t third_party_copy : 1;
uint8_t target_port_group_support : 2;
uint8_t access_control_coordinator : 1;
uint8_t scc_support : 1;
uint8_t addr16 : 1;
uint8_t : 3;
uint8_t multi_port : 1;
uint8_t : 1; // vendor specific
uint8_t enclosure_service : 1;
uint8_t : 1;
uint8_t : 1; // vendor specific
uint8_t cmd_que : 1;
uint8_t : 2;
uint8_t sync : 1;
uint8_t wbus16 : 1;
uint8_t : 2;
uint8_t vendor_id[8] ; ///< 8 bytes of ASCII data identifying the vendor of the product. The T10 vendor identification shall be one assigned by INCITS. A list of assigned T10 vendor identifications is in Annex E and on the T10 web site (http://www.t10.org).
uint8_t product_id[16] ; ///< 16 bytes of ASCII data defined by the vendor.
uint8_t product_revision[4] ; ///< 4 bytes of ASCII data defined by the vendor.
} scsi_inquiry_data_t;
VERIFY_STATIC(sizeof(scsi_inquiry_data_t) == 36, "size is not correct");
typedef struct ATTR_PACKED
{
uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format
uint8_t valid : 1;
uint8_t reserved; ///< Obsolete
uint8_t sense_key : 4;
uint8_t : 1;
uint8_t incorrect_len_idicatior : 1;
uint8_t end_of_medium : 1;
uint8_t filemark : 1;
uint32_t information;
uint8_t additional_sense_len;
uint32_t command_specific_info;
uint8_t additional_sense_code;
uint8_t additional_sense_qualifier;
uint8_t field_replaceable_unit_code;
uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout
} scsi_sense_fixed_data_t;
VERIFY_STATIC(sizeof(scsi_sense_fixed_data_t) == 18, "size is not correct");
typedef struct ATTR_PACKED
{
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6
uint8_t : 3;
uint8_t disable_block_descriptor : 1;
uint8_t : 0;
uint8_t page_code : 6;
uint8_t page_control : 2;
uint8_t subpage_code;
uint8_t alloc_length;
uint8_t control;
} scsi_mode_sense_6_t;
VERIFY_STATIC( sizeof(scsi_mode_sense_6_t) == 6, "size is not correct");
typedef struct ATTR_PACKED
{
uint8_t mode_data_length;
uint8_t medium_type;
uint8_t device_specific_para;
uint8_t block_descriptor_length;
} scsi_mode_parameters_t;
VERIFY_STATIC( sizeof(scsi_mode_parameters_t) == 4, "size is not correct");
typedef struct ATTR_PACKED
{
uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
uint8_t reserved[3];
uint8_t prohibit_removal;
uint8_t control;
} scsi_prevent_allow_medium_removal_t;
VERIFY_STATIC( sizeof(scsi_prevent_allow_medium_removal_t) == 6, "size is not correct");
//--------------------------------------------------------------------+
// SCSI MMC
//--------------------------------------------------------------------+
/// SCSI Read Format Capacity: Write Capacity
typedef struct ATTR_PACKED
{
uint8_t cmd_code;
uint8_t reserved[6];
uint16_t alloc_length;
uint8_t control;
} scsi_read_format_capacity_t;
VERIFY_STATIC( sizeof(scsi_read_format_capacity_t) == 10, "size is not correct");
typedef struct ATTR_PACKED{
uint8_t reserved[3];
uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it.
uint32_t block_num; /// Number of Logical Blocks
uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present
uint8_t reserved2;
uint16_t block_size_u16;
} scsi_read_format_capacity_data_t;
VERIFY_STATIC( sizeof(scsi_read_format_capacity_data_t) == 12, "size is not correct");
//--------------------------------------------------------------------+
// SCSI Block Command (SBC-3)
// NOTE: All data in SCSI command are in Big Endian
//--------------------------------------------------------------------+
/// SCSI Read Capacity 10 Command: Read Capacity
typedef struct ATTR_PACKED
{
uint8_t cmd_code ; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10
uint8_t reserved1 ;
uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
uint16_t reserved2 ;
uint8_t partial_medium_indicator ;
uint8_t control ;
} scsi_read_capacity10_t;
VERIFY_STATIC(sizeof(scsi_read_capacity10_t) == 10, "size is not correct");
/// SCSI Read Capacity 10 Response Data
typedef struct {
uint32_t last_lba ; ///< The last Logical Block Address of the device
uint32_t block_size ; ///< Block size in bytes
} scsi_read_capacity10_data_t;
VERIFY_STATIC(sizeof(scsi_read_capacity10_data_t) == 8, "size is not correct");
/// SCSI Read 10 Command
typedef struct ATTR_PACKED
{
uint8_t cmd_code ; ///< SCSI OpCode
uint8_t reserved ; // has LUN according to wiki
uint32_t lba ; ///< The first Logical Block Address (LBA) accessed by this command
uint8_t reserved2 ;
uint16_t block_count ; ///< Number of Blocks used by this command
uint8_t control ;
} scsi_read10_t, scsi_write10_t;
VERIFY_STATIC(sizeof(scsi_read10_t) == 10, "size is not correct");
VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_MSC_H_ */
/// @}
/// @}

396
src/class/msc/msc_device.c Normal file
View File

@@ -0,0 +1,396 @@
/**************************************************************************/
/*!
@file msc_device.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if (MODE_DEVICE_SUPPORTED && CFG_TUD_MSC)
#define _TINY_USB_SOURCE_FILE_
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "msc_device.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
enum
{
MSC_STAGE_CMD = 0,
MSC_STAGE_DATA,
MSC_STAGE_STATUS
};
typedef struct {
CFG_TUSB_MEM_ALIGN msc_cbw_t cbw;
#if defined (__ICCARM__) && (CFG_TUSB_MCU == OPT_MCU_LPC11UXX || CFG_TUSB_MCU == OPT_MCU_LPC13UXX)
uint8_t padding1[64-sizeof(msc_cbw_t)]; // IAR cannot align struct's member
#endif
CFG_TUSB_MEM_ALIGN msc_csw_t csw;
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
uint8_t stage;
uint16_t data_len;
uint16_t xferred_len; // numbered of bytes transferred so far in the Data Stage
}mscd_interface_t;
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE];
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc);
static inline uint32_t rdwr10_get_lba(uint8_t const command[])
{
// read10 & write10 has the same format
scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command;
// copy first to prevent mis-aligned access
uint32_t lba;
memcpy(&lba, &p_rdwr10->lba, 4);
return __be2n(lba);
}
static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
{
// read10 & write10 has the same format
scsi_write10_t* p_rdwr10 = (scsi_write10_t*) command;
// copy first to prevent mis-aligned access
uint16_t block_count;
memcpy(&block_count, &p_rdwr10->block_count, 2);
return __be2n(block_count);
}
//--------------------------------------------------------------------+
// USBD-CLASS API
//--------------------------------------------------------------------+
void mscd_init(void)
{
memclr_(&_mscd_itf, sizeof(mscd_interface_t));
}
void mscd_close(uint8_t rhport)
{
memclr_(&_mscd_itf, sizeof(mscd_interface_t));
}
tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf, uint16_t *p_len)
{
// only support SCSI's BOT protocol
VERIFY( ( MSC_SUBCLASS_SCSI == p_desc_itf->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == p_desc_itf->bInterfaceProtocol ), TUSB_ERROR_MSC_UNSUPPORTED_PROTOCOL );
mscd_interface_t * p_msc = &_mscd_itf;
// Open endpoint pair with usbd helper
tusb_desc_endpoint_t const *p_desc_ep = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_desc_itf );
TU_ASSERT_ERR( usbd_open_edpt_pair(rhport, p_desc_ep, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
p_msc->itf_num = p_desc_itf->bInterfaceNumber;
(*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
//------------- Queue Endpoint OUT for Command Block Wrapper -------------//
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_DCD_EDPT_XFER );
return TUSB_ERROR_NONE;
}
tusb_error_t mscd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
{
OSAL_SUBTASK_BEGIN
TU_ASSERT(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS, TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT);
if(MSC_REQUEST_RESET == p_request->bRequest)
{
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
}
else if (MSC_REQUEST_GET_MAX_LUN == p_request->bRequest)
{
// returned MAX LUN is minus 1 by specs
_mscd_buf[0] = CFG_TUD_MSC_MAXLUN-1;
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _mscd_buf, 1);
}else
{
dcd_control_stall(rhport); // stall unsupported request
}
OSAL_SUBTASK_END
}
tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, tusb_event_t event, uint32_t xferred_bytes)
{
mscd_interface_t* p_msc = &_mscd_itf;
msc_cbw_t const * p_cbw = &p_msc->cbw;
msc_csw_t * p_csw = &p_msc->csw;
VERIFY( (ep_addr == p_msc->ep_out) || (ep_addr == p_msc->ep_in), TUSB_ERROR_INVALID_PARA);
switch (p_msc->stage)
{
case MSC_STAGE_CMD:
//------------- new CBW received -------------//
// Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it
if(ep_addr != p_msc->ep_out) return TUSB_ERROR_NONE;
TU_ASSERT( event == TUSB_EVENT_XFER_COMPLETE &&
xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE, TUSB_ERROR_INVALID_PARA );
p_csw->signature = MSC_CSW_SIGNATURE;
p_csw->tag = p_cbw->tag;
p_csw->data_residue = 0;
/*------------- Parse command and prepare DATA -------------*/
p_msc->stage = MSC_STAGE_DATA;
p_msc->data_len = p_cbw->xfer_bytes;
p_msc->xferred_len = 0;
if (SCSI_CMD_READ_10 == p_cbw->command[0])
{
proc_read10_cmd(rhport, p_msc);
}
else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
{
proc_write10_cmd(rhport, p_msc);
}
else
{
// For other SCSI commands
// 1. Zero : Invoke app callback, skip DATA and move to STATUS stage
// 2. OUT : queue transfer (invoke app callback after done)
// 3. IN : invoke app callback to get response
if ( p_cbw->xfer_bytes == 0)
{
p_msc->data_len = tud_msc_scsi_cb(rhport, p_cbw->lun, p_cbw->command, NULL, 0);
p_csw->status = (p_msc->data_len == 0) ? MSC_CSW_STATUS_PASSED : MSC_CSW_STATUS_FAILED;
p_msc->stage = MSC_STAGE_STATUS;
TU_ASSERT( p_msc->data_len == 0, TUSB_ERROR_INVALID_PARA);
}
else if ( !BIT_TEST_(p_cbw->dir, 7) )
{
// OUT transfer
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->data_len), TUSB_ERROR_DCD_EDPT_XFER );
}
else
{
p_msc->data_len = tud_msc_scsi_cb(rhport, p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->data_len);
p_csw->status = (p_msc->data_len >= 0) ? MSC_CSW_STATUS_PASSED : MSC_CSW_STATUS_FAILED;
TU_ASSERT( p_cbw->xfer_bytes >= p_msc->data_len, TUSB_ERROR_INVALID_PARA ); // cannot return more than host expect
if ( p_msc->data_len )
{
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->data_len), TUSB_ERROR_DCD_EDPT_XFER );
}else
{
// application does not provide data to response --> possibly unsupported SCSI command
dcd_edpt_stall(rhport, p_msc->ep_in);
p_csw->status = MSC_CSW_STATUS_FAILED;
p_msc->stage = MSC_STAGE_STATUS;
}
}
}
break;
case MSC_STAGE_DATA:
// OUT transfer, invoke callback if needed
if ( !BIT_TEST_(p_cbw->dir, 7) )
{
if ( SCSI_CMD_WRITE_10 != p_cbw->command[0] )
{
p_csw->status = (tud_msc_scsi_cb(rhport, p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->data_len) >= 0 ) ? MSC_CSW_STATUS_PASSED : MSC_CSW_STATUS_FAILED;
}
else
{
uint32_t lba = rdwr10_get_lba(p_cbw->command);
// Application can consume smaller bytes
int32_t nbytes = tud_msc_write10_cb(rhport, p_cbw->lun, lba, p_msc->xferred_len, _mscd_buf, xferred_bytes);
if ( nbytes < 0 )
{
// negative means error -> skip to status phase, status in CSW set to failed
p_csw->data_residue = p_cbw->xfer_bytes - p_msc->xferred_len;
p_csw->status = MSC_CSW_STATUS_FAILED;
p_msc->stage = MSC_STAGE_STATUS;
break;
}else
{
// Application consume less than what we got (including zero)
if ( nbytes < xferred_bytes )
{
if ( nbytes > 0 )
{
p_msc->xferred_len += nbytes;
memmove(_mscd_buf, _mscd_buf+nbytes, xferred_bytes-nbytes);
}
// simulate an transfer complete with adjusted params
dcd_xfer_complete(rhport, p_msc->ep_out, xferred_bytes-nbytes, true);
return TUSB_ERROR_NONE; // skip the rest
}
else
{
// Application consume all bytes in our buffer
// Nothing to do, process with normal flow
}
}
}
}
// Accumulate data so far
p_msc->xferred_len += xferred_bytes;
if ( p_msc->xferred_len >= p_msc->data_len )
{
// Data Stage is complete
p_msc->stage = MSC_STAGE_STATUS;
}
else
{
// READ10 & WRITE10 Can be executed with large bulk of data e.g write 8K bytes (several flash write)
// We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_BUFSIZE
if (SCSI_CMD_READ_10 == p_cbw->command[0])
{
proc_read10_cmd(rhport, p_msc);
}
else if (SCSI_CMD_WRITE_10 == p_cbw->command[0])
{
proc_write10_cmd(rhport, p_msc);
}else
{
// No other command take more than one transfer yet -> unlikely error
verify_breakpoint();
}
}
break;
case MSC_STAGE_STATUS: break; // processed immediately after this switch
default : break;
}
if ( p_msc->stage == MSC_STAGE_STATUS )
{
// Invoke Complete Callback if defined
if ( SCSI_CMD_READ_10 == p_cbw->command[0])
{
if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(rhport, p_cbw->lun);
}else if ( SCSI_CMD_WRITE_10 == p_cbw->command[0] )
{
if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(rhport, p_cbw->lun);
}else
{
if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(rhport, p_cbw->lun, p_cbw->command);
}
// Move to default CMD stage after sending status
p_msc->stage = MSC_STAGE_CMD;
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in , (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)) );
//------------- Queue the next CBW -------------//
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
}
return TUSB_ERROR_NONE;
}
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;
msc_csw_t * p_csw = &p_msc->csw;
uint32_t lba = rdwr10_get_lba(p_cbw->command);
// remaining bytes capped at class buffer
int32_t nbytes = (int32_t) min32_of(sizeof(_mscd_buf), p_cbw->xfer_bytes-p_msc->xferred_len);
// Application can consume smaller bytes
nbytes = tud_msc_read10_cb (rhport, p_cbw->lun, lba, p_msc->xferred_len, _mscd_buf, (uint32_t) nbytes);
if ( nbytes < 0 )
{
// negative means error -> pipe is stalled & status in CSW set to failed
p_csw->data_residue = p_cbw->xfer_bytes - p_msc->xferred_len;
p_csw->status = MSC_CSW_STATUS_FAILED;
dcd_edpt_stall(rhport, p_msc->ep_in);
}
else if ( nbytes == 0 )
{
// zero means not ready -> try again later by simulate an transfer complete
dcd_xfer_complete(rhport, p_msc->ep_in, 0, true);
}
else
{
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, nbytes), );
}
}
static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;
// remaining bytes capped at class buffer
int32_t nbytes = (int32_t) min32_of(sizeof(_mscd_buf), p_cbw->xfer_bytes-p_msc->xferred_len);
// Write10 callback will be called later when usb transfer complete
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, nbytes), );
}
#endif

158
src/class/msc/msc_device.h Normal file
View File

@@ -0,0 +1,158 @@
/**************************************************************************/
/*!
@file msc_device.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#ifndef _TUSB_MSC_DEVICE_H_
#define _TUSB_MSC_DEVICE_H_
#include "common/tusb_common.h"
#include "device/usbd.h"
#include "msc.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \addtogroup ClassDriver_MSC
* @{
* \defgroup MSC_Device Device
* @{ */
//--------------------------------------------------------------------+
// APPLICATION API (Multiple Root Hub Ports)
// Should be used only with MCU that support more than 1 ports
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// APPLICATION CALLBACK API (WEAK is optional)
//--------------------------------------------------------------------+
/** \brief Callback invoked when received \ref SCSI_CMD_READ_10 command
* \param[in] \rhport Root hub port
* \param[in] lun Targeted Logical Unit
* Must be accessible by USB controller (see \ref CFG_TUSB_ATTR_USBRAM)
* \param[in] lba Starting Logical Block Address to be read
* \param[in] offset Byte offset from LBA for reading
* \param[out] buffer Buffer which application need to update with the response data.
* \param[in] bufsize Max bytes can be copied. Application must not return more than this value.
*
* \retval positive Actual bytes returned, must not be more than \a \b bufsize.
* If value less than bufsize is returned, Tinyusb will transfer this amount and afterwards
* invoke this callback again with adjusted offset.
*
* \retval zero Indicate application is not ready yet to response e.g disk I/O is not complete.
* Tinyusb will invoke this callback with the same params again some time later.
*
* \retval negative Indicate error reading disk I/O. Tinyusb will \b STALL the corresponding
* endpoint and return failed status in command status wrapper phase.
*
* \note Host can request dozens of Kbytes in one command e.g \a \b block_count = 128, it is insufficient to require
* application to have that much of buffer. Instead, application can return the number of blocks it can processed,
* the stack after transferred that amount of data will continue to invoke this callback with adjusted \a \b lba and \a \b block_count.
* \n\n Although this callback is called by tinyusb device task (non-isr context), however as all the classes share
* the same task (to save resource), any delay in this callback will cause delay in response on other classes.
*/
int32_t tud_msc_read10_cb (uint8_t rhport, uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
/** \brief Callback invoked when received \ref SCSI_CMD_WRITE_10 command
* \param[in] rhport Root hub port
* \param[in] lun Targeted Logical Unit
* Must be accessible by USB controller (see \ref CFG_TUSB_ATTR_USBRAM)
* \param[in] lba Starting Logical Block Address to be write
* \param[out] buffer Buffer which holds written data.
* \param[in] bufsize Max bytes in buffer. Application must not return more than this value.
*
* \retval positive Actual bytes written, must not be more than \a \b bufsize.
* If value less than bufsize is returned, Tinyusb will trim off this amount and
* invoke this callback again with adjusted offset some time later.
*
* \retval zero Indicate application is not ready yet e.g disk I/O is not complete.
* Tinyusb will invoke this callback with the same params again some time later.
*
* \retval negative Indicate error writing disk I/O. Tinyusb will \b STALL the corresponding
* endpoint and return failed status in command status wrapper phase.
*
* \note Host can request dozens of Kbytes in one command e.g \a \b block_count = 128, it is insufficient to require
* application to have that much of buffer. Instead, application can return the number of blocks it can processed,
* the stack after transferred that amount of data will continue to invoke this callback with adjusted \a \b lba and \a \b block_count.
* \n\n Although this callback is called by tinyusb device task (non-isr context), however as all the classes share
* the same task (to save resource), any delay in this callback will cause delay in response on other classes.
*/
int32_t tud_msc_write10_cb (uint8_t rhport, uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
/** \brief Callback invoked when received an SCSI command other than \ref SCSI_CMD_WRITE_10 and \ref SCSI_CMD_READ_10
* \param[in] rhport Root hub port
* \param[in] lun Targeted Logical Unit
* \param[in] scsi_cmd SCSI command contents, application should examine this command block to know which command host requested
* \param[out] buffer Buffer for SCSI Data Stage.
* - For INPUT: application must fill this with response.
* - For OUTPUT it holds the Data from host
* \param[in] bufsize Buffer's length.
*
* \retval negative Indicate error e.g accessing disk I/O. Tinyusb will \b STALL the corresponding
* endpoint and return failed status in command status wrapper phase.
* \retval otherwise Actual bytes processed, must not be more than \a \b bufsize. Can be zero for no-data command.
*/
int32_t tud_msc_scsi_cb (uint8_t rhport, uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize);
/*------------- Optional callbacks : Could be used by application to free up resources -------------*/
ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t rhport, uint8_t lun);
ATTR_WEAK void tud_msc_write10_complete_cb(uint8_t rhport, uint8_t lun);
ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t rhport, uint8_t lun, uint8_t const scsi_cmd[16]);
/** @} */
/** @} */
//--------------------------------------------------------------------+
// USBD-CLASS DRIVER API
//--------------------------------------------------------------------+
#ifdef _TINY_USB_SOURCE_FILE_
void mscd_init(void);
tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
tusb_error_t mscd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void mscd_close(uint8_t rhport);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_MSC_DEVICE_H_ */

430
src/class/msc/msc_host.c Normal file
View File

@@ -0,0 +1,430 @@
/**************************************************************************/
/*!
@file msc_host.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if MODE_HOST_SUPPORTED & CFG_TUSB_HOST_MSC
#define _TINY_USB_SOURCE_FILE_
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "msc_host.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
CFG_TUSB_ATTR_USBRAM STATIC_VAR msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX];
//------------- Initalization Data -------------//
static osal_semaphore_t msch_sem_hdl;
// buffer used to read scsi information when mounted, largest response data currently is inquiry
CFG_TUSB_ATTR_USBRAM ATTR_ALIGNED(4) STATIC_VAR uint8_t msch_buffer[sizeof(scsi_inquiry_data_t)];
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// PUBLIC API
//--------------------------------------------------------------------+
bool tuh_msc_is_mounted(uint8_t dev_addr)
{
return tuh_device_is_configured(dev_addr) && // is configured can be omitted
msch_data[dev_addr-1].is_initialized;
}
bool tuh_msc_is_busy(uint8_t dev_addr)
{
return msch_data[dev_addr-1].is_initialized &&
hcd_pipe_is_busy(msch_data[dev_addr-1].bulk_in);
}
uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr)
{
return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].vendor_id : NULL;
}
uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr)
{
return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].product_id : NULL;
}
tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size)
{
if ( !msch_data[dev_addr-1].is_initialized ) return TUSB_ERROR_MSCH_DEVICE_NOT_MOUNTED;
TU_ASSERT(p_last_lba != NULL && p_block_size != NULL, TUSB_ERROR_INVALID_PARA);
(*p_last_lba) = msch_data[dev_addr-1].last_lba;
(*p_block_size) = (uint32_t) msch_data[dev_addr-1].block_size;
return TUSB_ERROR_NONE;
}
//--------------------------------------------------------------------+
// PUBLIC API: SCSI COMMAND
//--------------------------------------------------------------------+
static inline void msc_cbw_add_signature(msc_cbw_t *p_cbw, uint8_t lun)
{
p_cbw->signature = MSC_CBW_SIGNATURE;
p_cbw->tag = 0xCAFECAFE;
p_cbw->lun = lun;
}
static tusb_error_t msch_command_xfer(msch_interface_t * p_msch, void* p_buffer) ATTR_WARN_UNUSED_RESULT;
static tusb_error_t msch_command_xfer(msch_interface_t * p_msch, void* p_buffer)
{
if ( NULL != p_buffer)
{ // there is data phase
if (p_msch->cbw.dir & TUSB_DIR_IN_MASK)
{
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false) );
TU_ASSERT_ERR( hcd_pipe_queue_xfer(p_msch->bulk_in , p_buffer, p_msch->cbw.xfer_bytes) );
}else
{
TU_ASSERT_ERR( hcd_pipe_queue_xfer(p_msch->bulk_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)) );
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_out , p_buffer, p_msch->cbw.xfer_bytes, false) );
}
}
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true) );
return TUSB_ERROR_NONE;
}
tusb_error_t tusbh_msc_inquiry(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
{
msch_interface_t* p_msch = &msch_data[dev_addr-1];
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = sizeof(scsi_inquiry_data_t);
p_msch->cbw.dir = TUSB_DIR_IN_MASK;
p_msch->cbw.cmd_len = sizeof(scsi_inquiry_t);
//------------- SCSI command -------------//
scsi_inquiry_t cmd_inquiry =
{
.cmd_code = SCSI_CMD_INQUIRY,
.alloc_length = sizeof(scsi_inquiry_data_t)
};
memcpy(p_msch->cbw.command, &cmd_inquiry, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, p_data) );
return TUSB_ERROR_NONE;
}
tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
{
msch_interface_t* p_msch = &msch_data[dev_addr-1];
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = sizeof(scsi_read_capacity10_data_t);
p_msch->cbw.dir = TUSB_DIR_IN_MASK;
p_msch->cbw.cmd_len = sizeof(scsi_read_capacity10_t);
//------------- SCSI command -------------//
scsi_read_capacity10_t cmd_read_capacity10 =
{
.cmd_code = SCSI_CMD_READ_CAPACITY_10,
.lba = 0,
.partial_medium_indicator = 0
};
memcpy(p_msch->cbw.command, &cmd_read_capacity10, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, p_data) );
return TUSB_ERROR_NONE;
}
tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
{
(void) lun; // TODO [MSCH] multiple lun support
msch_interface_t* p_msch = &msch_data[dev_addr-1];
//------------- Command Block Wrapper -------------//
p_msch->cbw.xfer_bytes = 18;
p_msch->cbw.dir = TUSB_DIR_IN_MASK;
p_msch->cbw.cmd_len = sizeof(scsi_request_sense_t);
//------------- SCSI command -------------//
scsi_request_sense_t cmd_request_sense =
{
.cmd_code = SCSI_CMD_REQUEST_SENSE,
.alloc_length = 18
};
memcpy(p_msch->cbw.command, &cmd_request_sense, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, p_data) );
return TUSB_ERROR_NONE;
}
tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw)
{
msch_interface_t* p_msch = &msch_data[dev_addr-1];
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = 0; // Number of bytes
p_msch->cbw.dir = TUSB_DIR_OUT;
p_msch->cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
//------------- SCSI command -------------//
scsi_test_unit_ready_t cmd_test_unit_ready =
{
.cmd_code = SCSI_CMD_TEST_UNIT_READY,
.lun = lun // according to wiki
};
memcpy(p_msch->cbw.command, &cmd_test_unit_ready, p_msch->cbw.cmd_len);
// TODO MSCH refractor test uinit ready
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false) );
TU_ASSERT_ERR( hcd_pipe_xfer(p_msch->bulk_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true) );
return TUSB_ERROR_NONE;
}
tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count)
{
msch_interface_t* p_msch = &msch_data[dev_addr-1];
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = p_msch->block_size*block_count; // Number of bytes
p_msch->cbw.dir = TUSB_DIR_IN_MASK;
p_msch->cbw.cmd_len = sizeof(scsi_read10_t);
//------------- SCSI command -------------//
scsi_read10_t cmd_read10 =
{
.cmd_code = SCSI_CMD_READ_10,
.lba = __n2be(lba),
.block_count = u16_le2be(block_count)
};
memcpy(p_msch->cbw.command, &cmd_read10, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, p_buffer));
return TUSB_ERROR_NONE;
}
tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count)
{
msch_interface_t* p_msch = &msch_data[dev_addr-1];
//------------- Command Block Wrapper -------------//
msc_cbw_add_signature(&p_msch->cbw, lun);
p_msch->cbw.xfer_bytes = p_msch->block_size*block_count; // Number of bytes
p_msch->cbw.dir = TUSB_DIR_OUT;
p_msch->cbw.cmd_len = sizeof(scsi_write10_t);
//------------- SCSI command -------------//
scsi_write10_t cmd_write10 =
{
.cmd_code = SCSI_CMD_WRITE_10,
.lba = __n2be(lba),
.block_count = u16_le2be(block_count)
};
memcpy(p_msch->cbw.command, &cmd_write10, p_msch->cbw.cmd_len);
TU_ASSERT_ERR ( msch_command_xfer(p_msch, (void*) p_buffer));
return TUSB_ERROR_NONE;
}
//--------------------------------------------------------------------+
// CLASS-USBH API (don't require to verify parameters)
//--------------------------------------------------------------------+
void msch_init(void)
{
memclr_(msch_data, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
msch_sem_hdl = osal_semaphore_create(1, 0);
}
tusb_error_t msch_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length)
{
tusb_error_t error;
OSAL_SUBTASK_BEGIN
if (! ( MSC_SUBCLASS_SCSI == p_interface_desc->bInterfaceSubClass &&
MSC_PROTOCOL_BOT == p_interface_desc->bInterfaceProtocol ) )
{
return TUSB_ERROR_MSC_UNSUPPORTED_PROTOCOL;
}
//------------- Open Data Pipe -------------//
tusb_desc_endpoint_t const *p_endpoint;
p_endpoint = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_interface_desc );
for(uint32_t i=0; i<2; i++)
{
STASK_ASSERT(TUSB_DESC_ENDPOINT == p_endpoint->bDescriptorType);
STASK_ASSERT(TUSB_XFER_BULK == p_endpoint->bmAttributes.xfer);
pipe_handle_t * p_pipe_hdl = ( p_endpoint->bEndpointAddress & TUSB_DIR_IN_MASK ) ?
&msch_data[dev_addr-1].bulk_in : &msch_data[dev_addr-1].bulk_out;
(*p_pipe_hdl) = hcd_pipe_open(dev_addr, p_endpoint, TUSB_CLASS_MSC);
STASK_ASSERT( pipehandle_is_valid(*p_pipe_hdl) );
p_endpoint = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_endpoint );
}
msch_data[dev_addr-1].interface_number = p_interface_desc->bInterfaceNumber;
(*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
//------------- Get Max Lun -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_IN, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
MSC_REQUEST_GET_MAX_LUN, 0, msch_data[dev_addr-1].interface_number,
1, msch_buffer ),
error
);
STASK_ASSERT( TUSB_ERROR_NONE == error /* && TODO STALL means zero */);
msch_data[dev_addr-1].max_lun = msch_buffer[0];
#if 0
//------------- Reset -------------//
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_CLASS, TUSB_REQ_RCPT_INTERFACE),
MSC_REQUEST_RESET, 0, msch_data[dev_addr-1].interface_number,
0, NULL ),
error
);
#endif
enum { SCSI_XFER_TIMEOUT = 2000 };
//------------- SCSI Inquiry -------------//
tusbh_msc_inquiry(dev_addr, 0, msch_buffer);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error);
STASK_ASSERT_ERR(error);
memcpy(msch_data[dev_addr-1].vendor_id , ((scsi_inquiry_data_t*) msch_buffer)->vendor_id , 8);
memcpy(msch_data[dev_addr-1].product_id, ((scsi_inquiry_data_t*) msch_buffer)->product_id, 16);
//------------- SCSI Read Capacity 10 -------------//
tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error);
STASK_ASSERT_ERR(error);
// NOTE: my toshiba thumb-drive stall the first Read Capacity and require the sequence
// Read Capacity --> Stalled --> Clear Stall --> Request Sense --> Read Capacity (2) to work
if ( hcd_pipe_is_stalled(msch_data[dev_addr-1].bulk_in) )
{ // clear stall TODO abstract clear stall function
STASK_INVOKE(
usbh_control_xfer_subtask( dev_addr, bm_request_type(TUSB_DIR_OUT, TUSB_REQ_TYPE_STANDARD, TUSB_REQ_RCPT_ENDPOINT),
TUSB_REQ_CLEAR_FEATURE, 0, hcd_pipe_get_endpoint_addr(msch_data[dev_addr-1].bulk_in),
0, NULL ),
error
);
STASK_ASSERT_ERR(error);
hcd_pipe_clear_stall(msch_data[dev_addr-1].bulk_in);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error); // wait for SCSI status
STASK_ASSERT_ERR(error);
//------------- SCSI Request Sense -------------//
(void) tuh_msc_request_sense(dev_addr, 0, msch_buffer);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error);
STASK_ASSERT_ERR(error);
//------------- Re-read SCSI Read Capactity -------------//
tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT, &error);
STASK_ASSERT_ERR(error);
}
msch_data[dev_addr-1].last_lba = __be2n( ((scsi_read_capacity10_data_t*)msch_buffer)->last_lba );
msch_data[dev_addr-1].block_size = (uint16_t) __be2n( ((scsi_read_capacity10_data_t*)msch_buffer)->block_size );
msch_data[dev_addr-1].is_initialized = true;
tuh_msc_mounted_cb(dev_addr);
OSAL_SUBTASK_END
}
void msch_isr(pipe_handle_t pipe_hdl, tusb_event_t event, uint32_t xferred_bytes)
{
if ( pipehandle_is_equal(pipe_hdl, msch_data[pipe_hdl.dev_addr-1].bulk_in) )
{
if (msch_data[pipe_hdl.dev_addr-1].is_initialized)
{
tuh_msc_isr(pipe_hdl.dev_addr, event, xferred_bytes);
}else
{ // still initializing under open subtask
osal_semaphore_post(msch_sem_hdl);
}
}
}
void msch_close(uint8_t dev_addr)
{
(void) hcd_pipe_close(msch_data[dev_addr-1].bulk_in);
(void) hcd_pipe_close(msch_data[dev_addr-1].bulk_out);
memclr_(&msch_data[dev_addr-1], sizeof(msch_interface_t));
osal_semaphore_reset(msch_sem_hdl);
tuh_msc_unmounted_cb(dev_addr); // invoke Application Callback
}
//--------------------------------------------------------------------+
// INTERNAL & HELPER
//--------------------------------------------------------------------+
#endif

219
src/class/msc/msc_host.h Normal file
View File

@@ -0,0 +1,219 @@
/**************************************************************************/
/*!
@file msc_host.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#ifndef _TUSB_MSC_HOST_H_
#define _TUSB_MSC_HOST_H_
#include "common/tusb_common.h"
#include "host/usbh.h"
#include "msc.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \addtogroup ClassDriver_MSC
* @{
* \defgroup MSC_Host Host
* The interface API includes status checking function, data transferring function and callback functions
* @{ */
//--------------------------------------------------------------------+
// MASS STORAGE Application API
//--------------------------------------------------------------------+
/** \brief Check if device supports MassStorage interface or not
* \param[in] dev_addr device address
* \retval true if device supports
* \retval false if device does not support or is not mounted
*/
bool tuh_msc_is_mounted(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
/** \brief Check if the interface is currently busy or not
* \param[in] dev_addr device address
* \retval true if the interface is busy meaning the stack is still transferring/waiting data from/to device
* \retval false if the interface is not busy meaning the stack successfully transferred data from/to device
* \note This function is used to check if previous transfer is complete (success or error), so that the next transfer
* can be scheduled. User needs to make sure the corresponding interface is mounted (by \ref tuh_msc_is_mounted)
* before calling this function
*/
bool tuh_msc_is_busy(uint8_t dev_addr) ATTR_PURE ATTR_WARN_UNUSED_RESULT;
/** \brief Get SCSI vendor's name of MassStorage device
* \param[in] dev_addr device address
* \return pointer to vendor's name or NULL if specified device does not support MassStorage
* \note SCSI vendor's name is 8-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already
* retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY
* command or allocate buffer for this.
*/
uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr);
/** \brief Get SCSI product's name of MassStorage device
* \param[in] dev_addr device address
* \return pointer to product's name or NULL if specified device does not support MassStorage
* \note SCSI product's name is 16-byte length field in \ref scsi_inquiry_data_t. During enumeration, the stack has already
* retrieved (via SCSI INQUIRY) and store this information internally. There is no need for application to re-send SCSI INQUIRY
* command or allocate buffer for this.
*/
uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr);
/** \brief Get SCSI Capacity of MassStorage device
* \param[in] dev_addr device address
* \param[out] p_last_lba Last Logical Block Address of device
* \param[out] p_block_size Block Size of device in bytes
* \retval pointer to product's name or NULL if specified device does not support MassStorage
* \note MassStorage's capacity can be computed by last LBA x block size (in bytes). During enumeration, the stack has already
* retrieved (via SCSI READ CAPACITY 10) and store this information internally. There is no need for application
* to re-send SCSI READ CAPACITY 10 command
*/
tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size);
/** \brief Perform SCSI READ 10 command to read data from MassStorage device
* \param[in] dev_addr device address
* \param[in] lun Targeted Logical Unit
* \param[out] p_buffer Buffer used to store data read from device. Must be accessible by USB controller (see \ref CFG_TUSB_ATTR_USBRAM)
* \param[in] lba Starting Logical Block Address to be read
* \param[in] block_count Number of Block to be read
* \retval TUSB_ERROR_NONE on success
* \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
* \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_msc_read10 (uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count) ATTR_WARN_UNUSED_RESULT;
/** \brief Perform SCSI WRITE 10 command to write data to MassStorage device
* \param[in] dev_addr device address
* \param[in] lun Targeted Logical Unit
* \param[in] p_buffer Buffer containing data. Must be accessible by USB controller (see \ref CFG_TUSB_ATTR_USBRAM)
* \param[in] lba Starting Logical Block Address to be written
* \param[in] block_count Number of Block to be written
* \retval TUSB_ERROR_NONE on success
* \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
* \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count) ATTR_WARN_UNUSED_RESULT;
/** \brief Perform SCSI REQUEST SENSE command, used to retrieve sense data from MassStorage device
* \param[in] dev_addr device address
* \param[in] lun Targeted Logical Unit
* \param[in] p_data Buffer to store response's data from device. Must be accessible by USB controller (see \ref CFG_TUSB_ATTR_USBRAM)
* \retval TUSB_ERROR_NONE on success
* \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
* \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data);
/** \brief Perform SCSI TEST UNIT READY command to test if MassStorage device is ready
* \param[in] dev_addr device address
* \param[in] lun Targeted Logical Unit
* \retval TUSB_ERROR_NONE on success
* \retval TUSB_ERROR_INTERFACE_IS_BUSY if the interface is already transferring data with device
* \retval TUSB_ERROR_DEVICE_NOT_READY if device is not yet configured (by SET CONFIGURED request)
* \retval TUSB_ERROR_INVALID_PARA if input parameters are not correct
* \note This function is non-blocking and returns immediately. The result of USB transfer will be reported by the interface's callback function
*/
tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw) ATTR_WARN_UNUSED_RESULT; // TODO to be refractor
//tusb_error_t tusbh_msc_scsi_send(uint8_t dev_addr, uint8_t lun, bool is_direction_in,
// uint8_t const * p_command, uint8_t cmd_len,
// uint8_t * p_response, uint32_t resp_len) ATTR_WARN_UNUSED_RESULT;
//------------- Application Callback -------------//
/** \brief Callback function that will be invoked when a device with MassStorage interface is mounted
* \param[in] dev_addr Address of newly mounted device
* \note This callback should be used by Application to set-up interface-related data
*/
void tuh_msc_mounted_cb(uint8_t dev_addr);
/** \brief Callback function that will be invoked when a device with MassStorage interface is unmounted
* \param[in] dev_addr Address of newly unmounted device
* \note This callback should be used by Application to tear-down interface-related data
*/
void tuh_msc_unmounted_cb(uint8_t dev_addr);
/** \brief Callback function that is invoked when an transferring event occurred
* \param[in] dev_addr Address of device
* \param[in] event an value from \ref tusb_event_t
* \param[in] xferred_bytes Number of bytes transferred via USB bus
* \note event can be one of following
* - TUSB_EVENT_XFER_COMPLETE : previously scheduled transfer completes successfully.
* - TUSB_EVENT_XFER_ERROR : previously scheduled transfer encountered a transaction error.
* - TUSB_EVENT_XFER_STALLED : previously scheduled transfer is stalled by device.
* \note
*/
void tuh_msc_isr(uint8_t dev_addr, tusb_event_t event, uint32_t xferred_bytes);
//--------------------------------------------------------------------+
// USBH-CLASS DRIVER API
//--------------------------------------------------------------------+
#ifdef _TINY_USB_SOURCE_FILE_
typedef struct {
pipe_handle_t bulk_in, bulk_out;
uint8_t interface_number;
uint8_t max_lun;
uint16_t block_size;
uint32_t last_lba; // last logical block address
volatile bool is_initialized;
uint8_t vendor_id[8];
uint8_t product_id[16];
msc_cbw_t cbw;
msc_csw_t csw;
}msch_interface_t;
void msch_init(void);
tusb_error_t msch_open_subtask(uint8_t dev_addr, tusb_desc_interface_t const *p_interface_desc, uint16_t *p_length) ATTR_WARN_UNUSED_RESULT;
void msch_isr(pipe_handle_t pipe_hdl, tusb_event_t event, uint32_t xferred_bytes);
void msch_close(uint8_t dev_addr);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_MSC_HOST_H_ */
/// @}
/// @}