2013-11-01 06:11:26 +01:00
/**************************************************************************/
/*!
@ 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 && TUSB_CFG_DEVICE_MSC)
# define _TINY_USB_SOURCE_FILE_
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
# include "common/common.h"
# include "msc_device.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct {
2014-03-22 16:50:08 +01:00
uint8_t scsi_data [ 64 ] ; // buffer for scsi's response other than read10 & write10. NOTE should be multiple of 64 to be compatible with lpc11/13u
2013-11-13 06:40:11 +01:00
ATTR_USB_MIN_ALIGNMENT msc_cmd_block_wrapper_t cbw ;
2014-03-12 08:28:43 +01:00
# if defined (__ICCARM__) && (TUSB_CFG_MCU == MCU_LPC11UXX || TUSB_CFG_MCU == MCU_LPC13UXX)
uint8_t padding1 [ 64 - sizeof ( msc_cmd_block_wrapper_t ) ] ; // IAR cannot align struct's member
# endif
2013-11-13 06:40:11 +01:00
ATTR_USB_MIN_ALIGNMENT msc_cmd_status_wrapper_t csw ;
2014-03-12 08:28:43 +01:00
2014-03-22 16:50:08 +01:00
uint8_t max_lun ;
2014-03-12 08:08:52 +01:00
uint8_t interface_number ;
endpoint_handle_t edpt_in , edpt_out ;
2013-11-01 06:11:26 +01:00
} mscd_interface_t ;
2014-03-21 13:23:19 +01:00
TUSB_CFG_ATTR_USBRAM STATIC_VAR mscd_interface_t mscd_data ;
2013-11-01 06:11:26 +01:00
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
2014-03-09 09:30:57 +01:00
static bool read10_write10_data_xfer ( mscd_interface_t * p_msc ) ;
2013-11-01 06:11:26 +01:00
//--------------------------------------------------------------------+
// USBD-CLASS API
//--------------------------------------------------------------------+
2013-11-13 06:40:11 +01:00
void mscd_init ( void )
{
2013-12-01 16:34:59 +01:00
memclr_ ( & mscd_data , sizeof ( mscd_interface_t ) ) ;
2013-11-13 06:40:11 +01:00
}
2013-11-21 06:47:55 +01:00
void mscd_close ( uint8_t coreid )
2013-11-13 06:40:11 +01:00
{
2013-12-01 16:34:59 +01:00
memclr_ ( & mscd_data , sizeof ( mscd_interface_t ) ) ;
2014-03-31 06:59:43 +02:00
tusbd_msc_unmounted_cb ( coreid ) ;
2013-11-13 06:40:11 +01:00
}
2013-11-01 06:11:26 +01:00
tusb_error_t mscd_open ( uint8_t coreid , tusb_descriptor_interface_t const * p_interface_desc , uint16_t * p_length )
{
ASSERT ( ( MSC_SUBCLASS_SCSI = = p_interface_desc - > bInterfaceSubClass & &
MSC_PROTOCOL_BOT = = p_interface_desc - > bInterfaceProtocol ) , TUSB_ERROR_MSC_UNSUPPORTED_PROTOCOL ) ;
mscd_interface_t * p_msc = & mscd_data ;
//------------- Open Data Pipe -------------//
tusb_descriptor_endpoint_t const * p_endpoint = ( tusb_descriptor_endpoint_t const * ) descriptor_next ( ( uint8_t const * ) p_interface_desc ) ;
for ( uint32_t i = 0 ; i < 2 ; i + + )
{
ASSERT ( TUSB_DESC_TYPE_ENDPOINT = = p_endpoint - > bDescriptorType & &
TUSB_XFER_BULK = = p_endpoint - > bmAttributes . xfer , TUSB_ERROR_DESCRIPTOR_CORRUPTED ) ;
endpoint_handle_t * p_edpt_hdl = ( p_endpoint - > bEndpointAddress & TUSB_DIR_DEV_TO_HOST_MASK ) ?
& p_msc - > edpt_in : & p_msc - > edpt_out ;
( * p_edpt_hdl ) = dcd_pipe_open ( coreid , p_endpoint , p_interface_desc - > bInterfaceClass ) ;
ASSERT ( endpointhandle_is_valid ( * p_edpt_hdl ) , TUSB_ERROR_DCD_FAILED ) ;
p_endpoint = ( tusb_descriptor_endpoint_t const * ) descriptor_next ( ( uint8_t const * ) p_endpoint ) ;
}
p_msc - > interface_number = p_interface_desc - > bInterfaceNumber ;
( * p_length ) + = sizeof ( tusb_descriptor_interface_t ) + 2 * sizeof ( tusb_descriptor_endpoint_t ) ;
2014-03-31 06:59:43 +02:00
tusbd_msc_mounted_cb ( coreid ) ;
2013-11-01 06:11:26 +01:00
//------------- Queue Endpoint OUT for Command Block Wrapper -------------//
2014-03-10 07:13:13 +01:00
ASSERT_STATUS ( dcd_pipe_xfer ( p_msc - > edpt_out , ( uint8_t * ) & p_msc - > cbw , sizeof ( msc_cmd_block_wrapper_t ) , true ) ) ;
2013-11-01 06:11:26 +01:00
return TUSB_ERROR_NONE ;
}
2013-12-09 05:15:13 +01:00
tusb_error_t mscd_control_request_subtask ( uint8_t coreid , tusb_control_request_t const * p_request )
2013-11-01 06:11:26 +01:00
{
ASSERT ( p_request - > bmRequestType_bit . type = = TUSB_REQUEST_TYPE_CLASS , TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT ) ;
mscd_interface_t * p_msc = & mscd_data ;
switch ( p_request - > bRequest )
{
case MSC_REQUEST_RESET :
2013-12-13 06:26:37 +01:00
dcd_pipe_control_xfer ( coreid , TUSB_DIR_HOST_TO_DEV , NULL , 0 , false ) ;
2013-11-01 06:11:26 +01:00
break ;
case MSC_REQUEST_GET_MAX_LUN :
2014-03-22 16:50:08 +01:00
p_msc - > scsi_data [ 0 ] = p_msc - > max_lun ; // Note: lpc11/13u need xfer data's address to be aligned 64 -> make use of scsi_data instead of using max_lun directly
dcd_pipe_control_xfer ( coreid , TUSB_DIR_DEV_TO_HOST , p_msc - > scsi_data , 1 , false ) ;
2013-11-01 06:11:26 +01:00
break ;
default :
return TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT ;
}
return TUSB_ERROR_NONE ;
}
2013-12-01 16:34:59 +01:00
//--------------------------------------------------------------------+
// MSCD APPLICATION CALLBACK
//--------------------------------------------------------------------+
2013-11-26 07:15:40 +01:00
tusb_error_t mscd_xfer_cb ( endpoint_handle_t edpt_hdl , tusb_event_t event , uint32_t xferred_bytes )
{
static bool is_waiting_read10_write10 = false ; // indicate we are transferring data in READ10, WRITE10 command
2013-11-25 10:42:04 +01:00
2013-11-26 07:15:40 +01:00
mscd_interface_t * const p_msc = & mscd_data ;
msc_cmd_block_wrapper_t * const p_cbw = & p_msc - > cbw ;
msc_cmd_status_wrapper_t * const p_csw = & p_msc - > csw ;
2013-12-01 16:34:59 +01:00
//------------- new CBW received -------------//
2014-03-09 09:30:57 +01:00
if ( ! is_waiting_read10_write10 )
2013-12-01 16:34:59 +01:00
{
2013-11-26 07:15:40 +01:00
if ( endpointhandle_is_equal ( p_msc - > edpt_in , edpt_hdl ) ) return TUSB_ERROR_NONE ; // bulk in interrupt for dcd to clean up
ASSERT ( endpointhandle_is_equal ( p_msc - > edpt_out , edpt_hdl ) & &
xferred_bytes = = sizeof ( msc_cmd_block_wrapper_t ) & &
event = = TUSB_EVENT_XFER_COMPLETE & &
p_cbw - > signature = = MSC_CBW_SIGNATURE , TUSB_ERROR_INVALID_PARA ) ;
2013-11-26 08:17:58 +01:00
p_csw - > signature = MSC_CSW_SIGNATURE ;
p_csw - > tag = p_cbw - > tag ;
p_csw - > data_residue = 0 ;
2013-11-26 07:15:40 +01:00
if ( ( SCSI_CMD_READ_10 ! = p_cbw - > command [ 0 ] ) & & ( SCSI_CMD_WRITE_10 ! = p_cbw - > command [ 0 ] ) )
{
2014-03-22 16:50:08 +01:00
void const * p_buffer = NULL ;
2013-12-01 16:34:59 +01:00
uint16_t actual_length = ( uint16_t ) p_cbw - > xfer_bytes ;
2013-11-26 07:15:40 +01:00
2014-03-22 16:50:08 +01:00
// TODO SCSI data out transfer is not yet supported
ASSERT_FALSE ( p_cbw - > xfer_bytes > 0 & & ! BIT_TEST_ ( p_cbw - > dir , 7 ) , TUSB_ERROR_NOT_SUPPORTED_YET ) ;
2014-03-09 09:30:57 +01:00
p_csw - > status = tusbd_msc_scsi_cb ( edpt_hdl . coreid , p_cbw - > lun , p_cbw - > command , & p_buffer , & actual_length ) ;
2013-11-26 07:15:40 +01:00
2014-03-09 09:30:57 +01:00
//------------- Data Phase (non READ10, WRITE10) -------------//
2013-11-26 07:15:40 +01:00
if ( p_cbw - > xfer_bytes )
2013-11-25 10:42:04 +01:00
{
2013-11-26 07:15:40 +01:00
ASSERT ( p_cbw - > xfer_bytes > = actual_length , TUSB_ERROR_INVALID_PARA ) ;
2014-03-22 16:50:08 +01:00
ASSERT ( sizeof ( p_msc - > scsi_data ) > = actual_length , TUSB_ERROR_NOT_ENOUGH_MEMORY ) ; // needs to increase size for scsi_data
2013-12-01 16:34:59 +01:00
endpoint_handle_t const edpt_data = BIT_TEST_ ( p_cbw - > dir , 7 ) ? p_msc - > edpt_in : p_msc - > edpt_out ;
2013-11-26 07:15:40 +01:00
if ( p_buffer = = NULL | | actual_length = = 0 )
{ // application does not provide data to response --> possibly unsupported SCSI command
2013-12-01 16:34:59 +01:00
ASSERT_STATUS ( dcd_pipe_stall ( edpt_data ) ) ;
2013-11-26 07:15:40 +01:00
p_csw - > status = MSC_CSW_STATUS_FAILED ;
} else
{
2014-03-22 16:50:08 +01:00
memcpy ( p_msc - > scsi_data , p_buffer , actual_length ) ;
ASSERT_STATUS ( dcd_pipe_queue_xfer ( edpt_data , p_msc - > scsi_data , actual_length ) ) ;
2013-11-26 07:15:40 +01:00
}
2013-11-25 10:42:04 +01:00
}
}
}
2013-11-01 06:11:26 +01:00
2013-11-26 07:15:40 +01:00
//------------- Data Phase For READ10 & WRITE10 (can be executed several times) -------------//
if ( ( SCSI_CMD_READ_10 = = p_cbw - > command [ 0 ] ) | | ( SCSI_CMD_WRITE_10 = = p_cbw - > command [ 0 ] ) )
2013-11-01 06:11:26 +01:00
{
2013-11-26 07:15:40 +01:00
is_waiting_read10_write10 = ! read10_write10_data_xfer ( p_msc ) ;
2013-11-01 06:11:26 +01:00
}
//------------- Status Phase -------------//
2013-12-01 09:48:49 +01:00
// Either bulk in & out can be stalled in the data phase, dcd must make sure these queued transfer will be resumed after host clear stall
2013-11-26 07:15:40 +01:00
if ( ! is_waiting_read10_write10 )
{
2014-03-10 07:13:13 +01:00
ASSERT_STATUS ( dcd_pipe_xfer ( p_msc - > edpt_in , ( uint8_t * ) p_csw , sizeof ( msc_cmd_status_wrapper_t ) , false ) ) ;
2013-11-01 06:11:26 +01:00
2013-11-26 07:15:40 +01:00
//------------- Queue the next CBW -------------//
2014-03-10 07:13:13 +01:00
ASSERT_STATUS ( dcd_pipe_xfer ( p_msc - > edpt_out , ( uint8_t * ) p_cbw , sizeof ( msc_cmd_block_wrapper_t ) , true ) ) ;
2013-11-26 07:15:40 +01:00
}
2013-11-01 06:11:26 +01:00
2013-11-26 07:15:40 +01:00
return TUSB_ERROR_NONE ;
2013-11-01 06:11:26 +01:00
}
2014-03-09 09:30:57 +01:00
// return true if data phase is complete, false if not yet complete
static bool read10_write10_data_xfer ( mscd_interface_t * p_msc )
{
msc_cmd_block_wrapper_t * const p_cbw = & p_msc - > cbw ;
msc_cmd_status_wrapper_t * const p_csw = & p_msc - > csw ;
scsi_read10_t * p_readwrite = ( scsi_read10_t * ) & p_cbw - > command ; // read10 & write10 has the same format
endpoint_handle_t const edpt_hdl = BIT_TEST_ ( p_cbw - > dir , 7 ) ? p_msc - > edpt_in : p_msc - > edpt_out ;
uint32_t const lba = __be2n ( p_readwrite - > lba ) ;
uint16_t const block_count = __be2n_16 ( p_readwrite - > block_count ) ;
void * p_buffer = NULL ;
uint16_t xferred_block = ( SCSI_CMD_READ_10 = = p_cbw - > command [ 0 ] ) ? tusbd_msc_read10_cb ( edpt_hdl . coreid , p_cbw - > lun , & p_buffer , lba , block_count ) :
tusbd_msc_write10_cb ( edpt_hdl . coreid , p_cbw - > lun , & p_buffer , lba , block_count ) ;
xferred_block = min16_of ( xferred_block , block_count ) ;
uint16_t const xferred_byte = xferred_block * ( p_cbw - > xfer_bytes / block_count ) ;
if ( 0 = = xferred_block )
{ // xferred_block is zero will cause pipe is stalled & status in CSW set to failed
p_csw - > data_residue = p_cbw - > xfer_bytes ;
p_csw - > status = MSC_CSW_STATUS_FAILED ;
( void ) dcd_pipe_stall ( edpt_hdl ) ;
return true ;
} else if ( xferred_block < block_count )
{
ASSERT_STATUS ( dcd_pipe_xfer ( edpt_hdl , p_buffer , xferred_byte , true ) ) ;
// adjust lba, block_count, xfer_bytes for the next call
p_readwrite - > lba = __n2be ( lba + xferred_block ) ;
p_readwrite - > block_count = __n2be_16 ( block_count - xferred_block ) ;
p_cbw - > xfer_bytes - = xferred_byte ;
return false ;
} else
{
p_csw - > status = MSC_CSW_STATUS_PASSED ;
ASSERT_STATUS ( dcd_pipe_queue_xfer ( edpt_hdl , p_buffer , xferred_byte ) ) ;
return true ;
}
}
2013-11-01 06:11:26 +01:00
# endif