summaryrefslogtreecommitdiff
path: root/src/class/msc/msc_device.c
diff options
context:
space:
mode:
authorhathach <[email protected]>2025-07-02 18:18:52 +0700
committerhathach <[email protected]>2025-07-02 18:18:52 +0700
commitcf3966efd9fc552d641ea1e76e5aaad3ace3afd2 (patch)
treed59add439dcbfcd6e2cd9fec9aaecc413e5b5509 /src/class/msc/msc_device.c
parentf10467e711017fb05a6e0ee496e451e2e8834549 (diff)
parent963971c6aa14411f0cc6f0c4f727d199c738e2a5 (diff)
Merge branch 'master' into fork/HiFiPhile/dcd_notif
Diffstat (limited to 'src/class/msc/msc_device.c')
-rw-r--r--src/class/msc/msc_device.c286
1 files changed, 175 insertions, 111 deletions
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index dd66bfb6f..747ad03ed 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -53,23 +53,26 @@ enum {
};
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;
+
+ uint8_t pending_io; // pending async IO
}mscd_interface_t;
static mscd_interface_t _mscd_itf;
@@ -82,31 +85,36 @@ 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_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_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);
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 +185,33 @@ 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_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));
+ }
+ }
+
+ #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
//--------------------------------------------------------------------+
@@ -214,15 +249,51 @@ 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);
}
+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_io;
+ 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);
+ }
+}
+
+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 reason to call this with BUSY here
+ }
+ usbd_defer_func(proc_async_io_done, (void *) (intptr_t) bytes_io, in_isr);
+ 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));
}
@@ -245,12 +316,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 +361,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 +416,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) {
@@ -365,7 +437,7 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
memcpy(p_cbw, _mscd_epbuf.buf, sizeof(msc_cbw_t));
TU_LOG_DRV(" SCSI Command [Lun%u]: %s\r\n", p_cbw->lun, tu_lookup_find(&_msc_scsi_cmd_table, p_cbw->command[0]));
- //TU_LOG_MEM(MSC_DEBUG, p_cbw, xferred_bytes, 2);
+ // TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, p_cbw, xferred_bytes, 2);
p_csw->signature = MSC_CSW_SIGNATURE;
p_csw->tag = p_cbw->tag;
@@ -382,12 +454,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 +472,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 +490,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);
+ // TU_LOG_DRV(" SCSI case 4 (Hi > Dn): %lu\r\n", p_cbw->total_bytes);
+ fail_scsi_op(p_msc, MSC_CSW_STATUS_FAILED);
} else {
// case 1 Hn = Dn: all good
p_msc->stage = MSC_STAGE_STATUS;
@@ -431,8 +503,8 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
} else {
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);
+ // TU_LOG_DRV(" SCSI case 2 (Hn < Di): %lu\r\n", p_cbw->total_bytes);
+ 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,11 +513,13 @@ bool mscd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t
}
}
}
- break;
+ break;
+ }
case MSC_STAGE_DATA:
TU_LOG_DRV(" SCSI Data [Lun%u]\r\n", p_cbw->lun);
- //TU_LOG_MEM(MSC_DEBUG, _mscd_epbuf.buf, xferred_bytes, 2);
+ TU_ASSERT(xferred_bytes <= CFG_TUD_MSC_EP_BUFSIZE); // sanity check to avoid buffer overflow
+ // TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, _mscd_epbuf.buf, xferred_bytes, 2);
if (SCSI_CMD_READ_10 == p_cbw->command[0]) {
p_msc->xferred_len += xferred_bytes;
@@ -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_host_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
}
@@ -489,10 +563,10 @@ 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(MSC_DEBUG, p_csw, xferred_bytes, 2);
+ // TU_LOG_MEM(CFG_TUD_MSC_LOG_LEVEL, p_csw, xferred_bytes, 2);
// Invoke complete callback if defined
// Note: There is racing issue with samd51 + qspi flash testing with arduino
@@ -517,9 +591,9 @@ 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
+ // Any xfer ended here is considered unknown error, ignore it
TU_LOG1(" Warning expect SCSI Status but received unknown data\r\n");
}
break;
@@ -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;
@@ -644,8 +699,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
@@ -677,8 +731,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,
@@ -698,8 +751,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,
@@ -720,8 +772,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
};
@@ -751,39 +802,49 @@ 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
- 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->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->pending_io = 0;
+ proc_read_io_data(p_msc, nbytes);
+ }
+}
- 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");
+static void proc_read_io_data(mscd_interface_t* p_msc, int32_t nbytes) {
+ 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;
- // set sense
- set_sense_medium_not_present(p_cbw->lun);
+ 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;
- fail_scsi_op(rhport, 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);
- } else {
- TU_ASSERT(usbd_edpt_xfer(rhport, p_msc->ep_in, _mscd_epbuf.buf, (uint16_t) nbytes),);
+ default: break;
+ }
}
}
-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,53 +856,56 @@ 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
- 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(uint8_t rhport, 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;
- int32_t nbytes = tud_msc_write10_cb(p_cbw->lun, lba, offset, _mscd_epbuf.buf, xferred_bytes);
-
- 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;
+ 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->pending_io = 0;
+ proc_write_io_data(p_msc, xferred_bytes, nbytes);
+ }
+}
- // Set sense
- set_sense_medium_not_present(p_cbw->lun);
+static void proc_write_io_data(mscd_interface_t* p_msc, uint32_t xferred_bytes, int32_t nbytes) {
+ if (nbytes < 0) {
+ // 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;
- fail_scsi_op(rhport, p_msc, MSC_CSW_STATUS_FAILED);
+ default: break;
+ }
} else {
- // Application consume less than what we got (including zero)
if ((uint32_t)nbytes < xferred_bytes) {
- uint32_t const left_over = xferred_bytes - (uint32_t)nbytes;
+ // 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) {
- p_msc->xferred_len += (uint16_t)nbytes;
memmove(_mscd_epbuf.buf, _mscd_epbuf.buf + nbytes, left_over);
}
- // simulate an transfer complete with adjusted parameters --> callback will be invoked with adjusted parameter
- 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;
@@ -851,7 +915,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);
}
}
}