summaryrefslogtreecommitdiff
path: root/src/class/msc
diff options
context:
space:
mode:
authornxf58843 <[email protected]>2021-08-23 09:55:04 -0700
committerGitHub <[email protected]>2021-08-23 09:55:04 -0700
commitfb16e80e575a44828f5367f4fb7380162b0354f0 (patch)
treebb965cc935083d99e083667d4f0f1533d50a29f5 /src/class/msc
parent616562a48aa1d8ce2f4ca71f6e780a8bec9fb5eb (diff)
parentea902493db49843dcdeca6bfa2b2efe540f44b2f (diff)
Merge pull request #2 from hathach/master
Pulling latest from source
Diffstat (limited to 'src/class/msc')
-rw-r--r--src/class/msc/msc.h12
-rw-r--r--src/class/msc/msc_device.c477
-rw-r--r--src/class/msc/msc_device.h96
-rw-r--r--src/class/msc/msc_host.c569
-rw-r--r--src/class/msc/msc_host.h203
5 files changed, 667 insertions, 690 deletions
diff --git a/src/class/msc/msc.h b/src/class/msc/msc.h
index cba8066b7..84b6e4d79 100644
--- a/src/class/msc/msc.h
+++ b/src/class/msc/msc.h
@@ -24,13 +24,6 @@
* 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_
@@ -255,7 +248,7 @@ typedef struct TU_ATTR_PACKED
uint8_t : 3;
uint8_t disable_block_descriptor : 1;
- uint8_t : 0;
+ uint8_t : 4;
uint8_t page_code : 6;
uint8_t page_control : 2;
@@ -387,6 +380,3 @@ TU_VERIFY_STATIC(sizeof(scsi_write10_t) == 10, "size is not correct");
#endif
#endif /* _TUSB_MSC_H_ */
-
-/// @}
-/// @}
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 3ff0eba92..014e8b69e 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -28,13 +28,19 @@
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_MSC)
-#include "common/tusb_common.h"
-#include "msc_device.h"
+#include "device/usbd.h"
#include "device/usbd_pvt.h"
+#include "device/dcd.h" // for faking dcd_event_xfer_complete
+
+#include "msc_device.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
+
+// Can be selectively disabled to reduce logging when troubleshooting other driver
+#define MSC_DEBUG 2
+
enum
{
MSC_STAGE_CMD = 0,
@@ -65,36 +71,30 @@ typedef struct
}mscd_interface_t;
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static mscd_interface_t _mscd_itf;
-CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_BUFSIZE];
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _mscd_buf[CFG_TUD_MSC_EP_BUFSIZE];
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize);
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);
+ // use offsetof to avoid pointer to the odd/unaligned address
+ uint32_t const lba = tu_unaligned_read32(command + offsetof(scsi_write10_t, lba));
- // lba is in Big Endian format
+ // lba is in Big Endian
return tu_ntohl(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);
+ // use offsetof to avoid pointer to the odd/misaligned address
+ uint16_t const block_count = tu_unaligned_read16(command + offsetof(scsi_write10_t, block_count));
+ // block count is in Big Endian
return tu_ntohs(block_count);
}
@@ -103,7 +103,7 @@ static inline uint16_t rdwr10_get_blockcount(uint8_t const command[])
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= 2
-static lookup_entry_t const _msc_scsi_cmd_lookup[] =
+TU_ATTR_UNUSED static tu_lookup_entry_t const _msc_scsi_cmd_lookup[] =
{
{ .key = SCSI_CMD_TEST_UNIT_READY , .data = "Test Unit Ready" },
{ .key = SCSI_CMD_INQUIRY , .data = "Inquiry" },
@@ -118,7 +118,7 @@ static lookup_entry_t const _msc_scsi_cmd_lookup[] =
{ .key = SCSI_CMD_WRITE_10 , .data = "Write10" }
};
-static lookup_table_t const _msc_scsi_cmd_table =
+TU_ATTR_UNUSED static tu_lookup_table_t const _msc_scsi_cmd_table =
{
.count = TU_ARRAY_SIZE(_msc_scsi_cmd_lookup),
.items = _msc_scsi_cmd_lookup
@@ -154,30 +154,43 @@ void mscd_reset(uint8_t rhport)
tu_memclr(&_mscd_itf, sizeof(mscd_interface_t));
}
-bool mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_len)
+uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
// only support SCSI's BOT protocol
- TU_ASSERT(MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
+ TU_VERIFY(TUSB_CLASS_MSC == itf_desc->bInterfaceClass &&
+ MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
+ MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol, 0);
- mscd_interface_t * p_msc = &_mscd_itf;
+ // msc driver length is fixed
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
- // Open endpoint pair
- TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in) );
+ // Max length mus be at least 1 interface + 2 endpoints
+ TU_ASSERT(max_len >= drv_len, 0);
+ mscd_interface_t * p_msc = &_mscd_itf;
p_msc->itf_num = itf_desc->bInterfaceNumber;
- (*p_len) = sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+
+ // Open endpoint pair
+ TU_ASSERT( usbd_open_edpt_pair(rhport, tu_desc_next(itf_desc), 2, TUSB_XFER_BULK, &p_msc->ep_out, &p_msc->ep_in), 0 );
// Prepare for Command Block Wrapper
- TU_ASSERT( usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) );
+ if ( !usbd_edpt_xfer(rhport, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)) )
+ {
+ TU_LOG_FAILED();
+ TU_BREAKPOINT();
+ }
- return true;
+ return drv_len;
}
-// Handle class control request
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
-bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
+bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request)
{
+ // nothing to do with DATA & ACK stage
+ if (stage != CONTROL_STAGE_SETUP) return true;
+
// Handle class request only
TU_VERIFY(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
@@ -207,190 +220,6 @@ bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
return true;
}
-// Invoked when class request DATA stage is finished.
-// return false to stall control endpoint (e.g Host send non-sense DATA)
-bool mscd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
- (void) rhport;
- (void) request;
-
- // nothing to do
- return true;
-}
-
-// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
-// In case of a failed status, sense key must be set for reason of failure
-int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
-{
- (void) bufsize; // TODO refractor later
- int32_t resplen;
-
- switch ( scsi_cmd[0] )
- {
- case SCSI_CMD_TEST_UNIT_READY:
- resplen = 0;
- if ( !tud_msc_test_unit_ready_cb(lun) )
- {
- // Failed status response
- resplen = - 1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }
- break;
-
- case SCSI_CMD_START_STOP_UNIT:
- resplen = 0;
-
- if (tud_msc_start_stop_cb)
- {
- scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
- if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
- {
- // Failed status response
- resplen = - 1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }
- }
- break;
-
- case SCSI_CMD_READ_CAPACITY_10:
- {
- uint32_t block_count;
- uint32_t block_size;
- uint16_t block_size_u16;
-
- tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
- block_size = (uint32_t) block_size_u16;
-
- // Invalid block size/count from callback, possibly unit is not ready
- // stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
- resplen = -1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }else
- {
- scsi_read_capacity10_resp_t read_capa10;
-
- read_capa10.last_lba = tu_htonl(block_count-1);
- read_capa10.block_size = tu_htonl(block_size);
-
- resplen = sizeof(read_capa10);
- memcpy(buffer, &read_capa10, resplen);
- }
- }
- break;
-
- case SCSI_CMD_READ_FORMAT_CAPACITY:
- {
- scsi_read_format_capacity_data_t read_fmt_capa =
- {
- .list_length = 8,
- .block_num = 0,
- .descriptor_type = 2, // formatted media
- .block_size_u16 = 0
- };
-
- uint32_t block_count;
- uint16_t block_size;
-
- tud_msc_capacity_cb(lun, &block_count, &block_size);
-
- // Invalid block size/count from callback, possibly unit is not ready
- // stall this request, set sense key to NOT READY
- if (block_count == 0 || block_size == 0)
- {
- resplen = -1;
-
- // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
- if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
- }else
- {
- read_fmt_capa.block_num = tu_htonl(block_count);
- read_fmt_capa.block_size_u16 = tu_htons(block_size);
-
- resplen = sizeof(read_fmt_capa);
- memcpy(buffer, &read_fmt_capa, resplen);
- }
- }
- break;
-
- case SCSI_CMD_INQUIRY:
- {
- scsi_inquiry_resp_t inquiry_rsp =
- {
- .is_removable = 1,
- .version = 2,
- .response_data_format = 2,
- };
-
- // vendor_id, product_id, product_rev is space padded string
- memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
- memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
- memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
-
- tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
-
- resplen = sizeof(inquiry_rsp);
- memcpy(buffer, &inquiry_rsp, resplen);
- }
- break;
-
- case SCSI_CMD_MODE_SENSE_6:
- {
- scsi_mode_sense6_resp_t mode_resp =
- {
- .data_len = 3,
- .medium_type = 0,
- .write_protected = false,
- .reserved = 0,
- .block_descriptor_len = 0 // no block descriptor are included
- };
-
- bool writable = true;
- if (tud_msc_is_writable_cb) {
- writable = tud_msc_is_writable_cb(lun);
- }
- mode_resp.write_protected = !writable;
-
- resplen = sizeof(mode_resp);
- memcpy(buffer, &mode_resp, resplen);
- }
- break;
-
- case SCSI_CMD_REQUEST_SENSE:
- {
- scsi_sense_fixed_resp_t sense_rsp =
- {
- .response_code = 0x70,
- .valid = 1
- };
-
- sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
-
- sense_rsp.sense_key = _mscd_itf.sense_key;
- sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
- sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
-
- resplen = sizeof(sense_rsp);
- memcpy(buffer, &sense_rsp, resplen);
-
- // Clear sense data after copy
- tud_msc_set_sense(lun, 0, 0, 0);
- }
- break;
-
- default: resplen = -1; break;
- }
-
- return resplen;
-}
-
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
mscd_interface_t* p_msc = &_mscd_itf;
@@ -407,8 +236,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
TU_ASSERT( event == XFER_RESULT_SUCCESS &&
xferred_bytes == sizeof(msc_cbw_t) && p_cbw->signature == MSC_CBW_SIGNATURE );
- TU_LOG2(" SCSI Command: %s\n", lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
- // TU_LOG2_MEM(p_cbw, xferred_bytes, 2);
+ TU_LOG(MSC_DEBUG, " SCSI Command: %s\r\n", tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
+ // TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
p_csw->signature = MSC_CSW_SIGNATURE;
p_csw->tag = p_cbw->tag;
@@ -480,8 +309,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
break;
case MSC_STAGE_DATA:
- TU_LOG2(" SCSI Data\n");
- //TU_LOG2_MEM(_mscd_buf, xferred_bytes, 2);
+ TU_LOG(MSC_DEBUG, " SCSI Data\r\n");
+ //TU_LOG_MEM(MSC_DEBUG, _mscd_buf, xferred_bytes, 2);
// OUT transfer, invoke callback if needed
if ( !tu_bit_test(p_cbw->dir, 7) )
@@ -553,7 +382,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
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
+ // We break it into multiple smaller command whose data size is up to CFG_TUD_MSC_EP_BUFSIZE
if (SCSI_CMD_READ_10 == p_cbw->command[0])
{
proc_read10_cmd(rhport, p_msc);
@@ -577,8 +406,26 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
// Wait for the Status phase to complete
if( (ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t)) )
{
- TU_LOG2(" SCSI Status: %u\n", p_csw->status);
- // TU_LOG2_MEM(p_csw, xferred_bytes, 2);
+ TU_LOG(MSC_DEBUG, " SCSI Status: %u\r\n", p_csw->status);
+ // TU_LOG_MEM(MSC_DEBUG, p_csw, xferred_bytes, 2);
+
+ // Invoke complete callback if defined
+ // Note: There is racing issue with samd51 + qspi flash testing with arduino
+ // if complete_cb() is invoked after queuing the status.
+ switch(p_cbw->command[0])
+ {
+ case SCSI_CMD_READ_10:
+ if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
+ break;
+
+ case SCSI_CMD_WRITE_10:
+ if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
+ break;
+
+ default:
+ if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
+ break;
+ }
// Move to default CMD stage
p_msc->stage = MSC_STAGE_CMD;
@@ -603,22 +450,6 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
}
else
{
- // Invoke complete callback if defined
- switch(p_cbw->command[0])
- {
- case SCSI_CMD_READ_10:
- if ( tud_msc_read10_complete_cb ) tud_msc_read10_complete_cb(p_cbw->lun);
- break;
-
- case SCSI_CMD_WRITE_10:
- if ( tud_msc_write10_complete_cb ) tud_msc_write10_complete_cb(p_cbw->lun);
- break;
-
- default:
- if ( tud_msc_scsi_complete_cb ) tud_msc_scsi_complete_cb(p_cbw->lun, p_cbw->command);
- break;
- }
-
// Move to Status Sent stage
p_msc->stage = MSC_STAGE_STATUS_SENT;
@@ -633,6 +464,180 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
/*------------------------------------------------------------------*/
/* SCSI Command Process
*------------------------------------------------------------------*/
+
+// return response's length (copied to buffer). Negative if it is not an built-in command or indicate Failed status (CSW)
+// In case of a failed status, sense key must be set for reason of failure
+static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_t* buffer, uint32_t bufsize)
+{
+ (void) bufsize; // TODO refractor later
+ int32_t resplen;
+
+ switch ( scsi_cmd[0] )
+ {
+ case SCSI_CMD_TEST_UNIT_READY:
+ resplen = 0;
+ if ( !tud_msc_test_unit_ready_cb(lun) )
+ {
+ // Failed status response
+ resplen = - 1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }
+ break;
+
+ case SCSI_CMD_START_STOP_UNIT:
+ resplen = 0;
+
+ if (tud_msc_start_stop_cb)
+ {
+ scsi_start_stop_unit_t const * start_stop = (scsi_start_stop_unit_t const *) scsi_cmd;
+ if ( !tud_msc_start_stop_cb(lun, start_stop->power_condition, start_stop->start, start_stop->load_eject) )
+ {
+ // Failed status response
+ resplen = - 1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }
+ }
+ break;
+
+ case SCSI_CMD_READ_CAPACITY_10:
+ {
+ uint32_t block_count;
+ uint32_t block_size;
+ uint16_t block_size_u16;
+
+ tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
+ block_size = (uint32_t) block_size_u16;
+
+ // Invalid block size/count from callback, possibly unit is not ready
+ // stall this request, set sense key to NOT READY
+ if (block_count == 0 || block_size == 0)
+ {
+ resplen = -1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }else
+ {
+ scsi_read_capacity10_resp_t read_capa10;
+
+ read_capa10.last_lba = tu_htonl(block_count-1);
+ read_capa10.block_size = tu_htonl(block_size);
+
+ resplen = sizeof(read_capa10);
+ memcpy(buffer, &read_capa10, resplen);
+ }
+ }
+ break;
+
+ case SCSI_CMD_READ_FORMAT_CAPACITY:
+ {
+ scsi_read_format_capacity_data_t read_fmt_capa =
+ {
+ .list_length = 8,
+ .block_num = 0,
+ .descriptor_type = 2, // formatted media
+ .block_size_u16 = 0
+ };
+
+ uint32_t block_count;
+ uint16_t block_size;
+
+ tud_msc_capacity_cb(lun, &block_count, &block_size);
+
+ // Invalid block size/count from callback, possibly unit is not ready
+ // stall this request, set sense key to NOT READY
+ if (block_count == 0 || block_size == 0)
+ {
+ resplen = -1;
+
+ // If sense key is not set by callback, default to Logical Unit Not Ready, Cause Not Reportable
+ if ( _mscd_itf.sense_key == 0 ) tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
+ }else
+ {
+ read_fmt_capa.block_num = tu_htonl(block_count);
+ read_fmt_capa.block_size_u16 = tu_htons(block_size);
+
+ resplen = sizeof(read_fmt_capa);
+ memcpy(buffer, &read_fmt_capa, resplen);
+ }
+ }
+ break;
+
+ case SCSI_CMD_INQUIRY:
+ {
+ scsi_inquiry_resp_t inquiry_rsp =
+ {
+ .is_removable = 1,
+ .version = 2,
+ .response_data_format = 2,
+ };
+
+ // vendor_id, product_id, product_rev is space padded string
+ memset(inquiry_rsp.vendor_id , ' ', sizeof(inquiry_rsp.vendor_id));
+ memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
+ memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
+
+ tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
+
+ resplen = sizeof(inquiry_rsp);
+ memcpy(buffer, &inquiry_rsp, resplen);
+ }
+ break;
+
+ case SCSI_CMD_MODE_SENSE_6:
+ {
+ scsi_mode_sense6_resp_t mode_resp =
+ {
+ .data_len = 3,
+ .medium_type = 0,
+ .write_protected = false,
+ .reserved = 0,
+ .block_descriptor_len = 0 // no block descriptor are included
+ };
+
+ bool writable = true;
+ if (tud_msc_is_writable_cb) {
+ writable = tud_msc_is_writable_cb(lun);
+ }
+ mode_resp.write_protected = !writable;
+
+ resplen = sizeof(mode_resp);
+ memcpy(buffer, &mode_resp, resplen);
+ }
+ break;
+
+ case SCSI_CMD_REQUEST_SENSE:
+ {
+ scsi_sense_fixed_resp_t sense_rsp =
+ {
+ .response_code = 0x70,
+ .valid = 1
+ };
+
+ sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
+
+ sense_rsp.sense_key = _mscd_itf.sense_key;
+ sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
+ sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
+
+ resplen = sizeof(sense_rsp);
+ memcpy(buffer, &sense_rsp, resplen);
+
+ // Clear sense data after copy
+ tud_msc_set_sense(lun, 0, 0, 0);
+ }
+ break;
+
+ default: resplen = -1; break;
+ }
+
+ return resplen;
+}
+
static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc)
{
msc_cbw_t const * p_cbw = &p_msc->cbw;
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index 66180777e..8f90ef4ad 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -28,7 +28,6 @@
#define _TUSB_MSC_DEVICE_H_
#include "common/tusb_common.h"
-#include "device/usbd.h"
#include "msc.h"
#ifdef __cplusplus
@@ -38,59 +37,58 @@
//--------------------------------------------------------------------+
// Class Driver Configuration
//--------------------------------------------------------------------+
-TU_VERIFY_STATIC(CFG_TUD_MSC_BUFSIZE < UINT16_MAX, "Size is not correct");
-#ifndef CFG_TUD_MSC_BUFSIZE
- #error CFG_TUD_MSC_BUFSIZE must be defined, value of a block size should work well, the more the better
+#if !defined(CFG_TUD_MSC_EP_BUFSIZE) & defined(CFG_TUD_MSC_BUFSIZE)
+ // TODO warn user to use new name later on
+ // #warning CFG_TUD_MSC_BUFSIZE is renamed to CFG_TUD_MSC_EP_BUFSIZE, please update to use the new name
+ #define CFG_TUD_MSC_EP_BUFSIZE CFG_TUD_MSC_BUFSIZE
#endif
-/** \addtogroup ClassDriver_MSC
- * @{
- * \defgroup MSC_Device Device
- * @{ */
+#ifndef CFG_TUD_MSC_EP_BUFSIZE
+ #error CFG_TUD_MSC_EP_BUFSIZE must be defined, value of a block size should work well, the more the better
+#endif
+
+TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct");
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+// Set SCSI sense response
bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, uint8_t add_sense_qualifier);
//--------------------------------------------------------------------+
// Application Callbacks (WEAK is optional)
//--------------------------------------------------------------------+
-/**
- * Invoked when received \ref SCSI_CMD_READ_10 command
- * \param[in] lun Logical unit number
- * \param[in] lba Logical Block Address to be read
- * \param[in] offset Byte offset from LBA
- * \param[out] buffer Buffer which application need to update with the response data.
- * \param[in] bufsize Requested bytes
- *
- * \return Number of byte read, if it is less than requested bytes by \a \b bufsize. Tinyusb will transfer
- * this amount first and invoked this again for remaining data.
- *
- * \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 parameters again some time later.
- *
- * \retval negative Indicate error e.g reading disk I/O. tinyusb will \b STALL the corresponding
- * endpoint and return failed status in command status wrapper phase.
- */
+// Invoked when received SCSI READ10 command
+// - Address = lba * BLOCK_SIZE + offset
+// - offset is only needed if CFG_TUD_MSC_EP_BUFSIZE is smaller than BLOCK_SIZE.
+//
+// - Application fill the buffer (up to bufsize) with address contents and return number of read byte. If
+// - read < bufsize : These bytes are transferred first and callback invoked again for remaining data.
+//
+// - read == 0 : Indicate application is not ready yet e.g disk I/O busy.
+// Callback invoked again with the same parameters later on.
+//
+// - read < 0 : Indicate application error e.g invalid address. This request will be STALLed
+// and return failed status in command status wrapper phase.
int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize);
-/**
- * Invoked when received \ref SCSI_CMD_WRITE_10 command
- * \param[in] lun Logical unit number
- * \param[in] lba Logical Block Address to be write
- * \param[in] offset Byte offset from LBA
- * \param[out] buffer Buffer which holds written data.
- * \param[in] bufsize Requested bytes
- *
- * \return Number of byte written, if it is less than requested bytes by \a \b bufsize. Tinyusb will proceed with
- * other work and invoked this again with adjusted parameters.
- *
- * \retval zero Indicate application is not ready yet e.g disk I/O is not complete.
- * Tinyusb will invoke this callback with the same parameters 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.
- */
+// Invoked when received SCSI WRITE10 command
+// - Address = lba * BLOCK_SIZE + offset
+// - offset is only needed if CFG_TUD_MSC_EP_BUFSIZE is smaller than BLOCK_SIZE.
+//
+// - Application write data from buffer to address contents (up to bufsize) and return number of written byte. If
+// - write < bufsize : callback invoked again with remaining data later on.
+//
+// - write == 0 : Indicate application is not ready yet e.g disk I/O busy.
+// Callback invoked again with the same parameters later on.
+//
+// - write < 0 : Indicate application error e.g invalid address. This request will be STALLed
+// and return failed status in command status wrapper phase.
+//
+// TODO change buffer to const uint8_t*
int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize);
// Invoked when received SCSI_CMD_INQUIRY
@@ -145,18 +143,14 @@ TU_ATTR_WEAK void tud_msc_scsi_complete_cb(uint8_t lun, uint8_t const scsi_cmd[1
// Hook to make a mass storage device read-only. TODO remove
TU_ATTR_WEAK bool tud_msc_is_writable_cb(uint8_t lun);
-/** @} */
-/** @} */
-
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
-void mscd_init (void);
-void mscd_reset (uint8_t rhport);
-bool mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
-bool mscd_control_request (uint8_t rhport, tusb_control_request_t const * p_request);
-bool mscd_control_complete (uint8_t rhport, tusb_control_request_t const * p_request);
-bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
+void mscd_init (void);
+void mscd_reset (uint8_t rhport);
+uint16_t mscd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool mscd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * p_request);
+bool mscd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}
diff --git a/src/class/msc/msc_host.c b/src/class/msc/msc_host.c
index e20969f57..f02f4550a 100644
--- a/src/class/msc/msc_host.c
+++ b/src/class/msc/msc_host.c
@@ -28,272 +28,354 @@
#if TUSB_OPT_HOST_ENABLED & CFG_TUH_MSC
-//--------------------------------------------------------------------+
-// INCLUDE
-//--------------------------------------------------------------------+
-#include "common/tusb_common.h"
+#include "host/usbh.h"
+#include "host/usbh_classdriver.h"
+
#include "msc_host.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
-CFG_TUSB_MEM_SECTION static msch_interface_t msch_data[CFG_TUSB_HOST_DEVICE_MAX];
+enum
+{
+ MSC_STAGE_IDLE = 0,
+ MSC_STAGE_CMD,
+ MSC_STAGE_DATA,
+ MSC_STAGE_STATUS,
+};
-//------------- Initalization Data -------------//
-static osal_semaphore_def_t msch_sem_def;
-static osal_semaphore_t msch_sem_hdl;
+typedef struct
+{
+ uint8_t itf_num;
+ uint8_t ep_in;
+ uint8_t ep_out;
-// buffer used to read scsi information when mounted, largest response data currently is inquiry
-CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) static uint8_t msch_buffer[sizeof(scsi_inquiry_resp_t)];
+ uint8_t max_lun;
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
+ volatile bool configured; // Receive SET_CONFIGURE
+ volatile bool mounted; // Enumeration is complete
+
+ struct {
+ uint32_t block_size;
+ uint32_t block_count;
+ } capacity[CFG_TUH_MSC_MAXLUN];
+
+ //------------- SCSI -------------//
+ uint8_t stage;
+ void* buffer;
+ tuh_msc_complete_cb_t complete_cb;
+
+ msc_cbw_t cbw;
+ msc_csw_t csw;
+}msch_interface_t;
+
+CFG_TUSB_MEM_SECTION static msch_interface_t _msch_itf[CFG_TUSB_HOST_DEVICE_MAX];
+
+// buffer used to read scsi information when mounted
+// largest response data currently is inquiry TODO Inquiry is not part of enum anymore
+CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4)
+static uint8_t _msch_buffer[sizeof(scsi_inquiry_resp_t)];
+
+static inline msch_interface_t* get_itf(uint8_t dev_addr)
+{
+ return &_msch_itf[dev_addr-1];
+}
//--------------------------------------------------------------------+
// PUBLIC API
//--------------------------------------------------------------------+
-bool tuh_msc_is_mounted(uint8_t dev_addr)
+uint8_t tuh_msc_get_maxlun(uint8_t dev_addr)
{
- return tuh_device_is_configured(dev_addr) && // is configured can be omitted
- msch_data[dev_addr-1].is_initialized;
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->max_lun;
}
-bool tuh_msc_is_busy(uint8_t dev_addr)
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun)
{
- return msch_data[dev_addr-1].is_initialized &&
- hcd_edpt_busy(dev_addr, msch_data[dev_addr-1].ep_in);
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->capacity[lun].block_count;
}
-uint8_t const* tuh_msc_get_vendor_name(uint8_t dev_addr)
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun)
{
- return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].vendor_id : NULL;
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->capacity[lun].block_size;
}
-uint8_t const* tuh_msc_get_product_name(uint8_t dev_addr)
+bool tuh_msc_mounted(uint8_t dev_addr)
{
- return msch_data[dev_addr-1].is_initialized ? msch_data[dev_addr-1].product_id : NULL;
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->mounted;
}
-tusb_error_t tuh_msc_get_capacity(uint8_t dev_addr, uint32_t* p_last_lba, uint32_t* p_block_size)
+bool tuh_msc_ready(uint8_t dev_addr)
{
- 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;
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ return p_msc->mounted && !usbh_edpt_busy(dev_addr, p_msc->ep_in);
}
//--------------------------------------------------------------------+
// PUBLIC API: SCSI COMMAND
//--------------------------------------------------------------------+
-static inline void msc_cbw_add_signature(msc_cbw_t *p_cbw, uint8_t lun)
+static inline void cbw_init(msc_cbw_t *cbw, uint8_t lun)
{
- p_cbw->signature = MSC_CBW_SIGNATURE;
- p_cbw->tag = 0xCAFECAFE;
- p_cbw->lun = lun;
+ tu_memclr(cbw, sizeof(msc_cbw_t));
+ cbw->signature = MSC_CBW_SIGNATURE;
+ cbw->tag = 0x54555342; // TUSB
+ cbw->lun = lun;
}
-static tusb_error_t msch_command_xfer(uint8_t dev_addr, msch_interface_t * p_msch, void* p_buffer)
+bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb)
{
- if ( NULL != p_buffer)
- { // there is data phase
- if (p_msch->cbw.dir & TUSB_DIR_IN_MASK)
- {
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_in , p_buffer, p_msch->cbw.total_bytes), TUSB_ERROR_FAILED );
- }else
- {
- TU_ASSERT( hcd_pipe_queue_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t)), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out , p_buffer, p_msch->cbw.total_bytes, false), TUSB_ERROR_FAILED );
- }
- }
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) &p_msch->csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED);
+ // TODO claim endpoint
- return TUSB_ERROR_NONE;
-}
+ p_msc->cbw = *cbw;
+ p_msc->stage = MSC_STAGE_CMD;
+ p_msc->buffer = data;
+ p_msc->complete_cb = complete_cb;
-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];
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_out, (uint8_t*) &p_msc->cbw, sizeof(msc_cbw_t)));
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
- p_msch->cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_inquiry_t);
+ return true;
+}
- //------------- SCSI command -------------//
- scsi_inquiry_t cmd_inquiry =
- {
- .cmd_code = SCSI_CMD_INQUIRY,
- .alloc_length = sizeof(scsi_inquiry_resp_t)
- };
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
- memcpy(p_msch->cbw.command, &cmd_inquiry, p_msch->cbw.cmd_len);
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
+ cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_read_capacity10_t);
+ cbw.command[0] = SCSI_CMD_READ_CAPACITY_10;
- return TUSB_ERROR_NONE;
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb);
}
-tusb_error_t tusbh_msc_read_capacity10(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb)
{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
- p_msch->cbw.total_bytes = sizeof(scsi_read_capacity10_resp_t);
- p_msch->cbw.dir = TUSB_DIR_IN_MASK;
- p_msch->cbw.cmd_len = sizeof(scsi_read_capacity10_t);
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- //------------- SCSI command -------------//
- scsi_read_capacity10_t cmd_read_capacity10 =
+ cbw.total_bytes = sizeof(scsi_inquiry_resp_t);
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_inquiry_t);
+
+ scsi_inquiry_t const cmd_inquiry =
{
- .cmd_code = SCSI_CMD_READ_CAPACITY_10,
- .lba = 0,
- .partial_medium_indicator = 0
+ .cmd_code = SCSI_CMD_INQUIRY,
+ .alloc_length = sizeof(scsi_inquiry_resp_t)
};
+ memcpy(cbw.command, &cmd_inquiry, cbw.cmd_len);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, response, complete_cb);
+}
+
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->configured);
- memcpy(p_msch->cbw.command, &cmd_read_capacity10, p_msch->cbw.cmd_len);
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_data) );
+ cbw.total_bytes = 0;
+ cbw.dir = TUSB_DIR_OUT;
+ cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
+ cbw.command[0] = SCSI_CMD_TEST_UNIT_READY;
+ cbw.command[1] = lun; // according to wiki TODO need verification
- return TUSB_ERROR_NONE;
+ return tuh_msc_scsi_command(dev_addr, &cbw, NULL, complete_cb);
}
-tusb_error_t tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, uint8_t *p_data)
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb)
{
- (void) lun; // TODO [MSCH] multiple lun support
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ cbw.total_bytes = 18; // TODO sense response
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_request_sense_t);
- //------------- Command Block Wrapper -------------//
- p_msch->cbw.total_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 =
+ scsi_request_sense_t const cmd_request_sense =
{
- .cmd_code = SCSI_CMD_REQUEST_SENSE,
- .alloc_length = 18
+ .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(dev_addr, p_msch, p_data) );
+ memcpy(cbw.command, &cmd_request_sense, cbw.cmd_len);
- return TUSB_ERROR_NONE;
+ return tuh_msc_scsi_command(dev_addr, &cbw, resposne, complete_cb);
}
-tusb_error_t tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, msc_csw_t * p_csw)
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
+
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
+
+ cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+ cbw.dir = TUSB_DIR_IN_MASK;
+ cbw.cmd_len = sizeof(scsi_read10_t);
+
+ scsi_read10_t const cmd_read10 =
+ {
+ .cmd_code = SCSI_CMD_READ_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
+ };
+
+ memcpy(cbw.command, &cmd_read10, cbw.cmd_len);
+
+ return tuh_msc_scsi_command(dev_addr, &cbw, buffer, complete_cb);
+}
+
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb)
{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_VERIFY(p_msc->mounted);
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
+ msc_cbw_t cbw;
+ cbw_init(&cbw, lun);
- p_msch->cbw.total_bytes = 0; // Number of bytes
- p_msch->cbw.dir = TUSB_DIR_OUT;
- p_msch->cbw.cmd_len = sizeof(scsi_test_unit_ready_t);
+ cbw.total_bytes = block_count*p_msc->capacity[lun].block_size;
+ cbw.dir = TUSB_DIR_OUT;
+ cbw.cmd_len = sizeof(scsi_write10_t);
- //------------- SCSI command -------------//
- scsi_test_unit_ready_t cmd_test_unit_ready =
+ scsi_write10_t const cmd_write10 =
{
- .cmd_code = SCSI_CMD_TEST_UNIT_READY,
- .lun = lun // according to wiki
+ .cmd_code = SCSI_CMD_WRITE_10,
+ .lba = tu_htonl(lba),
+ .block_count = tu_htons(block_count)
};
- memcpy(p_msch->cbw.command, &cmd_test_unit_ready, p_msch->cbw.cmd_len);
-
- // TODO MSCH refractor test uinit ready
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_out, (uint8_t*) &p_msch->cbw, sizeof(msc_cbw_t), false), TUSB_ERROR_FAILED );
- TU_ASSERT( hcd_pipe_xfer(dev_addr, p_msch->ep_in , (uint8_t*) p_csw, sizeof(msc_csw_t), true), TUSB_ERROR_FAILED );
+ memcpy(cbw.command, &cmd_write10, cbw.cmd_len);
- return TUSB_ERROR_NONE;
+ return tuh_msc_scsi_command(dev_addr, &cbw, (void*) buffer, complete_cb);
}
-tusb_error_t tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * p_buffer, uint32_t lba, uint16_t block_count)
+#if 0
+// MSC interface Reset (not used now)
+bool tuh_msc_reset(uint8_t dev_addr)
{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
-
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
-
- p_msch->cbw.total_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 =
+ tusb_control_request_t const new_request =
{
- .cmd_code = SCSI_CMD_READ_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_OUT
+ },
+ .bRequest = MSC_REQ_RESET,
+ .wValue = 0,
+ .wIndex = p_msc->itf_num,
+ .wLength = 0
};
+ TU_ASSERT( usbh_control_xfer( dev_addr, &new_request, NULL ) );
+}
+#endif
+
+//--------------------------------------------------------------------+
+// CLASS-USBH API
+//--------------------------------------------------------------------+
+void msch_init(void)
+{
+ tu_memclr(_msch_itf, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
+}
- memcpy(p_msch->cbw.command, &cmd_read10, p_msch->cbw.cmd_len);
+void msch_close(uint8_t dev_addr)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, p_buffer));
+ // invoke Application Callback
+ if (p_msc->mounted && tuh_msc_umount_cb) tuh_msc_umount_cb(dev_addr);
- return TUSB_ERROR_NONE;
+ tu_memclr(p_msc, sizeof(msch_interface_t));
}
-tusb_error_t tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * p_buffer, uint32_t lba, uint16_t block_count)
+bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
{
- msch_interface_t* p_msch = &msch_data[dev_addr-1];
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ msc_cbw_t const * cbw = &p_msc->cbw;
+ msc_csw_t * csw = &p_msc->csw;
- //------------- Command Block Wrapper -------------//
- msc_cbw_add_signature(&p_msch->cbw, lun);
+ switch (p_msc->stage)
+ {
+ case MSC_STAGE_CMD:
+ // Must be Command Block
+ TU_ASSERT(ep_addr == p_msc->ep_out && event == XFER_RESULT_SUCCESS && xferred_bytes == sizeof(msc_cbw_t));
- p_msch->cbw.total_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);
+ if ( cbw->total_bytes && p_msc->buffer )
+ {
+ // Data stage if any
+ p_msc->stage = MSC_STAGE_DATA;
- //------------- SCSI command -------------//
- scsi_write10_t cmd_write10 =
- {
- .cmd_code = SCSI_CMD_WRITE_10,
- .lba = tu_htonl(lba),
- .block_count = tu_htons(block_count)
- };
+ uint8_t const ep_data = (cbw->dir & TUSB_DIR_IN_MASK) ? p_msc->ep_in : p_msc->ep_out;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, ep_data, p_msc->buffer, cbw->total_bytes));
+ }else
+ {
+ // Status stage
+ p_msc->stage = MSC_STAGE_STATUS;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
+ }
+ break;
- memcpy(p_msch->cbw.command, &cmd_write10, p_msch->cbw.cmd_len);
+ case MSC_STAGE_DATA:
+ // Status stage
+ p_msc->stage = MSC_STAGE_STATUS;
+ TU_ASSERT(usbh_edpt_xfer(dev_addr, p_msc->ep_in, (uint8_t*) &p_msc->csw, sizeof(msc_csw_t)));
+ break;
- TU_ASSERT_ERR ( msch_command_xfer(dev_addr, p_msch, (void*) p_buffer));
+ case MSC_STAGE_STATUS:
+ // SCSI op is complete
+ p_msc->stage = MSC_STAGE_IDLE;
- return TUSB_ERROR_NONE;
+ if (p_msc->complete_cb) p_msc->complete_cb(dev_addr, cbw, csw);
+ break;
+
+ // unknown state
+ default: break;
+ }
+
+ return true;
}
//--------------------------------------------------------------------+
-// CLASS-USBH API (don't require to verify parameters)
+// MSC Enumeration
//--------------------------------------------------------------------+
-void msch_init(void)
-{
- tu_memclr(msch_data, sizeof(msch_interface_t)*CFG_TUSB_HOST_DEVICE_MAX);
- msch_sem_hdl = osal_semaphore_create(&msch_sem_def);
-}
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length)
+static bool config_get_maxlun_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
+static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
+
+bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
{
- TU_VERIFY (MSC_SUBCLASS_SCSI == itf_desc->bInterfaceSubClass &&
- MSC_PROTOCOL_BOT == itf_desc->bInterfaceProtocol);
+ TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
+ MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
- msch_interface_t* p_msc = &msch_data[dev_addr-1];
+ // msc driver length is fixed
+ uint16_t const drv_len = sizeof(tusb_desc_interface_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
+ TU_ASSERT(drv_len <= max_len);
- //------------- Open Data Pipe -------------//
- tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc);
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ tusb_desc_endpoint_t const * ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(desc_itf);
for(uint32_t i=0; i<2; i++)
{
- TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType);
- TU_ASSERT(TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
-
- TU_ASSERT(hcd_edpt_open(rhport, dev_addr, ep_desc));
+ TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
+ TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
{
@@ -306,108 +388,101 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it
ep_desc = (tusb_desc_endpoint_t const *) tu_desc_next(ep_desc);
}
- p_msc->itf_numr = itf_desc->bInterfaceNumber;
- (*p_length) += sizeof(tusb_desc_interface_t) + 2*sizeof(tusb_desc_endpoint_t);
+ p_msc->itf_num = desc_itf->bInterfaceNumber;
- //------------- Get Max Lun -------------//
- tusb_control_request_t request = {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_IN },
- .bRequest = MSC_REQ_GET_MAX_LUN,
- .wValue = 0,
- .wIndex = p_msc->itf_numr,
- .wLength = 1
- };
- // TODO STALL means zero
- TU_ASSERT( usbh_control_xfer( dev_addr, &request, msch_buffer ) );
- p_msc->max_lun = msch_buffer[0];
-
-#if 0
- //------------- Reset -------------//
- request = (tusb_control_request_t) {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_INTERFACE, .type = TUSB_REQ_TYPE_CLASS, .direction = TUSB_DIR_OUT },
- .bRequest = MSC_REQ_RESET,
- .wValue = 0,
- .wIndex = p_msc->itf_numr,
- .wLength = 0
- };
- TU_ASSERT( usbh_control_xfer( dev_addr, &request, NULL ) );
-#endif
-
- enum { SCSI_XFER_TIMEOUT = 2000 };
- //------------- SCSI Inquiry -------------//
- tusbh_msc_inquiry(dev_addr, 0, msch_buffer);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) );
+ return true;
+}
- memcpy(p_msc->vendor_id , ((scsi_inquiry_resp_t*) msch_buffer)->vendor_id , 8);
- memcpy(p_msc->product_id, ((scsi_inquiry_resp_t*) msch_buffer)->product_id, 16);
+bool msch_set_config(uint8_t dev_addr, uint8_t itf_num)
+{
+ msch_interface_t* p_msc = get_itf(dev_addr);
+ TU_ASSERT(p_msc->itf_num == itf_num);
- //------------- SCSI Read Capacity 10 -------------//
- tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
+ p_msc->configured = true;
- // 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_edpt_stalled(dev_addr, p_msc->ep_in) )
+ //------------- Get Max Lun -------------//
+ TU_LOG2("MSC Get Max Lun\r\n");
+ tusb_control_request_t request =
{
- // clear stall TODO abstract clear stall function
- request = (tusb_control_request_t) {
- .bmRequestType_bit = { .recipient = TUSB_REQ_RCPT_ENDPOINT, .type = TUSB_REQ_TYPE_STANDARD, .direction = TUSB_DIR_OUT },
- .bRequest = TUSB_REQ_CLEAR_FEATURE,
- .wValue = 0,
- .wIndex = p_msc->ep_in,
- .wLength = 0
- };
-
- TU_ASSERT(usbh_control_xfer( dev_addr, &request, NULL ));
-
- hcd_edpt_clear_stall(dev_addr, p_msc->ep_in);
- TU_ASSERT( osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT) ); // wait for SCSI status
+ .bmRequestType_bit =
+ {
+ .recipient = TUSB_REQ_RCPT_INTERFACE,
+ .type = TUSB_REQ_TYPE_CLASS,
+ .direction = TUSB_DIR_IN
+ },
+ .bRequest = MSC_REQ_GET_MAX_LUN,
+ .wValue = 0,
+ .wIndex = itf_num,
+ .wLength = 1
+ };
+ TU_ASSERT(tuh_control_xfer(dev_addr, &request, &p_msc->max_lun, config_get_maxlun_complete));
- //------------- SCSI Request Sense -------------//
- (void) tuh_msc_request_sense(dev_addr, 0, msch_buffer);
- TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
+ return true;
+}
- //------------- Re-read SCSI Read Capactity -------------//
- tusbh_msc_read_capacity10(dev_addr, 0, msch_buffer);
- TU_ASSERT(osal_semaphore_wait(msch_sem_hdl, SCSI_XFER_TIMEOUT));
- }
+static bool config_get_maxlun_complete (uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
+{
+ (void) request;
- p_msc->last_lba = tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->last_lba );
- p_msc->block_size = (uint16_t) tu_ntohl( ((scsi_read_capacity10_resp_t*)msch_buffer)->block_size );
+ msch_interface_t* p_msc = get_itf(dev_addr);
- p_msc->is_initialized = true;
+ // STALL means zero
+ p_msc->max_lun = (XFER_RESULT_SUCCESS == result) ? _msch_buffer[0] : 0;
+ p_msc->max_lun++; // MAX LUN is minus 1 by specs
- tuh_msc_mounted_cb(dev_addr);
+ // TODO multiple LUN support
+ TU_LOG2("SCSI Test Unit Ready\r\n");
+ uint8_t const lun = 0;
+ tuh_msc_test_unit_ready(dev_addr, lun, config_test_unit_ready_complete);
return true;
}
-void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
+static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
{
- msch_interface_t* p_msc = &msch_data[dev_addr-1];
- if ( ep_addr == p_msc->ep_in )
+ if (csw->status == 0)
{
- if (p_msc->is_initialized)
- {
- tuh_msc_isr(dev_addr, event, xferred_bytes);
- }else
- { // still initializing under open subtask
- osal_semaphore_post(msch_sem_hdl, true);
- }
+ // Unit is ready, read its capacity
+ TU_LOG2("SCSI Read Capacity\r\n");
+ tuh_msc_read_capacity(dev_addr, cbw->lun, (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer), config_read_capacity_complete);
+ }else
+ {
+ // Note: During enumeration, some device fails Test Unit Ready and require a few retries
+ // with Request Sense to start working !!
+ // TODO limit number of retries
+ TU_LOG2("SCSI Request Sense\r\n");
+ TU_ASSERT(tuh_msc_request_sense(dev_addr, cbw->lun, _msch_buffer, config_request_sense_complete));
}
+
+ return true;
}
-void msch_close(uint8_t dev_addr)
+static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
{
- tu_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
+ TU_ASSERT(csw->status == 0);
+ TU_ASSERT(tuh_msc_test_unit_ready(dev_addr, cbw->lun, config_test_unit_ready_complete));
+ return true;
}
-//--------------------------------------------------------------------+
-// INTERNAL & HELPER
-//--------------------------------------------------------------------+
+static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
+{
+ TU_ASSERT(csw->status == 0);
+ msch_interface_t* p_msc = get_itf(dev_addr);
+
+ // Capacity response field: Block size and Last LBA are both Big-Endian
+ scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer);
+ p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
+ p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
+
+ // Mark enumeration is complete
+ p_msc->mounted = true;
+ if (tuh_msc_mount_cb) tuh_msc_mount_cb(dev_addr);
+
+ // notify usbh that driver enumeration is complete
+ usbh_driver_set_config_complete(dev_addr, p_msc->itf_num);
+
+ return true;
+}
#endif
diff --git a/src/class/msc/msc_host.h b/src/class/msc/msc_host.h
index 76cf86853..7718ad4fe 100644
--- a/src/class/msc/msc_host.h
+++ b/src/class/msc/msc_host.h
@@ -27,180 +27,93 @@
#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
+// Class Driver Configuration
//--------------------------------------------------------------------+
-/** \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);
-/** \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);
+#ifndef CFG_TUH_MSC_MAXLUN
+#define CFG_TUH_MSC_MAXLUN 4
+#endif
-/** \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);
+typedef bool (*tuh_msc_complete_cb_t)(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
-/** \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);
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
-/** \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);
+// Check if device supports MassStorage interface.
+// This function true after tuh_msc_mounted_cb() and false after tuh_msc_unmounted_cb()
+bool tuh_msc_mounted(uint8_t dev_addr);
-/** \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_MEM_SECTION)
- * \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);
+// Check if the interface is currently ready or busy transferring data
+bool tuh_msc_ready(uint8_t dev_addr);
-/** \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_MEM_SECTION)
- * \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);
+// Get Max Lun
+uint8_t tuh_msc_get_maxlun(uint8_t dev_addr);
-/** \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_MEM_SECTION)
- * \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);
+// Get number of block
+uint32_t tuh_msc_get_block_count(uint8_t dev_addr, uint8_t lun);
-/** \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); // TODO to be refractor
+// Get block size in bytes
+uint32_t tuh_msc_get_block_size(uint8_t dev_addr, uint8_t lun);
-//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);
+// Perform a full SCSI command (cbw, data, csw) in non-blocking manner.
+// Complete callback is invoked when SCSI op is complete.
+// return true if success, false if there is already pending operation.
+bool tuh_msc_scsi_command(uint8_t dev_addr, msc_cbw_t const* cbw, void* data, tuh_msc_complete_cb_t complete_cb);
-//------------- 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);
+// Perform SCSI Inquiry command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_inquiry(uint8_t dev_addr, uint8_t lun, scsi_inquiry_resp_t* response, tuh_msc_complete_cb_t complete_cb);
-/** \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);
+// Perform SCSI Test Unit Ready command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_test_unit_ready(uint8_t dev_addr, uint8_t lun, tuh_msc_complete_cb_t complete_cb);
-/** \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 xfer_result_t
- * \param[in] xferred_bytes Number of bytes transferred via USB bus
- * \note event can be one of following
- * - XFER_RESULT_SUCCESS : previously scheduled transfer completes successfully.
- * - XFER_RESULT_FAILED : previously scheduled transfer encountered a transaction error.
- * - XFER_RESULT_STALLED : previously scheduled transfer is stalled by device.
- * \note
- */
-void tuh_msc_isr(uint8_t dev_addr, xfer_result_t event, uint32_t xferred_bytes);
+// Perform SCSI Request Sense 10 command
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_request_sense(uint8_t dev_addr, uint8_t lun, void *resposne, tuh_msc_complete_cb_t complete_cb);
+// Perform SCSI Read 10 command. Read n blocks starting from LBA to buffer
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_read10(uint8_t dev_addr, uint8_t lun, void * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb);
-//--------------------------------------------------------------------+
-// Internal Class Driver API
-//--------------------------------------------------------------------+
-typedef struct
-{
- uint8_t itf_numr;
- uint8_t ep_in;
- uint8_t ep_out;
+// Perform SCSI Write 10 command. Write n blocks starting from LBA to device
+// Complete callback is invoked when SCSI op is complete.
+bool tuh_msc_write10(uint8_t dev_addr, uint8_t lun, void const * buffer, uint32_t lba, uint16_t block_count, tuh_msc_complete_cb_t complete_cb);
- uint8_t max_lun;
- uint16_t block_size;
- uint32_t last_lba; // last logical block address
+// Perform SCSI Read Capacity 10 command
+// Complete callback is invoked when SCSI op is complete.
+// Note: during enumeration, host stack already carried out this request. Application can retrieve capacity by
+// simply call tuh_msc_get_block_count() and tuh_msc_get_block_size()
+bool tuh_msc_read_capacity(uint8_t dev_addr, uint8_t lun, scsi_read_capacity10_resp_t* response, tuh_msc_complete_cb_t complete_cb);
- volatile bool is_initialized;
- uint8_t vendor_id[8];
- uint8_t product_id[16];
+//------------- Application Callback -------------//
- msc_cbw_t cbw;
- msc_csw_t csw;
-}msch_interface_t;
+// Invoked when a device with MassStorage interface is mounted
+TU_ATTR_WEAK void tuh_msc_mount_cb(uint8_t dev_addr);
-void msch_init(void);
-bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t *p_length);
-void msch_isr(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
-void msch_close(uint8_t dev_addr);
+// Invoked when a device with MassStorage interface is unmounted
+TU_ATTR_WEAK void tuh_msc_umount_cb(uint8_t dev_addr);
+
+//--------------------------------------------------------------------+
+// Internal Class Driver API
+//--------------------------------------------------------------------+
+
+void msch_init (void);
+bool msch_open (uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
+bool msch_set_config (uint8_t dev_addr, uint8_t itf_num);
+void msch_close (uint8_t dev_addr);
+bool msch_xfer_cb (uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_MSC_HOST_H_ */
-
-/// @}
-/// @}