From d1ee2bf18fced22364537949c4a18e1c15c99b6d Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 29 Jan 2025 15:14:25 +0100 Subject: [PATCH 01/11] Fix Auto speed display. Signed-off-by: HiFiPhile --- src/device/usbd.c | 23 ++++++++++++++++++++--- src/host/usbh.c | 23 ++++++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/device/usbd.c b/src/device/usbd.c index 2a6081673..8435b5c02 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -464,13 +464,30 @@ bool tud_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { return true; // skip if already initialized } TU_ASSERT(rh_init); - - TU_LOG_USBD("USBD init on controller %u, speed = %s\r\n", rhport, - rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full"); +#if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL + char const* speed_str = 0; + switch (rh_init->speed) { + case TUSB_SPEED_HIGH: + speed_str = "High"; + break; + case TUSB_SPEED_FULL: + speed_str = "Full"; + break; + case TUSB_SPEED_LOW: + speed_str = "Low"; + break; + case TUSB_SPEED_AUTO: + speed_str = "Auto"; + break; + default: + break; + } + TU_LOG_USBD("USBD init on controller %u, speed = %s\r\n", rhport, speed_str); TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t)); TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t)); TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t)); TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t)); +#endif tu_varclr(&_usbd_dev); _usbd_queued_setup = 0; diff --git a/src/host/usbh.c b/src/host/usbh.c index a2994cde7..f022d93f1 100644 --- a/src/host/usbh.c +++ b/src/host/usbh.c @@ -365,9 +365,26 @@ bool tuh_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { if (tuh_rhport_is_active(rhport)) { return true; // skip if already initialized } - - TU_LOG_USBH("USBH init on controller %u, speed = %s\r\n", rhport, - rh_init->speed == TUSB_SPEED_HIGH ? "High" : "Full"); +#if CFG_TUSB_DEBUG >= CFG_TUH_LOG_LEVEL + char const* speed_str = 0; + switch (rh_init->speed) { + case TUSB_SPEED_HIGH: + speed_str = "High"; + break; + case TUSB_SPEED_FULL: + speed_str = "Full"; + break; + case TUSB_SPEED_LOW: + speed_str = "Low"; + break; + case TUSB_SPEED_AUTO: + speed_str = "Auto"; + break; + default: + break; + } + TU_LOG_USBH("USBH init on controller %u, speed = %s\r\n", rhport, speed_str); +#endif // Init host stack if not already if (!tuh_inited()) { From cc626f35d212aef86b0aa7275eea016ae69b5de4 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 29 Jan 2025 15:16:02 +0100 Subject: [PATCH 02/11] msc_device: add async IO support. Signed-off-by: HiFiPhile --- src/class/msc/msc_device.c | 182 ++++++++++++++++++++++++++----------- src/class/msc/msc_device.h | 16 ++++ 2 files changed, 146 insertions(+), 52 deletions(-) diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index dd66bfb6f..c7c926f4e 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -52,10 +52,18 @@ enum { MSC_STAGE_NEED_RESET, }; +enum { + MSC_NEXT_OP_NONE = 0, + MSC_NEXT_OP_READ10, + MSC_NEXT_OP_WRITE10, + MSC_NEXT_OP_STATUS +}; + typedef struct { TU_ATTR_ALIGNED(4) msc_cbw_t cbw; TU_ATTR_ALIGNED(4) msc_csw_t csw; + uint8_t rhport; uint8_t itf_num; uint8_t ep_in; uint8_t ep_out; @@ -70,6 +78,10 @@ typedef struct { uint8_t sense_key; uint8_t add_sense_code; uint8_t add_sense_qualifier; +#if CFG_TUD_MSC_ASYNC_IO + uint8_t next_op; + uint32_t xferred_bytes; +#endif }mscd_interface_t; static mscd_interface_t _mscd_itf; @@ -82,31 +94,39 @@ CFG_TUD_MEM_SECTION static struct { // 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 void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes); +static void proc_read10_cmd(mscd_interface_t* p_msc); +static void proc_read10_next(mscd_interface_t* p_msc, int32_t nbytes); +static void proc_write10_cmd(mscd_interface_t* p_msc); +static void proc_write10_new_data(mscd_interface_t* p_msc, uint32_t xferred_bytes); +static void proc_write10_next(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes); +static bool proc_stage_status(mscd_interface_t* p_msc); +#if CFG_TUD_MSC_ASYNC_IO +static void tud_msc_async_io_done_cb(void* bytes_processed); +#endif TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir) { return tu_bit_test(dir, 7); } -static inline bool send_csw(uint8_t rhport, mscd_interface_t* p_msc) { +static inline bool send_csw(mscd_interface_t* p_msc) { // Data residue is always = host expect - actual transferred + uint8_t rhport = p_msc->rhport; p_msc->csw.data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len; p_msc->stage = MSC_STAGE_STATUS_SENT; memcpy(_mscd_epbuf.buf, &p_msc->csw, sizeof(msc_csw_t)); return usbd_edpt_xfer(rhport, p_msc->ep_in , _mscd_epbuf.buf, sizeof(msc_csw_t)); } -static inline bool prepare_cbw(uint8_t rhport, mscd_interface_t* p_msc) { +static inline bool prepare_cbw(mscd_interface_t* p_msc) { + uint8_t rhport = p_msc->rhport; p_msc->stage = MSC_STAGE_CMD; return usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, sizeof(msc_cbw_t)); } -static void fail_scsi_op(uint8_t rhport, mscd_interface_t* p_msc, uint8_t status) { +static void fail_scsi_op(mscd_interface_t* p_msc, uint8_t status) { msc_cbw_t const * p_cbw = &p_msc->cbw; msc_csw_t * p_csw = &p_msc->csw; + uint8_t rhport = p_msc->rhport; p_csw->status = status; p_csw->data_residue = p_msc->cbw.total_bytes - p_msc->xferred_len; @@ -177,6 +197,32 @@ static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) { return status; } +static bool proc_stage_status(mscd_interface_t* p_msc) { + uint8_t rhport = p_msc->rhport; + msc_cbw_t const* p_cbw = &p_msc->cbw; + // skip status if epin is currently stalled, will do it when received Clear Stall request + if (!usbd_edpt_stalled(rhport, p_msc->ep_in)) { + if ((p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir)) { + // 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status + // TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len); + usbd_edpt_stall(rhport, p_msc->ep_in); + } else { + TU_ASSERT(send_csw(p_msc)); + } + } + + #if TU_CHECK_MCU(OPT_MCU_CXD56) + // WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD. + // There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and + // hope everything will work + if ( usbd_edpt_stalled(rhport, p_msc->ep_in) ) { + usbd_edpt_clear_stall(rhport, p_msc->ep_in); + send_csw(p_msc); + } + #endif + return true; +} + //--------------------------------------------------------------------+ // Debug //--------------------------------------------------------------------+ @@ -219,6 +265,32 @@ static inline void set_sense_medium_not_present(uint8_t lun) { tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00); } +#if CFG_TUD_MSC_ASYNC_IO +void tud_msc_async_io_done(int32_t bytes_processed) { + // Precheck to avoid queueing multiple RW done callback + TU_VERIFY(_mscd_itf.next_op != MSC_NEXT_OP_NONE,); + // Call usbd_edpt_xfer() in tud_task() to avoid racing condition + usbd_defer_func(tud_msc_async_io_done_cb, (void*) bytes_processed, false); +} + +static void tud_msc_async_io_done_cb(void* bytes_processed) { + TU_VERIFY(_mscd_itf.next_op != MSC_NEXT_OP_NONE,); + uint8_t next_op = _mscd_itf.next_op; + _mscd_itf.next_op = MSC_NEXT_OP_NONE; + int32_t nbytes = (int32_t)bytes_processed; + // READ10 + if (next_op == MSC_NEXT_OP_READ10) { + proc_read10_next(&_mscd_itf, nbytes); + } else if (next_op == MSC_NEXT_OP_WRITE10) { + proc_write10_next(&_mscd_itf, _mscd_itf.xferred_bytes, nbytes); + // Need to manually invoke CSW transfer + if (_mscd_itf.stage == MSC_STAGE_STATUS) { + proc_stage_status(&_mscd_itf); + } + } +} +#endif + //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ @@ -245,12 +317,13 @@ uint16_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1 mscd_interface_t * p_msc = &_mscd_itf; p_msc->itf_num = itf_desc->bInterfaceNumber; + p_msc->rhport = rhport; // 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(prepare_cbw(rhport, p_msc), drv_len); + TU_ASSERT(prepare_cbw(p_msc), drv_len); return drv_len; } @@ -289,14 +362,14 @@ bool mscd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t if (ep_addr == p_msc->ep_in) { if (p_msc->stage == MSC_STAGE_STATUS) { // resume sending SCSI status if we are in this stage previously before stalled - TU_ASSERT(send_csw(rhport, p_msc)); + TU_ASSERT(send_csw(p_msc)); } } else if (ep_addr == p_msc->ep_out) { if (p_msc->stage == MSC_STAGE_CMD) { // part of reset recovery (probably due to invalid CBW) -> prepare for new command // Note: skip if already queued previously if (usbd_edpt_ready(rhport, p_msc->ep_out)) { - TU_ASSERT(prepare_cbw(rhport, p_msc)); + TU_ASSERT(prepare_cbw(p_msc)); } } } @@ -344,7 +417,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t msc_csw_t * p_csw = &p_msc->csw; switch (p_msc->stage) { - case MSC_STAGE_CMD: + case MSC_STAGE_CMD: { //------------- new CBW received -------------// // Complete IN while waiting for CMD is usually Status of previous SCSI op, ignore it if (ep_addr != p_msc->ep_out) { @@ -382,12 +455,12 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t uint8_t const status = rdwr10_validate_cmd(p_cbw); if (status != MSC_CSW_STATUS_PASSED) { - fail_scsi_op(rhport, p_msc, status); + fail_scsi_op(p_msc, status); } else if (p_cbw->total_bytes) { if (SCSI_CMD_READ_10 == p_cbw->command[0]) { - proc_read10_cmd(rhport, p_msc); + proc_read10_cmd(p_msc); } else { - proc_write10_cmd(rhport, p_msc); + proc_write10_cmd(p_msc); } } else { // no data transfer, only exist in complaint test suite @@ -400,7 +473,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if ((p_cbw->total_bytes > 0) && !is_data_in(p_cbw->dir)) { if (p_cbw->total_bytes > CFG_TUD_MSC_EP_BUFSIZE) { TU_LOG_DRV(" SCSI reject non READ10/WRITE10 with large data\r\n"); - fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); } else { // Didn't check for case 9 (Ho > Dn), which requires examining scsi command first // but it is OK to just receive data then responded with failed status @@ -418,12 +491,12 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if (resplen < 0) { // unsupported command TU_LOG_DRV(" SCSI unsupported or failed command\r\n"); - fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); } else if (resplen == 0) { if (p_cbw->total_bytes) { // 6.7 The 13 Cases: case 4 (Hi > Dn) // TU_LOG(MSC_DEBUG, " SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes); - fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); } else { // case 1 Hn = Dn: all good p_msc->stage = MSC_STAGE_STATUS; @@ -432,7 +505,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if (p_cbw->total_bytes == 0) { // 6.7 The 13 Cases: case 2 (Hn < Di) // TU_LOG(MSC_DEBUG, " SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes); - fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); } else { // cannot return more than host expect p_msc->total_len = tu_min32((uint32_t)resplen, p_cbw->total_bytes); @@ -441,6 +514,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t } } } + } break; case MSC_STAGE_DATA: @@ -454,10 +528,10 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t // Data Stage is complete p_msc->stage = MSC_STAGE_STATUS; }else { - proc_read10_cmd(rhport, p_msc); + proc_read10_cmd(p_msc); } } else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) { - proc_write10_new_data(rhport, p_msc, xferred_bytes); + proc_write10_new_data(p_msc, xferred_bytes); } else { p_msc->xferred_len += xferred_bytes; @@ -468,7 +542,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t if ( cb_result < 0 ) { // unsupported command TU_LOG_DRV(" SCSI unsupported command\r\n"); - fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); }else { // TODO haven't implement this scenario any further yet } @@ -517,7 +591,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; } - TU_ASSERT(prepare_cbw(rhport, p_msc)); + TU_ASSERT(prepare_cbw(p_msc)); } else { // Any xfer ended here is consider unknown error, ignore it TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n"); @@ -528,26 +602,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t } if (p_msc->stage == MSC_STAGE_STATUS) { - // skip status if epin is currently stalled, will do it when received Clear Stall request - if (!usbd_edpt_stalled(rhport, p_msc->ep_in)) { - if ((p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir)) { - // 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status - // TU_LOG(MSC_DEBUG, " SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len); - usbd_edpt_stall(rhport, p_msc->ep_in); - } else { - TU_ASSERT(send_csw(rhport, p_msc)); - } - } - - #if TU_CHECK_MCU(OPT_MCU_CXD56) - // WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD. - // There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and - // hope everything will work - if ( usbd_edpt_stalled(rhport, p_msc->ep_in) ) { - usbd_edpt_clear_stall(rhport, p_msc->ep_in); - send_csw(rhport, p_msc); - } - #endif + TU_ASSERT(proc_stage_status(p_msc)); } return true; @@ -751,7 +806,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ return resplen; } -static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) { +static void proc_read10_cmd(mscd_interface_t* p_msc) { msc_cbw_t const* p_cbw = &p_msc->cbw; // block size already verified not zero @@ -765,16 +820,27 @@ static void proc_read10_cmd(uint8_t rhport, mscd_interface_t* p_msc) { // Application can consume smaller bytes uint32_t const offset = p_msc->xferred_len % block_sz; - nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes); +#if CFG_TUD_MSC_ASYNC_IO + p_msc->next_op = MSC_NEXT_OP_READ10; + tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes); +#else + nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes); + proc_read10_next(p_msc, nbytes); +#endif +} + +static void proc_read10_next(mscd_interface_t* p_msc, int32_t nbytes) { + uint8_t rhport = p_msc->rhport; if (nbytes < 0) { // negative means error -> endpoint is stalled & status in CSW set to failed TU_LOG_DRV(" tud_msc_read10_cb() return -1\r\n"); // set sense + msc_cbw_t const* p_cbw = &p_msc->cbw; set_sense_medium_not_present(p_cbw->lun); - fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); } else if (nbytes == 0) { // zero means not ready -> simulate an transfer complete so that this driver callback will fired again dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false); @@ -783,7 +849,7 @@ 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 void proc_write10_cmd(mscd_interface_t* p_msc) { msc_cbw_t const* p_cbw = &p_msc->cbw; bool writable = true; @@ -795,19 +861,19 @@ static void proc_write10_cmd(uint8_t rhport, mscd_interface_t* p_msc) { // Not writable, complete this SCSI op with error // Sense = Write protected tud_msc_set_sense(p_cbw->lun, SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); - fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); return; } // remaining bytes capped at class buffer uint16_t nbytes = (uint16_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len); - // Write10 callback will be called later when usb transfer complete + uint8_t rhport = p_msc->rhport; TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),); } // process new data arrived from WRITE10 -static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint32_t xferred_bytes) { +static void proc_write10_new_data(mscd_interface_t* p_msc, uint32_t xferred_bytes) { msc_cbw_t const* p_cbw = &p_msc->cbw; // block size already verified not zero @@ -818,8 +884,18 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3 // Invoke callback to consume new data uint32_t const offset = p_msc->xferred_len % block_sz; - int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes); +#if CFG_TUD_MSC_ASYNC_IO + p_msc->next_op = MSC_NEXT_OP_WRITE10; + p_msc->xferred_bytes = xferred_bytes; + tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes); +#else + int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes); + proc_write10_next(p_msc, xferred_bytes, nbytes); +#endif +} + +static void proc_write10_next(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes) { if (nbytes < 0) { // negative means error -> failed this scsi op TU_LOG_DRV(" tud_msc_write10_cb() return -1\r\n"); @@ -828,9 +904,10 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3 p_msc->xferred_len += xferred_bytes; // Set sense + msc_cbw_t const* p_cbw = &p_msc->cbw; set_sense_medium_not_present(p_cbw->lun); - fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); } else { // Application consume less than what we got (including zero) if ((uint32_t)nbytes < xferred_bytes) { @@ -841,6 +918,7 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3 } // simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter + uint8_t rhport = p_msc->rhport; dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false); } else { // Application consume all bytes in our buffer @@ -851,7 +929,7 @@ static void proc_write10_new_data(uint8_t rhport, mscd_interface_t* p_msc, uint3 p_msc->stage = MSC_STAGE_STATUS; } else { // prepare to receive more data from host - proc_write10_cmd(rhport, p_msc); + proc_write10_cmd(p_msc); } } } diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index 29acd280a..407fe241c 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -48,6 +48,11 @@ #error CFG_TUD_MSC_EP_BUFSIZE must be defined, value of a block size should work well, the more the better #endif +// Enable asynchronous read/write, once operation is finished tud_msc_async_io_done() must be called +#ifndef CFG_TUD_MSC_ASYNC_IO +#define CFG_TUD_MSC_ASYNC_IO 0 +#endif + TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct"); //--------------------------------------------------------------------+ @@ -73,6 +78,9 @@ bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, u // // - read < 0 : Indicate application error e.g invalid address. This request will be STALLed // and return failed status in command status wrapper phase. +// +// - In case of asynchronous IO enabled, application should passing reading parameters to background IO +// task and return immediately. Once reading is done, tud_msc_async_io_done() must be called. int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); // Invoked when received SCSI WRITE10 command @@ -88,6 +96,8 @@ int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buf // - write < 0 : Indicate application error e.g invalid address. This request will be STALLed // and return failed status in command status wrapper phase. // +// - In case of asynchronous IO enabled, application should passing writing parameters to background IO +// task and return immediately. Once writing is done, tud_msc_async_io_done() must be called. // 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); @@ -121,6 +131,12 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz */ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize); +#if CFG_TUD_MSC_ASYNC_IO +// Called once asynchronous read/write operation is done +// bytes_processed has the same meaning of tud_msc_read10_cb() / +// tud_msc_write10_cb() return value +void tud_msc_async_io_done(int32_t bytes_processed); +#endif /*------------- Optional callbacks -------------*/ // Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation From 04b9e203107ef5f8b9d44f9b8a302cf6504ae3ae Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 29 Jan 2025 15:20:44 +0100 Subject: [PATCH 03/11] msc_disk: fix overflow check when EP buffer size > 512. Signed-off-by: HiFiPhile --- examples/device/cdc_msc/src/msc_disk.c | 11 +++++++++-- examples/device/cdc_msc_freertos/src/msc_disk.c | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/examples/device/cdc_msc/src/msc_disk.c b/examples/device/cdc_msc/src/msc_disk.c index d325d77fa..6fc0760b6 100644 --- a/examples/device/cdc_msc/src/msc_disk.c +++ b/examples/device/cdc_msc/src/msc_disk.c @@ -195,7 +195,7 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff } // Check for overflow of offset + bufsize - if ( offset + bufsize > DISK_BLOCK_SIZE ) { + if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { return -1; } @@ -223,7 +223,14 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* (void) lun; // out of ramdisk - if ( lba >= DISK_BLOCK_NUM ) return -1; + if ( lba >= DISK_BLOCK_NUM ) { + return -1; + } + + // Check for overflow of offset + bufsize + if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { + return -1; + } #ifndef CFG_EXAMPLE_MSC_READONLY uint8_t* addr = msc_disk[lba] + offset; diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index d325d77fa..6fc0760b6 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -195,7 +195,7 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff } // Check for overflow of offset + bufsize - if ( offset + bufsize > DISK_BLOCK_SIZE ) { + if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { return -1; } @@ -223,7 +223,14 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* (void) lun; // out of ramdisk - if ( lba >= DISK_BLOCK_NUM ) return -1; + if ( lba >= DISK_BLOCK_NUM ) { + return -1; + } + + // Check for overflow of offset + bufsize + if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { + return -1; + } #ifndef CFG_EXAMPLE_MSC_READONLY uint8_t* addr = msc_disk[lba] + offset; From f43100bdfd5611c2ba463a05960ea5569a53a20b Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 29 Jan 2025 15:28:19 +0100 Subject: [PATCH 04/11] cdc_msc_freertos: add async IO support. Signed-off-by: HiFiPhile --- examples/device/cdc_msc_freertos/src/main.c | 3 +- .../device/cdc_msc_freertos/src/msc_disk.c | 70 ++++++++++++++++--- .../device/cdc_msc_freertos/src/tusb_config.h | 4 ++ 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c index c51e8ea81..4dada9801 100644 --- a/examples/device/cdc_msc_freertos/src/main.c +++ b/examples/device/cdc_msc_freertos/src/main.c @@ -72,7 +72,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; static void usb_device_task(void *param); void led_blinking_task(void* param); void cdc_task(void *params); - +extern void msc_disk_init(void); //--------------------------------------------------------------------+ // Main //--------------------------------------------------------------------+ @@ -123,6 +123,7 @@ static void usb_device_task(void *param) { board_init_after_tusb(); } + msc_disk_init(); // RTOS forever loop while (1) { // put this thread to waiting state until there is new events diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index 6fc0760b6..b0324911c 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -28,6 +28,20 @@ #if CFG_TUD_MSC +#if CFG_TUD_MSC_ASYNC_IO +// Simulate read/write operation time +#define SIM_IO_TIME_MS 20 + +TimerHandle_t sim_io_ops_timer; +static int32_t bytes_processed; +#if configSUPPORT_STATIC_ALLOCATION +StaticTimer_t sim_io_ops_timer_buf; +#endif +static void sim_io_ops_done_cb(TimerHandle_t xTimer); +#endif + +void msc_disk_init(void); + // whether host does safe-eject static bool ejected = false; @@ -119,6 +133,24 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = README_CONTENTS }; +#if CFG_TUD_MSC_ASYNC_IO +void msc_disk_init() { + +#if configSUPPORT_DYNAMIC_ALLOCATION + sim_io_ops_timer = xTimerCreate("sim_io_ops", pdMS_TO_TICKS(SIM_IO_TIME_MS), pdFALSE, NULL, sim_io_ops_done_cb); +#else + sim_io_ops_timer = xTimerCreateStatic("sim_io_ops", pdMS_TO_TICKS(SIM_IO_TIME_MS), pdFALSE, NULL, sim_io_ops_done_cb, &sim_io_ops_timer_buf); +#endif +} + +static void sim_io_ops_done_cb(TimerHandle_t xTimer) { + (void) xTimer; + tud_msc_async_io_done(bytes_processed); +} +#else +void msc_disk_init() {} +#endif + // 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]) @@ -188,21 +220,30 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { (void) lun; + int32_t ret = bufsize; // out of ramdisk if ( lba >= DISK_BLOCK_NUM ) { - return -1; + ret = -1; } // Check for overflow of offset + bufsize if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { - return -1; + ret = -1; } - uint8_t const* addr = msc_disk[lba] + offset; - memcpy(buffer, addr, bufsize); + if (ret != -1) { + uint8_t const* addr = msc_disk[lba] + offset; + memcpy(buffer, addr, bufsize); + } - return (int32_t) bufsize; +#if CFG_TUD_MSC_ASYNC_IO + // Simulate read operation + bytes_processed = ret; + xTimerStart(sim_io_ops_timer, 0); +#endif + + return ret; } bool tud_msc_is_writable_cb (uint8_t lun) @@ -221,25 +262,34 @@ bool tud_msc_is_writable_cb (uint8_t lun) int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { (void) lun; + int32_t ret = bufsize; // out of ramdisk if ( lba >= DISK_BLOCK_NUM ) { - return -1; + ret = -1; } // Check for overflow of offset + bufsize if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { - return -1; + ret = -1; } #ifndef CFG_EXAMPLE_MSC_READONLY - uint8_t* addr = msc_disk[lba] + offset; - memcpy(addr, buffer, bufsize); + if (ret != -1) { + uint8_t* addr = msc_disk[lba] + offset; + memcpy(addr, buffer, bufsize); + } #else (void) lba; (void) offset; (void) buffer; #endif - return (int32_t) bufsize; +#if CFG_TUD_MSC_ASYNC_IO + // Simulate read operation + bytes_processed = ret; + xTimerStart(sim_io_ops_timer, 0); +#endif + + return ret; } // Callback invoked when received an SCSI command not in built-in list below diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h index c3f2f7fb5..eb4798017 100644 --- a/examples/device/cdc_msc_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_freertos/src/tusb_config.h @@ -114,6 +114,10 @@ // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 +// Enable Async IO on MSC +#define CFG_TUD_MSC_ASYNC_IO 0 + + #ifdef __cplusplus } #endif From a40722b221f878f0d182c602f6d6db8d35031e19 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Wed, 29 Jan 2025 17:12:09 +0100 Subject: [PATCH 05/11] Enable SIM_IO_TIME_MS for normal operation. Signed-off-by: HiFiPhile --- examples/device/cdc_msc_freertos/src/msc_disk.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index b0324911c..2f3a1c30b 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -28,10 +28,10 @@ #if CFG_TUD_MSC -#if CFG_TUD_MSC_ASYNC_IO // Simulate read/write operation time -#define SIM_IO_TIME_MS 20 +#define SIM_IO_TIME_MS 0 +#if CFG_TUD_MSC_ASYNC_IO TimerHandle_t sim_io_ops_timer; static int32_t bytes_processed; #if configSUPPORT_STATIC_ALLOCATION @@ -238,9 +238,12 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff } #if CFG_TUD_MSC_ASYNC_IO - // Simulate read operation + // Simulate background read operation bytes_processed = ret; xTimerStart(sim_io_ops_timer, 0); +#elif SIM_IO_TIME_MS > 0 + // Simulate read operation + tusb_time_delay_ms_api(SIM_IO_TIME_MS); #endif return ret; @@ -284,9 +287,12 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* #endif #if CFG_TUD_MSC_ASYNC_IO - // Simulate read operation + // Simulate background write operation bytes_processed = ret; xTimerStart(sim_io_ops_timer, 0); +#elif SIM_IO_TIME_MS > 0 + // Simulate write operation + tusb_time_delay_ms_api(SIM_IO_TIME_MS); #endif return ret; From 84f8876c7c3639ef4ac707956831311791aefd76 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 31 Jan 2025 16:26:10 +0100 Subject: [PATCH 06/11] Use return code to choose async io. Signed-off-by: HiFiPhile --- src/class/msc/msc_device.c | 26 +++++++----------- src/class/msc/msc_device.h | 56 +++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index c7c926f4e..e3fe5a078 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -78,10 +78,10 @@ typedef struct { uint8_t sense_key; uint8_t add_sense_code; uint8_t add_sense_qualifier; -#if CFG_TUD_MSC_ASYNC_IO + + // Async IO uint8_t next_op; uint32_t xferred_bytes; -#endif }mscd_interface_t; static mscd_interface_t _mscd_itf; @@ -100,9 +100,7 @@ static void proc_write10_cmd(mscd_interface_t* p_msc); static void proc_write10_new_data(mscd_interface_t* p_msc, uint32_t xferred_bytes); static void proc_write10_next(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes); static bool proc_stage_status(mscd_interface_t* p_msc); -#if CFG_TUD_MSC_ASYNC_IO static void tud_msc_async_io_done_cb(void* bytes_processed); -#endif TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir) { return tu_bit_test(dir, 7); @@ -265,7 +263,6 @@ static inline void set_sense_medium_not_present(uint8_t lun) { tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00); } -#if CFG_TUD_MSC_ASYNC_IO void tud_msc_async_io_done(int32_t bytes_processed) { // Precheck to avoid queueing multiple RW done callback TU_VERIFY(_mscd_itf.next_op != MSC_NEXT_OP_NONE,); @@ -289,7 +286,6 @@ static void tud_msc_async_io_done_cb(void* bytes_processed) { } } } -#endif //--------------------------------------------------------------------+ // USBD Driver API @@ -821,13 +817,12 @@ static void proc_read10_cmd(mscd_interface_t* p_msc) { // Application can consume smaller bytes uint32_t const offset = p_msc->xferred_len % block_sz; -#if CFG_TUD_MSC_ASYNC_IO p_msc->next_op = MSC_NEXT_OP_READ10; - tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes); -#else nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes); - proc_read10_next(p_msc, nbytes); -#endif + if (nbytes != TUD_MSC_RET_ASYNC) { + p_msc->next_op = MSC_NEXT_OP_NONE; + proc_read10_next(p_msc, nbytes); + } } static void proc_read10_next(mscd_interface_t* p_msc, int32_t nbytes) { @@ -885,14 +880,13 @@ static void proc_write10_new_data(mscd_interface_t* p_msc, uint32_t xferred_byte // Invoke callback to consume new data uint32_t const offset = p_msc->xferred_len % block_sz; -#if CFG_TUD_MSC_ASYNC_IO p_msc->next_op = MSC_NEXT_OP_WRITE10; p_msc->xferred_bytes = xferred_bytes; - tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes); -#else int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes); - proc_write10_next(p_msc, xferred_bytes, nbytes); -#endif + if (nbytes != TUD_MSC_RET_ASYNC) { + p_msc->next_op = MSC_NEXT_OP_NONE; + proc_write10_next(p_msc, xferred_bytes, nbytes); + } } static void proc_write10_next(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes) { diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index 407fe241c..7162b11e4 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -48,10 +48,11 @@ #error CFG_TUD_MSC_EP_BUFSIZE must be defined, value of a block size should work well, the more the better #endif -// Enable asynchronous read/write, once operation is finished tud_msc_async_io_done() must be called -#ifndef CFG_TUD_MSC_ASYNC_IO -#define CFG_TUD_MSC_ASYNC_IO 0 -#endif +// Return value of callback functions +// Error +#define TUD_MSC_RET_ERROR -1 +// Asynchronous IO +#define TUD_MSC_RET_ASYNC -16 TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct"); @@ -62,6 +63,11 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct"); // 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); +// Called once asynchronous read/write operation is done +// bytes_processed has the same meaning of tud_msc_read10_cb() / +// tud_msc_write10_cb() return value +void tud_msc_async_io_done(int32_t bytes_processed); + //--------------------------------------------------------------------+ // Application Callbacks (WEAK is optional) //--------------------------------------------------------------------+ @@ -70,34 +76,40 @@ bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, u // - 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. +// - Application fill the buffer (up to bufsize) with address contents and return number of bytes read or status. // -// - read == 0 : Indicate application is not ready yet e.g disk I/O busy. -// Callback invoked again with the same parameters later on. +// - ret < bufsize : These bytes are transferred first and callback will be invoked again for remaining data. // -// - read < 0 : Indicate application error e.g invalid address. This request will be STALLed +// - ret == 0 : Indicate application is not ready yet e.g disk I/O busy. +// Callback will be invoked again with the same parameters later on. +// +// - ret == TUD_MSC_RET_ERROR (-1) +// : Indicate application error e.g invalid address. This request will be STALLed // and return failed status in command status wrapper phase. // -// - In case of asynchronous IO enabled, application should passing reading parameters to background IO -// task and return immediately. Once reading is done, tud_msc_async_io_done() must be called. +// - ret == TUD_MSC_RET_ASYNC (-16) +// : Data reading will be done asynchronously in a background task. Application should return immediately. +// tud_msc_async_io_done() must be called once reading is done to signal completion. int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); // 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. +// - Application writes data from buffer to address contents (up to bufsize) and returns the number of bytes written or status. // -// - write == 0 : Indicate application is not ready yet e.g disk I/O busy. -// Callback invoked again with the same parameters later on. +// - ret < bufsize : Callback will be invoked again with remaining data 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. +// - ret == 0 : Indicate application is not ready yet e.g disk I/O busy. +// Callback will be invoked again with the same parameters later on. // -// - In case of asynchronous IO enabled, application should passing writing parameters to background IO -// task and return immediately. Once writing is done, tud_msc_async_io_done() must be called. +// - ret == TUD_MSC_RET_ERROR (-1) +// : Indicate application error e.g invalid address. This request will be STALLed +// and return failed status in command status wrapper phase. +// +// - ret == TUD_MSC_RET_ASYNC (-16) +// : Data writing will be done asynchronously in a background task. Application should return immediately. +// tud_msc_async_io_done() must be called once writing is done to signal completion. // 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); @@ -131,12 +143,6 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz */ int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize); -#if CFG_TUD_MSC_ASYNC_IO -// Called once asynchronous read/write operation is done -// bytes_processed has the same meaning of tud_msc_read10_cb() / -// tud_msc_write10_cb() return value -void tud_msc_async_io_done(int32_t bytes_processed); -#endif /*------------- Optional callbacks -------------*/ // Invoked when received GET_MAX_LUN request, required for multiple LUNs implementation From 2707347decb0ea362fd0a6e4d58102c77f366429 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 31 Jan 2025 16:29:09 +0100 Subject: [PATCH 07/11] Update example. Signed-off-by: HiFiPhile --- .../device/cdc_msc_freertos/src/msc_disk.c | 126 +++++++++++------- .../device/cdc_msc_freertos/src/tusb_config.h | 6 +- 2 files changed, 79 insertions(+), 53 deletions(-) diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index 2f3a1c30b..c75b9ae34 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -28,16 +28,28 @@ #if CFG_TUD_MSC -// Simulate read/write operation time -#define SIM_IO_TIME_MS 0 +#if CFG_EXAMPLE_MSC_ASYNC_IO -#if CFG_TUD_MSC_ASYNC_IO -TimerHandle_t sim_io_ops_timer; -static int32_t bytes_processed; +#define IO_STACK_SIZE configMINIMAL_STACK_SIZE + +typedef struct { + uint8_t lun; + bool is_read; + uint32_t lba; + uint32_t offset; + void* buffer; + uint32_t bufsize; +} io_ops_t; + +QueueHandle_t io_queue; #if configSUPPORT_STATIC_ALLOCATION -StaticTimer_t sim_io_ops_timer_buf; +uint8_t io_queue_buf[sizeof(io_ops_t)]; +StaticQueue_t io_queue_static; +StackType_t io_stack[IO_STACK_SIZE]; +StaticTask_t io_taskdef; #endif -static void sim_io_ops_done_cb(TimerHandle_t xTimer); + +static void io_task(void *params); #endif void msc_disk_init(void); @@ -133,20 +145,37 @@ uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = README_CONTENTS }; -#if CFG_TUD_MSC_ASYNC_IO +#if CFG_EXAMPLE_MSC_ASYNC_IO void msc_disk_init() { -#if configSUPPORT_DYNAMIC_ALLOCATION - sim_io_ops_timer = xTimerCreate("sim_io_ops", pdMS_TO_TICKS(SIM_IO_TIME_MS), pdFALSE, NULL, sim_io_ops_done_cb); +#if configSUPPORT_STATIC_ALLOCATION + io_queue = xQueueCreateStatic(1, sizeof(io_ops_t), io_queue_buf, &io_queue_static); + xTaskCreateStatic(io_task, "io", IO_STACK_SIZE, NULL, 2, io_stack, &io_taskdef); #else - sim_io_ops_timer = xTimerCreateStatic("sim_io_ops", pdMS_TO_TICKS(SIM_IO_TIME_MS), pdFALSE, NULL, sim_io_ops_done_cb, &sim_io_ops_timer_buf); + io_queue = xQueueCreate(1, sizeof(io_ops_t)); + xTaskCreate(io_task, "io", IO_STACK_SIZE, NULL, 2, NULL); #endif } -static void sim_io_ops_done_cb(TimerHandle_t xTimer) { - (void) xTimer; - tud_msc_async_io_done(bytes_processed); +static void io_task(void *params) { + (void) params; + io_ops_t io_ops; + while (1) { + if (xQueueReceive(io_queue, &io_ops, portMAX_DELAY)) { + if (io_ops.is_read) { + uint8_t const* addr = msc_disk[io_ops.lba] + io_ops.offset; + memcpy(io_ops.buffer, addr, io_ops.bufsize); + } else { + uint8_t* addr = msc_disk[io_ops.lba] + io_ops.offset; + memcpy(addr, io_ops.buffer, io_ops.bufsize); + } + + tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); + tud_msc_async_io_done(io_ops.bufsize); + } + } } + #else void msc_disk_init() {} #endif @@ -220,33 +249,31 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { (void) lun; - int32_t ret = bufsize; // out of ramdisk if ( lba >= DISK_BLOCK_NUM ) { - ret = -1; + return TUD_MSC_RET_ERROR; } // Check for overflow of offset + bufsize if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { - ret = -1; + return TUD_MSC_RET_ERROR; } - if (ret != -1) { - uint8_t const* addr = msc_disk[lba] + offset; - memcpy(buffer, addr, bufsize); - } +#if CFG_EXAMPLE_MSC_ASYNC_IO + io_ops_t io_ops = { .is_read = true, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize }; -#if CFG_TUD_MSC_ASYNC_IO - // Simulate background read operation - bytes_processed = ret; - xTimerStart(sim_io_ops_timer, 0); -#elif SIM_IO_TIME_MS > 0 - // Simulate read operation - tusb_time_delay_ms_api(SIM_IO_TIME_MS); + // Send IO operation to IO task + TU_ASSERT(xQueueSend(io_queue, &io_ops, 0) == pdPASS); + + return TUD_MSC_RET_ASYNC; +#else + uint8_t const* addr = msc_disk[lba] + offset; + memcpy(buffer, addr, bufsize); + tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); + + return bufsize; #endif - - return ret; } bool tud_msc_is_writable_cb (uint8_t lun) @@ -264,38 +291,35 @@ bool tud_msc_is_writable_cb (uint8_t lun) // Process data in buffer to disk's storage and return number of written bytes int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { - (void) lun; - int32_t ret = bufsize; - // out of ramdisk if ( lba >= DISK_BLOCK_NUM ) { - ret = -1; + return TUD_MSC_RET_ERROR; } // Check for overflow of offset + bufsize if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { - ret = -1; + return TUD_MSC_RET_ERROR; } -#ifndef CFG_EXAMPLE_MSC_READONLY - if (ret != -1) { - uint8_t* addr = msc_disk[lba] + offset; - memcpy(addr, buffer, bufsize); - } +#ifdef CFG_EXAMPLE_MSC_READONLY + (void) lun; (void) buffer; + return bufsize; +#endif + +#if CFG_EXAMPLE_MSC_ASYNC_IO + io_ops_t io_ops = { .is_read = false, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize }; + + // Send IO operation to IO task + TU_ASSERT(xQueueSend(io_queue, &io_ops, 0) == pdPASS); + + return TUD_MSC_RET_ASYNC; #else - (void) lba; (void) offset; (void) buffer; -#endif + uint8_t* addr = msc_disk[lba] + offset; + memcpy(addr, buffer, bufsize); + tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); -#if CFG_TUD_MSC_ASYNC_IO - // Simulate background write operation - bytes_processed = ret; - xTimerStart(sim_io_ops_timer, 0); -#elif SIM_IO_TIME_MS > 0 - // Simulate write operation - tusb_time_delay_ms_api(SIM_IO_TIME_MS); + return bufsize; #endif - - return ret; } // Callback invoked when received an SCSI command not in built-in list below diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h index eb4798017..d70456287 100644 --- a/examples/device/cdc_msc_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_freertos/src/tusb_config.h @@ -114,9 +114,11 @@ // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 -// Enable Async IO on MSC -#define CFG_TUD_MSC_ASYNC_IO 0 +// Use async IO in example or not +#define CFG_EXAMPLE_MSC_ASYNC_IO 1 +// Simulate read/write operation delay +#define CFG_EXAMPLE_MSC_IO_DELAY_MS 0 #ifdef __cplusplus } From 8d2310247c47160bf8de6e5f754284360ad7e937 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 31 Jan 2025 16:31:47 +0100 Subject: [PATCH 08/11] Fix CI. Signed-off-by: HiFiPhile --- examples/device/cdc_msc_freertos/src/msc_disk.c | 2 ++ src/class/msc/msc_device.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index c75b9ae34..c2aaca847 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -166,8 +166,10 @@ static void io_task(void *params) { uint8_t const* addr = msc_disk[io_ops.lba] + io_ops.offset; memcpy(io_ops.buffer, addr, io_ops.bufsize); } else { +#ifndef CFG_EXAMPLE_MSC_READONLY uint8_t* addr = msc_disk[io_ops.lba] + io_ops.offset; memcpy(addr, io_ops.buffer, io_ops.bufsize); +#endif } tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index e3fe5a078..72c3747fc 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -267,14 +267,14 @@ void tud_msc_async_io_done(int32_t bytes_processed) { // Precheck to avoid queueing multiple RW done callback TU_VERIFY(_mscd_itf.next_op != MSC_NEXT_OP_NONE,); // Call usbd_edpt_xfer() in tud_task() to avoid racing condition - usbd_defer_func(tud_msc_async_io_done_cb, (void*) bytes_processed, false); + usbd_defer_func(tud_msc_async_io_done_cb, (void*) (intptr_t)bytes_processed, false); } static void tud_msc_async_io_done_cb(void* bytes_processed) { TU_VERIFY(_mscd_itf.next_op != MSC_NEXT_OP_NONE,); uint8_t next_op = _mscd_itf.next_op; _mscd_itf.next_op = MSC_NEXT_OP_NONE; - int32_t nbytes = (int32_t)bytes_processed; + int32_t nbytes = (int32_t)(intptr_t)bytes_processed; // READ10 if (next_op == MSC_NEXT_OP_READ10) { proc_read10_next(&_mscd_itf, nbytes); From d22cbe4cb5b88c869754e91c97eff513ed8b7b33 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 1 Jul 2025 20:13:21 +0700 Subject: [PATCH 09/11] refactor async io, add in_isr argument to tud_msc_async_io_done() use cbw.command[0] for pending IO command --- src/class/msc/msc_device.c | 211 +++++++++++++++++-------------------- src/class/msc/msc_device.h | 70 +++++------- 2 files changed, 123 insertions(+), 158 deletions(-) diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index e583f6ba8..709734b87 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -52,36 +52,27 @@ enum { MSC_STAGE_NEED_RESET, }; -enum { - MSC_NEXT_OP_NONE = 0, - MSC_NEXT_OP_READ10, - MSC_NEXT_OP_WRITE10, - MSC_NEXT_OP_STATUS -}; - typedef struct { - TU_ATTR_ALIGNED(4) msc_cbw_t cbw; - TU_ATTR_ALIGNED(4) msc_csw_t csw; - + TU_ATTR_ALIGNED(4) msc_cbw_t cbw; // 31 bytes uint8_t rhport; + + TU_ATTR_ALIGNED(4) msc_csw_t csw; // 13 bytes uint8_t itf_num; uint8_t ep_in; uint8_t ep_out; - // Bulk Only Transfer (BOT) Protocol - uint8_t stage; - uint32_t total_len; // byte to be transferred, can be smaller than total_bytes in cbw uint32_t xferred_len; // numbered of bytes transferred so far in the Data Stage - // Sense Response Data + // Bulk Only Transfer (BOT) Protocol + uint8_t stage; + + // SCSI Sense Response Data uint8_t sense_key; uint8_t add_sense_code; uint8_t add_sense_qualifier; - // Async IO - uint8_t next_op; - uint32_t xferred_bytes; + uint8_t pending_io; // pending async IO }mscd_interface_t; static mscd_interface_t _mscd_itf; @@ -95,12 +86,11 @@ CFG_TUD_MEM_SECTION static struct { //--------------------------------------------------------------------+ 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(mscd_interface_t* p_msc); -static void proc_read10_next(mscd_interface_t* p_msc, int32_t nbytes); +static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes); static void proc_write10_cmd(mscd_interface_t* p_msc); -static void proc_write10_new_data(mscd_interface_t* p_msc, uint32_t xferred_bytes); -static void proc_write10_next(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes); +static void proc_write10_host_data(mscd_interface_t* p_msc, uint32_t xferred_bytes); +static void proc_write_io_data(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes); static bool proc_stage_status(mscd_interface_t* p_msc); -static void tud_msc_async_io_done_cb(void* bytes_processed); TU_ATTR_ALWAYS_INLINE static inline bool is_data_in(uint8_t dir) { return tu_bit_test(dir, 7); @@ -195,30 +185,31 @@ static uint8_t rdwr10_validate_cmd(msc_cbw_t const* cbw) { return status; } -static bool proc_stage_status(mscd_interface_t* p_msc) { +static bool proc_stage_status(mscd_interface_t *p_msc) { uint8_t rhport = p_msc->rhport; - msc_cbw_t const* p_cbw = &p_msc->cbw; - // skip status if epin is currently stalled, will do it when received Clear Stall request - if (!usbd_edpt_stalled(rhport, p_msc->ep_in)) { - if ((p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir)) { - // 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status - // TU_LOG_DRV(" SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len); - usbd_edpt_stall(rhport, p_msc->ep_in); - } else { - TU_ASSERT(send_csw(p_msc)); - } - } + msc_cbw_t const *p_cbw = &p_msc->cbw; - #if TU_CHECK_MCU(OPT_MCU_CXD56) - // WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD. - // There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and - // hope everything will work - if ( usbd_edpt_stalled(rhport, p_msc->ep_in) ) { - usbd_edpt_clear_stall(rhport, p_msc->ep_in); - send_csw(p_msc); + // skip status if epin is currently stalled, will do it when received Clear Stall request + if (!usbd_edpt_stalled(rhport, p_msc->ep_in)) { + if ((p_cbw->total_bytes > p_msc->xferred_len) && is_data_in(p_cbw->dir)) { + // 6.7 The 13 Cases: case 5 (Hi > Di): STALL before status + // TU_LOG_DRV(" SCSI case 5 (Hi > Di): %lu > %lu\r\n", p_cbw->total_bytes, p_msc->xferred_len); + usbd_edpt_stall(rhport, p_msc->ep_in); + } else { + TU_ASSERT(send_csw(p_msc)); } - #endif - return true; + } + + #if TU_CHECK_MCU(OPT_MCU_CXD56) + // WORKAROUND: cxd56 has its own nuttx usb stack which does not forward Set/ClearFeature(Endpoint) to DCD. + // There is no way for us to know when EP is un-stall, therefore we will unconditionally un-stall here and + // hope everything will work + if (usbd_edpt_stalled(rhport, p_msc->ep_in)) { + usbd_edpt_clear_stall(rhport, p_msc->ep_in); + send_csw(p_msc); + } + #endif + return true; } //--------------------------------------------------------------------+ @@ -258,39 +249,57 @@ bool tud_msc_set_sense(uint8_t lun, uint8_t sense_key, uint8_t add_sense_code, u return true; } -static inline void set_sense_medium_not_present(uint8_t lun) { +TU_ATTR_ALWAYS_INLINE static inline void set_sense_medium_not_present(uint8_t lun) { // default sense is NOT READY, MEDIUM NOT PRESENT tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00); } -void tud_msc_async_io_done(int32_t bytes_processed) { - // Precheck to avoid queueing multiple RW done callback - TU_VERIFY(_mscd_itf.next_op != MSC_NEXT_OP_NONE,); - // Call usbd_edpt_xfer() in tud_task() to avoid racing condition - usbd_defer_func(tud_msc_async_io_done_cb, (void*) (intptr_t)bytes_processed, false); +static void proc_async_io_done(void *bytes_processed) { + mscd_interface_t *p_msc = &_mscd_itf; + TU_VERIFY(p_msc->pending_io, ); + const int32_t nbytes = (int32_t) (intptr_t) bytes_processed; + const uint8_t cmd = p_msc->cbw.command[0]; + + p_msc->pending_io = 0; + switch (cmd) { + case SCSI_CMD_READ_10: + proc_read_io_data(p_msc, nbytes); + break; + + case SCSI_CMD_WRITE_10: + proc_write_io_data(p_msc, (uint32_t) nbytes, nbytes); + break; + + default: break; + } + + // send status if stage is transitioned to STATUS + if (p_msc->stage == MSC_STAGE_STATUS) { + proc_stage_status(p_msc); + } } -static void tud_msc_async_io_done_cb(void* bytes_processed) { - TU_VERIFY(_mscd_itf.next_op != MSC_NEXT_OP_NONE,); - uint8_t next_op = _mscd_itf.next_op; - _mscd_itf.next_op = MSC_NEXT_OP_NONE; - int32_t nbytes = (int32_t)(intptr_t)bytes_processed; - // READ10 - if (next_op == MSC_NEXT_OP_READ10) { - proc_read10_next(&_mscd_itf, nbytes); - } else if (next_op == MSC_NEXT_OP_WRITE10) { - proc_write10_next(&_mscd_itf, _mscd_itf.xferred_bytes, nbytes); - // Need to manually invoke CSW transfer - if (_mscd_itf.stage == MSC_STAGE_STATUS) { - proc_stage_status(&_mscd_itf); - } +bool tud_msc_async_io_done(int32_t bytes_io, bool in_isr) { + // Precheck to avoid queueing multiple RW done callback + TU_VERIFY(_mscd_itf.pending_io); + if (bytes_io == 0) { + bytes_io = TUD_MSC_RET_ERROR; // 0 is treated as error, no sense to call this with BUSY here } + + if (in_isr) { + usbd_defer_func(proc_async_io_done, (void*) (intptr_t)bytes_io, in_isr); + } else { + proc_async_io_done((void*)(intptr_t) bytes_io); + } + + return true; } //--------------------------------------------------------------------+ // USBD Driver API //--------------------------------------------------------------------+ void mscd_init(void) { + TU_LOG_INT(CFG_TUD_MSC_LOG_LEVEL, sizeof(mscd_interface_t)); tu_memclr(&_mscd_itf, sizeof(mscd_interface_t)); } @@ -528,7 +537,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t proc_read10_cmd(p_msc); } } else if (SCSI_CMD_WRITE_10 == p_cbw->command[0]) { - proc_write10_new_data(p_msc, xferred_bytes); + proc_write10_host_data(p_msc, xferred_bytes); } else { p_msc->xferred_len += xferred_bytes; @@ -560,7 +569,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t break; case MSC_STAGE_STATUS_SENT: - // Wait for the Status phase to complete + // Status phase is complete if ((ep_addr == p_msc->ep_in) && (xferred_bytes == sizeof(msc_csw_t))) { TU_LOG_DRV(" SCSI Status [Lun%u] = %u\r\n", p_cbw->lun, p_csw->status); // TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, p_csw, xferred_bytes, 2); @@ -590,7 +599,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t TU_ASSERT(prepare_cbw(p_msc)); } else { - // Any xfer ended here is consider unknown error, ignore it + // Any xfer ended here is considered unknown error, ignore it TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n"); } break; @@ -696,8 +705,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ break; case SCSI_CMD_READ_FORMAT_CAPACITY: { - scsi_read_format_capacity_data_t read_fmt_capa = - { + scsi_read_format_capacity_data_t read_fmt_capa = { .list_length = 8, .block_num = 0, .descriptor_type = 2, // formatted media @@ -729,8 +737,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ break; case SCSI_CMD_INQUIRY: { - scsi_inquiry_resp_t inquiry_rsp = - { + scsi_inquiry_resp_t inquiry_rsp = { .is_removable = 1, .version = 2, .response_data_format = 2, @@ -750,8 +757,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ break; case SCSI_CMD_MODE_SENSE_6: { - scsi_mode_sense6_resp_t mode_resp = - { + scsi_mode_sense6_resp_t mode_resp = { .data_len = 3, .medium_type = 0, .write_protected = false, @@ -772,8 +778,7 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ break; case SCSI_CMD_REQUEST_SENSE: { - scsi_sense_fixed_resp_t sense_rsp = - { + scsi_sense_fixed_resp_t sense_rsp = { .response_code = 0x70, // current, fixed format .valid = 1 }; @@ -805,32 +810,27 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_ static void proc_read10_cmd(mscd_interface_t* p_msc) { msc_cbw_t const* p_cbw = &p_msc->cbw; - - // block size already verified not zero - uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); - - // Adjust lba with transferred bytes + uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); // already verified non-zero + // Adjust lba & offset with transferred bytes uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz); + uint32_t const offset = p_msc->xferred_len % block_sz; // remaining bytes capped at class buffer int32_t nbytes = (int32_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len); - // Application can consume smaller bytes - uint32_t const offset = p_msc->xferred_len % block_sz; - - p_msc->next_op = MSC_NEXT_OP_READ10; + p_msc->pending_io = 1; nbytes = tud_msc_read10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, (uint32_t)nbytes); if (nbytes != TUD_MSC_RET_ASYNC) { - p_msc->next_op = MSC_NEXT_OP_NONE; - proc_read10_next(p_msc, nbytes); + p_msc->pending_io = 0; + proc_read_io_data(p_msc, nbytes); } } -static void proc_read10_next(mscd_interface_t* p_msc, int32_t nbytes) { +static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) { uint8_t rhport = p_msc->rhport; if (nbytes < 0) { // negative means error -> endpoint is stalled & status in CSW set to failed - TU_LOG_DRV(" tud_msc_read10_cb() return -1\r\n"); + TU_LOG_DRV(" IO read() failed\r\n"); // set sense msc_cbw_t const* p_cbw = &p_msc->cbw; @@ -838,7 +838,7 @@ static void proc_read10_next(mscd_interface_t* p_msc, int32_t nbytes) { fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); } else if (nbytes == 0) { - // zero means not ready -> simulate an transfer complete so that this driver callback will fired again + // zero means not ready -> fake a transfer complete so that this driver callback will fire again dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false); } else { TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes),); @@ -864,55 +864,42 @@ static void proc_write10_cmd(mscd_interface_t* p_msc) { // remaining bytes capped at class buffer uint16_t nbytes = (uint16_t)tu_min32(CFG_TUD_MSC_EP_BUFSIZE, p_cbw->total_bytes - p_msc->xferred_len); // Write10 callback will be called later when usb transfer complete - uint8_t rhport = p_msc->rhport; - TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),); + TU_ASSERT(usbd_edpt_xfer(p_msc->rhport, p_msc->ep_out, _mscd_epbuf.buf, nbytes),); } // process new data arrived from WRITE10 -static void proc_write10_new_data(mscd_interface_t* p_msc, uint32_t xferred_bytes) { +static void proc_write10_host_data(mscd_interface_t* p_msc, uint32_t xferred_bytes) { msc_cbw_t const* p_cbw = &p_msc->cbw; + uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); // already verified non-zero - // block size already verified not zero - uint16_t const block_sz = rdwr10_get_blocksize(p_cbw); - - // Adjust lba with transferred bytes + // Adjust lba & offset with transferred bytes uint32_t const lba = rdwr10_get_lba(p_cbw->command) + (p_msc->xferred_len / block_sz); - - // Invoke callback to consume new data uint32_t const offset = p_msc->xferred_len % block_sz; - p_msc->next_op = MSC_NEXT_OP_WRITE10; - p_msc->xferred_bytes = xferred_bytes; + p_msc->pending_io = 1; int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes); if (nbytes != TUD_MSC_RET_ASYNC) { - p_msc->next_op = MSC_NEXT_OP_NONE; - proc_write10_next(p_msc, xferred_bytes, nbytes); + p_msc->pending_io = 0; + proc_write_io_data(p_msc, xferred_bytes, nbytes); } } -static void proc_write10_next(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes) { +static void proc_write_io_data(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes) { if (nbytes < 0) { // negative means error -> failed this scsi op - TU_LOG_DRV(" tud_msc_write10_cb() return -1\r\n"); - - // update actual byte before failed - p_msc->xferred_len += xferred_bytes; - - msc_cbw_t const* p_cbw = &p_msc->cbw; - set_sense_medium_not_present(p_cbw->lun); + TU_LOG_DRV(" IO write() failed\r\n"); + set_sense_medium_not_present(p_msc->cbw.lun); fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); } else { if ((uint32_t)nbytes < xferred_bytes) { // Application consume less than what we got (including zero) const uint32_t left_over = xferred_bytes - (uint32_t)nbytes; if (nbytes > 0) { - p_msc->xferred_len += (uint16_t)nbytes; memmove(_mscd_epbuf.buf, _mscd_epbuf.buf + nbytes, left_over); } - // simulate a transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter - uint8_t rhport = p_msc->rhport; - dcd_event_xfer_complete(rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false); + // fake a transfer complete with adjusted parameters --> callback will be invoked with adjusted parameters + dcd_event_xfer_complete(p_msc->rhport, p_msc->ep_out, left_over, XFER_RESULT_SUCCESS, false); } else { // Application consume all bytes in our buffer p_msc->xferred_len += xferred_bytes; diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index 7162b11e4..2ad31c245 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -49,10 +49,11 @@ #endif // Return value of callback functions -// Error -#define TUD_MSC_RET_ERROR -1 -// Asynchronous IO -#define TUD_MSC_RET_ASYNC -16 +enum { + TUD_MSC_RET_BUSY = 0, // Busy, e.g disk I/O is not ready + TUD_MSC_RET_ERROR = -1, + TUD_MSC_RET_ASYNC = -2, // Asynchronous IO +}; TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct"); @@ -63,54 +64,31 @@ TU_VERIFY_STATIC(CFG_TUD_MSC_EP_BUFSIZE < UINT16_MAX, "Size is not correct"); // 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); -// Called once asynchronous read/write operation is done -// bytes_processed has the same meaning of tud_msc_read10_cb() / -// tud_msc_write10_cb() return value -void tud_msc_async_io_done(int32_t bytes_processed); +// Called by Application once asynchronous I/O operation is done +// bytes_io is number of bytes in I/O op, typically the bufsize in read/write_cb() or +// TUD_MSC_RET_ERROR (-1) for error. Note TUD_MSC_RET_BUSY (0) will be treated as error as well. +bool tud_msc_async_io_done(int32_t bytes_io, bool in_isr); //--------------------------------------------------------------------+ // Application Callbacks (WEAK is optional) //--------------------------------------------------------------------+ -// 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 bytes read or status. -// -// - ret < bufsize : These bytes are transferred first and callback will be invoked again for remaining data. -// -// - ret == 0 : Indicate application is not ready yet e.g disk I/O busy. -// Callback will be invoked again with the same parameters later on. -// -// - ret == TUD_MSC_RET_ERROR (-1) -// : Indicate application error e.g invalid address. This request will be STALLed -// and return failed status in command status wrapper phase. -// -// - ret == TUD_MSC_RET_ASYNC (-16) -// : Data reading will be done asynchronously in a background task. Application should return immediately. -// tud_msc_async_io_done() must be called once reading is done to signal completion. +/* + Invoked when received SCSI READ10/WRITE10 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 bytes read or status. + - 0 < ret < bufsize: These bytes are transferred first and callback will be invoked again for remaining data. + - ret == TUD_MSC_RET_BUSY + Application is buys e.g disk I/O not ready. + Callback will be invoked again with the same parameters later on. + - ret == TUD_MSC_RET_ERROR + error such as invalid address. This request will be STALLed and scsi command will be failed + - ret == TUD_MSC_RET_ASYNC + Data I/O will be done asynchronously in a background task. Application should return immediately. + tud_msc_async_io_done() must be called once IO/ is done to signal completion. +*/ int32_t tud_msc_read10_cb (uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); - -// 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 writes data from buffer to address contents (up to bufsize) and returns the number of bytes written or status. -// -// - ret < bufsize : Callback will be invoked again with remaining data later on. -// -// - ret == 0 : Indicate application is not ready yet e.g disk I/O busy. -// Callback will be invoked again with the same parameters later on. -// -// - ret == TUD_MSC_RET_ERROR (-1) -// : Indicate application error e.g invalid address. This request will be STALLed -// and return failed status in command status wrapper phase. -// -// - ret == TUD_MSC_RET_ASYNC (-16) -// : Data writing will be done asynchronously in a background task. Application should return immediately. -// tud_msc_async_io_done() must be called once writing is done to signal completion. -// 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 From 216a35e59a095e6ccb70f7c9bd4f7bc737f44140 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 1 Jul 2025 20:15:16 +0700 Subject: [PATCH 10/11] update example --- .../device/cdc_msc_freertos/src/msc_disk.c | 117 ++++++++---------- .../device/cdc_msc_freertos/src/tusb_config.h | 6 - src/class/msc/msc_device.c | 6 +- 3 files changed, 55 insertions(+), 74 deletions(-) diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c index c2aaca847..d1ff2f71b 100644 --- a/examples/device/cdc_msc_freertos/src/msc_disk.c +++ b/examples/device/cdc_msc_freertos/src/msc_disk.c @@ -28,8 +28,13 @@ #if CFG_TUD_MSC -#if CFG_EXAMPLE_MSC_ASYNC_IO +// Use async IO in example or not +#define CFG_EXAMPLE_MSC_ASYNC_IO 1 +// Simulate read/write operation delay +#define CFG_EXAMPLE_MSC_IO_DELAY_MS 0 + +#if CFG_EXAMPLE_MSC_ASYNC_IO #define IO_STACK_SIZE configMINIMAL_STACK_SIZE typedef struct { @@ -66,8 +71,7 @@ static bool ejected = false; If you find any bugs or get any questions, feel free to file an\r\n\ issue at github.com/hathach/tinyusb" -enum -{ +enum { DISK_BLOCK_NUM = 16, // 8KB is the smallest size that windows allow to mount DISK_BLOCK_SIZE = 512 }; @@ -162,18 +166,20 @@ static void io_task(void *params) { io_ops_t io_ops; while (1) { if (xQueueReceive(io_queue, &io_ops, portMAX_DELAY)) { + const uint8_t* addr = msc_disk[io_ops.lba] + io_ops.offset; + int32_t nbytes = io_ops.bufsize; if (io_ops.is_read) { - uint8_t const* addr = msc_disk[io_ops.lba] + io_ops.offset; memcpy(io_ops.buffer, addr, io_ops.bufsize); } else { #ifndef CFG_EXAMPLE_MSC_READONLY - uint8_t* addr = msc_disk[io_ops.lba] + io_ops.offset; - memcpy(addr, io_ops.buffer, io_ops.bufsize); + memcpy((uint8_t*) addr, io_ops.buffer, io_ops.bufsize); +#else + nbytes = -1; // failed to write #endif } tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); - tud_msc_async_io_done(io_ops.bufsize); + tud_msc_async_io_done(nbytes, false); } } } @@ -184,14 +190,11 @@ void msc_disk_init() {} // 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]) -{ +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { (void) lun; - const char vid[] = "TinyUSB"; const char pid[] = "Mass Storage"; const char rev[] = "1.0"; - memcpy(vendor_id , vid, strlen(vid)); memcpy(product_id , pid, strlen(pid)); memcpy(product_rev, rev, strlen(rev)); @@ -199,8 +202,7 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16 // 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) -{ +bool tud_msc_test_unit_ready_cb(uint8_t lun) { (void) lun; // RAM disk is ready until ejected @@ -215,10 +217,8 @@ 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) -{ +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; } @@ -226,18 +226,14 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz // Invoked when received Start Stop Unit command // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage // - Start = 1 : active mode, if load_eject = 1 : load disk storage -bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) -{ +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { (void) lun; (void) power_condition; - if ( load_eject ) - { - if (start) - { + if (load_eject) { + if (start) { // load disk storage - }else - { + } else { // unload disk storage ejected = true; } @@ -248,116 +244,107 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo // 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) -{ +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) { (void) lun; // out of ramdisk - if ( lba >= DISK_BLOCK_NUM ) { + if (lba >= DISK_BLOCK_NUM) { return TUD_MSC_RET_ERROR; } // Check for overflow of offset + bufsize - if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { + if (lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE) { return TUD_MSC_RET_ERROR; } -#if CFG_EXAMPLE_MSC_ASYNC_IO - io_ops_t io_ops = { .is_read = true, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize }; + #if CFG_EXAMPLE_MSC_ASYNC_IO + io_ops_t io_ops = {.is_read = true, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize}; // Send IO operation to IO task TU_ASSERT(xQueueSend(io_queue, &io_ops, 0) == pdPASS); return TUD_MSC_RET_ASYNC; -#else - uint8_t const* addr = msc_disk[lba] + offset; + #else + uint8_t const *addr = msc_disk[lba] + offset; memcpy(buffer, addr, bufsize); - tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); - return bufsize; -#endif + #endif } -bool tud_msc_is_writable_cb (uint8_t lun) -{ +bool tud_msc_is_writable_cb (uint8_t lun) { (void) lun; -#ifdef CFG_EXAMPLE_MSC_READONLY + #ifdef CFG_EXAMPLE_MSC_READONLY return false; -#else + #else return true; -#endif + #endif } // Callback invoked when received WRITE10 command. // Process data in buffer to disk's storage and return number of written bytes -int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) -{ +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) { // out of ramdisk - if ( lba >= DISK_BLOCK_NUM ) { + if (lba >= DISK_BLOCK_NUM) { return TUD_MSC_RET_ERROR; } // Check for overflow of offset + bufsize - if ( lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE ) { + if (lba * DISK_BLOCK_SIZE + offset + bufsize > DISK_BLOCK_NUM * DISK_BLOCK_SIZE) { return TUD_MSC_RET_ERROR; } -#ifdef CFG_EXAMPLE_MSC_READONLY - (void) lun; (void) buffer; + #ifdef CFG_EXAMPLE_MSC_READONLY + (void) lun; + (void) buffer; return bufsize; -#endif + #endif -#if CFG_EXAMPLE_MSC_ASYNC_IO - io_ops_t io_ops = { .is_read = false, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize }; + #if CFG_EXAMPLE_MSC_ASYNC_IO + io_ops_t io_ops = {.is_read = false, .lun = lun, .lba = lba, .offset = offset, .buffer = buffer, .bufsize = bufsize}; // Send IO operation to IO task TU_ASSERT(xQueueSend(io_queue, &io_ops, 0) == pdPASS); return TUD_MSC_RET_ASYNC; -#else - uint8_t* addr = msc_disk[lba] + offset; + #else + uint8_t *addr = msc_disk[lba] + offset; memcpy(addr, buffer, bufsize); tusb_time_delay_ms_api(CFG_EXAMPLE_MSC_IO_DELAY_MS); return bufsize; -#endif + #endif } // Callback 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 -int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) -{ +int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) { // read10 & write10 has their own callback and MUST not be handled here - void const* response = NULL; + void const *response = NULL; int32_t resplen = 0; // most scsi handled is input bool in_xfer = true; - switch (scsi_cmd[0]) - { + switch (scsi_cmd[0]) { default: // Set Sense = Invalid Command Operation tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); // negative means error -> tinyusb could stall and/or response with failed status resplen = -1; - break; + break; } // return resplen must not larger than bufsize - if ( resplen > bufsize ) resplen = bufsize; + if (resplen > bufsize) { resplen = bufsize; } - if ( response && (resplen > 0) ) - { - if(in_xfer) - { + if (response && (resplen > 0)) { + if (in_xfer) { memcpy(buffer, response, (size_t) resplen); - }else - { + } else { // SCSI output } } diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h index d70456287..c3f2f7fb5 100644 --- a/examples/device/cdc_msc_freertos/src/tusb_config.h +++ b/examples/device/cdc_msc_freertos/src/tusb_config.h @@ -114,12 +114,6 @@ // MSC Buffer size of Device Mass storage #define CFG_TUD_MSC_EP_BUFSIZE 512 -// Use async IO in example or not -#define CFG_EXAMPLE_MSC_ASYNC_IO 1 - -// Simulate read/write operation delay -#define CFG_EXAMPLE_MSC_IO_DELAY_MS 0 - #ifdef __cplusplus } #endif diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index 709734b87..a8c52b6bd 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -828,8 +828,8 @@ static void proc_read10_cmd(mscd_interface_t* p_msc) { static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) { uint8_t rhport = p_msc->rhport; - if (nbytes < 0) { - // negative means error -> endpoint is stalled & status in CSW set to failed + if (nbytes == TUD_MSC_RET_ERROR) { + // error -> endpoint is stalled & status in CSW set to failed TU_LOG_DRV(" IO read() failed\r\n"); // set sense @@ -837,7 +837,7 @@ static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) { set_sense_medium_not_present(p_cbw->lun); fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); - } else if (nbytes == 0) { + } else if (nbytes == TUD_MSC_RET_BUSY) { // zero means not ready -> fake a transfer complete so that this driver callback will fire again dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false); } else { From c96cc4369f1b97efe85575f3e5633e38598e9fc2 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 1 Jul 2025 21:52:57 +0700 Subject: [PATCH 11/11] defer proc_async_io_done() --- src/class/msc/msc_device.c | 64 +++++++++++++++++++++----------------- src/class/msc/msc_device.h | 9 +++--- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c index a8c52b6bd..747ad03ed 100644 --- a/src/class/msc/msc_device.c +++ b/src/class/msc/msc_device.c @@ -254,10 +254,10 @@ TU_ATTR_ALWAYS_INLINE static inline void set_sense_medium_not_present(uint8_t lu tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00); } -static void proc_async_io_done(void *bytes_processed) { +static void proc_async_io_done(void *bytes_io) { mscd_interface_t *p_msc = &_mscd_itf; TU_VERIFY(p_msc->pending_io, ); - const int32_t nbytes = (int32_t) (intptr_t) bytes_processed; + const int32_t nbytes = (int32_t) (intptr_t) bytes_io; const uint8_t cmd = p_msc->cbw.command[0]; p_msc->pending_io = 0; @@ -283,15 +283,9 @@ bool tud_msc_async_io_done(int32_t bytes_io, bool in_isr) { // Precheck to avoid queueing multiple RW done callback TU_VERIFY(_mscd_itf.pending_io); if (bytes_io == 0) { - bytes_io = TUD_MSC_RET_ERROR; // 0 is treated as error, no sense to call this with BUSY here + bytes_io = TUD_MSC_RET_ERROR; // 0 is treated as error, no reason to call this with BUSY here } - - if (in_isr) { - usbd_defer_func(proc_async_io_done, (void*) (intptr_t)bytes_io, in_isr); - } else { - proc_async_io_done((void*)(intptr_t) bytes_io); - } - + usbd_defer_func(proc_async_io_done, (void *) (intptr_t) bytes_io, in_isr); return true; } @@ -827,21 +821,26 @@ static void proc_read10_cmd(mscd_interface_t* p_msc) { } static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) { - uint8_t rhport = p_msc->rhport; - if (nbytes == TUD_MSC_RET_ERROR) { - // error -> endpoint is stalled & status in CSW set to failed - TU_LOG_DRV(" IO read() failed\r\n"); - - // set sense - msc_cbw_t const* p_cbw = &p_msc->cbw; - set_sense_medium_not_present(p_cbw->lun); - - fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); - } else if (nbytes == TUD_MSC_RET_BUSY) { - // zero means not ready -> fake a transfer complete so that this driver callback will fire again - dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false); - } else { + const uint8_t rhport = p_msc->rhport; + if (nbytes > 0) { TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes),); + } else { + // nbytes is status + switch (nbytes) { + case TUD_MSC_RET_ERROR: + // error -> endpoint is stalled & status in CSW set to failed + TU_LOG_DRV(" IO read() failed\r\n"); + set_sense_medium_not_present(p_msc->cbw.lun); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); + break; + + case TUD_MSC_RET_BUSY: + // not ready yet -> fake a transfer complete so that this driver callback will fire again + dcd_event_xfer_complete(rhport, p_msc->ep_in, 0, XFER_RESULT_SUCCESS, false); + break; + + default: break; + } } } @@ -886,13 +885,20 @@ static void proc_write10_host_data(mscd_interface_t* p_msc, uint32_t xferred_byt static void proc_write_io_data(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes) { if (nbytes < 0) { - // negative means error -> failed this scsi op - TU_LOG_DRV(" IO write() failed\r\n"); - set_sense_medium_not_present(p_msc->cbw.lun); - fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); + // nbytes is status + switch (nbytes) { + case TUD_MSC_RET_ERROR: + // IO error -> failed this scsi op + TU_LOG_DRV(" IO write() failed\r\n"); + set_sense_medium_not_present(p_msc->cbw.lun); + fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED); + break; + + default: break; + } } else { if ((uint32_t)nbytes < xferred_bytes) { - // Application consume less than what we got (including zero) + // Application consume less than what we got including TUD_MSC_RET_BUSY (0) const uint32_t left_over = xferred_bytes - (uint32_t)nbytes; if (nbytes > 0) { memmove(_mscd_epbuf.buf, _mscd_epbuf.buf + nbytes, left_over); diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h index 2ad31c245..f2ea256b4 100644 --- a/src/class/msc/msc_device.h +++ b/src/class/msc/msc_device.h @@ -79,12 +79,11 @@ bool tud_msc_async_io_done(int32_t bytes_io, bool in_isr); - 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 bytes read or status. - 0 < ret < bufsize: These bytes are transferred first and callback will be invoked again for remaining data. - - ret == TUD_MSC_RET_BUSY - Application is buys e.g disk I/O not ready. - Callback will be invoked again with the same parameters later on. - - ret == TUD_MSC_RET_ERROR + - TUD_MSC_RET_BUSY + Application is buys e.g disk I/O not ready. Callback will be invoked again with the same parameters later on. + - TUD_MSC_RET_ERROR error such as invalid address. This request will be STALLed and scsi command will be failed - - ret == TUD_MSC_RET_ASYNC + - TUD_MSC_RET_ASYNC Data I/O will be done asynchronously in a background task. Application should return immediately. tud_msc_async_io_done() must be called once IO/ is done to signal completion. */