diff --git a/examples/device/cdc_msc_hid/src/msc_disk.c b/examples/device/cdc_msc_hid/src/msc_disk.c index 5c91e7da7..970f0adc9 100644 --- a/examples/device/cdc_msc_hid/src/msc_disk.c +++ b/examples/device/cdc_msc_hid/src/msc_disk.c @@ -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; diff --git a/examples/device/cdc_msc_hid_freertos/src/msc_disk.c b/examples/device/cdc_msc_hid_freertos/src/msc_disk.c index c60558ea8..3d9103bda 100644 --- a/examples/device/cdc_msc_hid_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_hid_freertos/src/msc_disk.c @@ -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; diff --git a/examples/device/msc_dual_lun/src/msc_disk_dual.c b/examples/device/msc_dual_lun/src/msc_disk_dual.c index 780705db2..48f5cfb18 100644 --- a/examples/device/msc_dual_lun/src/msc_disk_dual.c +++ b/examples/device/msc_dual_lun/src/msc_disk_dual.c @@ -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; diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 5f472c39f..fcb118a67 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -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; + } } } } diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index 068bea3ef..cbaea4bb8 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -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);