add Test Unit Ready to builtin command, add tud_msc_test_unit_ready_cb()

- rename tud_msc_maxlun_cb to tud_msc_get_maxlun_cb
This commit is contained in:
hathach 2019-05-06 15:01:38 +07:00
parent fae9aba68f
commit efefbd3a4e
5 changed files with 133 additions and 115 deletions

View File

@ -118,16 +118,6 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
README_CONTENTS
};
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun;
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
@ -143,6 +133,25 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16
memcpy(product_rev, rev, strlen(rev));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
(void) lun;
return true; // RAM disk is always ready
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun;
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
@ -186,11 +195,6 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
switch (scsi_cmd[0])
{
case SCSI_CMD_TEST_UNIT_READY:
// Command that host uses to check our readiness before sending other commands
resplen = 0;
break;
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
resplen = 0;

View File

@ -118,16 +118,6 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
README_CONTENTS
};
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun;
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
@ -143,6 +133,25 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16
memcpy(product_rev, rev, strlen(rev));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
(void) lun;
return true; // RAM disk is always ready
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun;
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
@ -186,11 +195,6 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
switch (scsi_cmd[0])
{
case SCSI_CMD_TEST_UNIT_READY:
// Command that host uses to check our readiness before sending other commands
resplen = 0;
break;
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
resplen = 0;

View File

@ -205,20 +205,11 @@ uint8_t msc_disk1[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
};
// Invoked to determine max LUN
uint8_t tud_msc_maxlun_cb(void)
uint8_t tud_msc_get_maxlun_cb(void)
{
return 2; // dual LUN
}
// Callback invoked to determine disk's size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun; // both LUNs have same size
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
@ -234,6 +225,24 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16
memcpy(product_rev, rev, strlen(rev));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
(void) lun;
return true; // RAM disk is always ready
}
// Callback invoked to determine disk's size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
{
(void) lun; // both LUNs have same size
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
@ -273,11 +282,6 @@ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer,
switch (scsi_cmd[0])
{
case SCSI_CMD_TEST_UNIT_READY:
// Command that host uses to check our readiness before sending other commands
resplen = 0;
break;
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
resplen = 0;

View File

@ -159,7 +159,7 @@ bool mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_reque
case MSC_REQ_GET_MAX_LUN:
{
uint8_t maxlun = 1;
if (tud_msc_maxlun_cb) maxlun = tud_msc_maxlun_cb();
if (tud_msc_get_maxlun_cb) maxlun = tud_msc_get_maxlun_cb();
TU_VERIFY(maxlun);
// MAX LUN is minus 1 by specs
@ -186,14 +186,27 @@ bool mscd_control_request_complete(uint8_t rhport, tusb_control_request_t const
return true;
}
// return length of response (copied to buffer), -1 if it is not an built-in commands
int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t bufsize)
// 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 ret;
int32_t resplen;
switch ( p_cbw->command[0] )
switch ( scsi_cmd[0] )
{
case SCSI_CMD_TEST_UNIT_READY:
resplen = 0;
if ( !tud_msc_test_unit_ready_cb(lun) )
{
// not ready response with Failed status and sense key = not ready
resplen = - 1;
// Logical Unit Not Ready, Cause Not Reportable
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x04, 0x00);
}
break;
case SCSI_CMD_READ_CAPACITY_10:
{
scsi_read_capacity10_resp_t read_capa10;
@ -202,14 +215,14 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
uint32_t block_size;
uint16_t block_size_u16;
tud_msc_capacity_cb(p_cbw->lun, &block_count, &block_size_u16);
tud_msc_capacity_cb(lun, &block_count, &block_size_u16);
block_size = (uint32_t) block_size_u16;
read_capa10.last_lba = ENDIAN_BE(block_count-1);
read_capa10.block_size = ENDIAN_BE(block_size);
ret = sizeof(read_capa10);
memcpy(buffer, &read_capa10, ret);
resplen = sizeof(read_capa10);
memcpy(buffer, &read_capa10, resplen);
}
break;
@ -226,12 +239,12 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
uint32_t block_count;
uint16_t block_size;
tud_msc_capacity_cb(p_cbw->lun, &block_count, &block_size);
tud_msc_capacity_cb(lun, &block_count, &block_size);
read_fmt_capa.block_num = ENDIAN_BE(block_count);
read_fmt_capa.block_size_u16 = ENDIAN_BE16(block_size);
ret = sizeof(read_fmt_capa);
memcpy(buffer, &read_fmt_capa, ret);
resplen = sizeof(read_fmt_capa);
memcpy(buffer, &read_fmt_capa, resplen);
}
break;
@ -249,10 +262,10 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
memset(inquiry_rsp.product_id , ' ', sizeof(inquiry_rsp.product_id));
memset(inquiry_rsp.product_rev, ' ', sizeof(inquiry_rsp.product_rev));
tud_msc_inquiry_cb(p_cbw->lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
tud_msc_inquiry_cb(lun, inquiry_rsp.vendor_id, inquiry_rsp.product_id, inquiry_rsp.product_rev);
ret = sizeof(inquiry_rsp);
memcpy(buffer, &inquiry_rsp, ret);
resplen = sizeof(inquiry_rsp);
memcpy(buffer, &inquiry_rsp, resplen);
}
break;
@ -269,12 +282,12 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
bool writable = true;
if (tud_msc_is_writable_cb) {
writable = tud_msc_is_writable_cb(p_cbw->lun);
writable = tud_msc_is_writable_cb(lun);
}
mode_resp.write_protected = !writable;
ret = sizeof(mode_resp);
memcpy(buffer, &mode_resp, ret);
resplen = sizeof(mode_resp);
memcpy(buffer, &mode_resp, resplen);
}
break;
@ -292,18 +305,18 @@ int32_t proc_builtin_scsi(msc_cbw_t const * p_cbw, uint8_t* buffer, uint32_t buf
sense_rsp.add_sense_code = _mscd_itf.add_sense_code;
sense_rsp.add_sense_qualifier = _mscd_itf.add_sense_qualifier;
ret = sizeof(sense_rsp);
memcpy(buffer, &sense_rsp, ret);
resplen = sizeof(sense_rsp);
memcpy(buffer, &sense_rsp, resplen);
// Clear sense data after copy
tud_msc_set_sense(p_cbw->lun, 0, 0, 0);
tud_msc_set_sense(lun, 0, 0, 0);
}
break;
default: ret = -1; break;
default: resplen = -1; break;
}
return ret;
return resplen;
}
bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes)
@ -342,60 +355,50 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
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->total_bytes == 0)
// 1. OUT : queue transfer (invoke app callback after done)
// 2. IN & Zero: Process if is built-in, else Invoke app callback. Skip DATA if zero length
if ( (p_cbw->total_bytes > 0 ) && !TU_BIT_TEST(p_cbw->dir, 7) )
{
int32_t const cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, NULL, 0);
p_msc->total_len = 0;
p_msc->stage = MSC_STAGE_STATUS;
if ( cb_result < 0 )
{
p_csw->status = MSC_CSW_STATUS_FAILED;
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
}
else
{
p_csw->status = MSC_CSW_STATUS_PASSED;
}
}
else if ( !TU_BIT_TEST(p_cbw->dir, 7) )
{
// OUT transfer
// queue transfer
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_out, _mscd_buf, p_msc->total_len) );
}
else
}else
{
// IN Transfer
int32_t cb_result;
int32_t resplen;
// first process if it is a built-in commands
cb_result = proc_builtin_scsi(p_cbw, _mscd_buf, sizeof(_mscd_buf));
// First process if it is a built-in commands
resplen = proc_builtin_scsi(p_cbw->lun, p_cbw->command, _mscd_buf, sizeof(_mscd_buf));
// Not an built-in command, invoke user callback
if ( cb_result < 0 )
// Not built-in, invoke user callback
if ( (resplen < 0) && (p_msc->sense_key == 0) )
{
cb_result = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
resplen = tud_msc_scsi_cb(p_cbw->lun, p_cbw->command, _mscd_buf, p_msc->total_len);
}
if ( cb_result > 0 )
{
p_msc->total_len = (uint32_t) cb_result;
p_csw->status = MSC_CSW_STATUS_PASSED;
TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
}else
if ( resplen < 0 )
{
p_msc->total_len = 0;
p_csw->status = MSC_CSW_STATUS_FAILED;
p_msc->stage = MSC_STAGE_STATUS;
tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // Sense = Invalid Command Operation
usbd_edpt_stall(rhport, p_msc->ep_in);
// failed but senskey is not set: default to Illegal Request
if ( p_msc->sense_key == 0 ) tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
/// Stall bulk In if needed
if (p_cbw->total_bytes) usbd_edpt_stall(rhport, p_msc->ep_in);
}
else
{
p_msc->total_len = (uint32_t) resplen;
p_csw->status = MSC_CSW_STATUS_PASSED;
if (p_msc->total_len)
{
TU_ASSERT( p_cbw->total_bytes >= p_msc->total_len ); // cannot return more than host expect
TU_ASSERT( dcd_edpt_xfer(rhport, p_msc->ep_in, _mscd_buf, p_msc->total_len) );
}else
{
p_msc->stage = MSC_STAGE_STATUS;
}
}
}
}

View File

@ -97,12 +97,19 @@ int32_t tud_msc_write10_cb (uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]);
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun);
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size);
/**
* Callback invoked when received an SCSI command not in built-in list below.
* Invoked when received an SCSI command not in built-in list below.
* - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
* - READ10 and WRITE10 has their own callbacks
*
* \param[in] lun Logical unit number
* \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly
* \param[out] buffer Buffer for SCSI Data Stage.
@ -113,17 +120,13 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz
* \return Actual bytes processed, can be zero for no-data command.
* \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding
* endpoint and return failed status in command status wrapper phase.
*
* \note Following command is automatically handled by tinyusb stack, callback should not be worried:
* - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
* - READ10 and WRITE10 has their own callbacks
*/
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize);
/*------------- Optional callbacks -------------*/
// Invoked when received GET_MAX_LUN request
ATTR_WEAK uint8_t tud_msc_maxlun_cb(void);
// Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation
ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
// Invoked when Read10 command is complete
ATTR_WEAK void tud_msc_read10_complete_cb(uint8_t lun);