From c9c9bfa0a505d137c724c254998745bb9e8c7dcc Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 17 Aug 2026 09:25:18 +1000 Subject: [PATCH 1/2] USB MSD: support multi-block transfers Process READ(10) and WRITE(10) requests in chunks sized by the caller-provided block buffer, and propagate transport and media failures. --- os/hal/include/hal_usb_msd.h | 2 +- os/hal/src/hal_usb_msd.c | 10 ++--- os/various/scsi_bindings/lib_scsi.c | 63 +++++++++++++++++++++++++---- os/various/scsi_bindings/lib_scsi.h | 8 +++- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/os/hal/include/hal_usb_msd.h b/os/hal/include/hal_usb_msd.h index 72c70897c8..5187845dcd 100644 --- a/os/hal/include/hal_usb_msd.h +++ b/os/hal/include/hal_usb_msd.h @@ -214,7 +214,7 @@ extern "C" { #endif void msdObjectInit(USBMassStorageDriver *msdp); void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, - BaseBlockDevice *blkdev, uint8_t *blkbuf, + BaseBlockDevice *blkdev, uint8_t *blkbuf, size_t blkbuf_size, uint8_t *txbuf, const scsi_inquiry_response_t *scsi_inquiry_response, const scsi_unit_serial_number_inquiry_response_t *serialInquiry, diff --git a/os/hal/src/hal_usb_msd.c b/os/hal/src/hal_usb_msd.c index c1c9499af7..a66afd1123 100644 --- a/os/hal/src/hal_usb_msd.c +++ b/os/hal/src/hal_usb_msd.c @@ -193,10 +193,6 @@ static uint32_t scsi_transport_transmit_async(const SCSITransport *transport, osalSysLock(); osalThreadResumeS(&trp->txworker, MSG_OK); osalSysUnlock(); - // if (trp->txlen > 0) { - // usbTransmit(trp->usbp, trp->ep, trp->txbuf, - // trp->txlen); - // } return len; } @@ -416,13 +412,14 @@ void msdStop(USBMassStorageDriver *msdp) { * @param[in] blkdev pointer to the @p BaseBlockDevice object * @param[in] blkbuf pointer to the working area buffer, must be allocated * by user, must be big enough to store 1 data block + * @param[in] blkbuf_size size of the working area buffer in bytes * @param[in] inquiry pointer to the SCSI inquiry response structure, * set it to @p NULL to use default hardcoded value. * * @api */ void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, - BaseBlockDevice *blkdev, uint8_t *blkbuf, + BaseBlockDevice *blkdev, uint8_t *blkbuf, size_t blkbuf_size, uint8_t *txbuf, const scsi_inquiry_response_t *inquiry, const scsi_unit_serial_number_inquiry_response_t *serialInquiry, @@ -430,7 +427,7 @@ void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, scsi_free_filesystem_access_t freeFilesystemAccess) { osalDbgCheck((msdp != NULL) && (usbp != NULL) - && (blkdev != NULL) && (blkbuf != NULL)); + && (blkdev != NULL) && (blkbuf != NULL) && (blkbuf_size > 0U)); osalDbgAssert((msdp->state == USB_MSD_STOP), "invalid state"); msdp->usbp = usbp; @@ -463,6 +460,7 @@ void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, msdp->scsi_config.unit_serial_number_inquiry_response = serialInquiry; } msdp->scsi_config.blkbuf = blkbuf; + msdp->scsi_config.blkbuf_size = blkbuf_size; msdp->scsi_config.blkdev = blkdev; msdp->scsi_config.transport = &msdp->scsi_transport; diff --git a/os/various/scsi_bindings/lib_scsi.c b/os/various/scsi_bindings/lib_scsi.c index ec9a62ac45..27a9b21c29 100644 --- a/os/various/scsi_bindings/lib_scsi.c +++ b/os/various/scsi_bindings/lib_scsi.c @@ -352,6 +352,7 @@ static bool data_overflow(SCSITarget *scsip, const data_request_t *req) { static bool data_read_write10(SCSITarget *scsip, const uint8_t *cmd) { data_request_t req = decode_data_request(cmd); + scsip->residue = 0; if (data_overflow(scsip, &req)) { return SCSI_FAILED; @@ -363,19 +364,65 @@ static bool data_read_write10(SCSITarget *scsip, const uint8_t *cmd) { blkGetInfo(blkdev, &bdi); size_t bs = bdi.blk_size; uint8_t *buf = scsip->config->blkbuf; + size_t max_blocks = bs > 0U ? scsip->config->blkbuf_size / bs : 0U; + + if (max_blocks == 0U) { + set_sense(scsip, SCSI_SENSE_KEY_HARDWARE_ERROR, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = req.blk_cnt * bs; + return SCSI_FAILED; + } size_t i = 0; - for (i=0; i max_blocks) { + n = max_blocks; + } + size_t len = n * bs; + if (cmd[0] == SCSI_CMD_READ_10) { - // TODO: block error handling - blkRead(blkdev, req.first_lba + i, buf, 1); - tr->transmit_async(tr, buf, bs); + if (blkRead(blkdev, req.first_lba + i, buf, n) != HAL_SUCCESS) { + set_sense(scsip, SCSI_SENSE_KEY_MEDIUM_ERROR, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = (req.blk_cnt - i) * bs; + return SCSI_FAILED; + } + uint32_t sent = tr->transmit(tr, buf, len); + if (sent != len) { + set_sense(scsip, SCSI_SENSE_KEY_ABORTED_COMMAND, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = (req.blk_cnt - i) * bs; + if (sent < len) { + scsip->residue -= sent; + } + return SCSI_FAILED; + } } else { - // TODO: block error handling - tr->receive(tr, buf, bs); - blkWrite(blkdev, req.first_lba + i, buf, 1); + uint32_t received = tr->receive(tr, buf, len); + if (received != len) { + set_sense(scsip, SCSI_SENSE_KEY_ABORTED_COMMAND, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = (req.blk_cnt - i) * bs; + if (received < len) { + scsip->residue -= received; + } + return SCSI_FAILED; + } + if (blkWrite(blkdev, req.first_lba + i, buf, n) != HAL_SUCCESS) { + set_sense(scsip, SCSI_SENSE_KEY_MEDIUM_ERROR, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = (req.blk_cnt - i - n) * bs; + return SCSI_FAILED; + } } + i += n; } } return SCSI_SUCCESS; @@ -545,4 +592,4 @@ uint32_t scsiResidue(const SCSITarget *scsip) { return scsip->residue; } -/** @} */ \ No newline at end of file +/** @} */ diff --git a/os/various/scsi_bindings/lib_scsi.h b/os/various/scsi_bindings/lib_scsi.h index 018742bf8b..95a05cd163 100644 --- a/os/various/scsi_bindings/lib_scsi.h +++ b/os/various/scsi_bindings/lib_scsi.h @@ -258,9 +258,13 @@ typedef struct { */ BaseBlockDevice *blkdev; /** - * @brief Pointer to data buffer for single block. + * @brief Pointer to block data buffer. */ uint8_t *blkbuf; + /** + * @brief Size of the block data buffer in bytes. + */ + size_t blkbuf_size; /** * @brief Pointer to SCSI inquiry response object. */ @@ -319,4 +323,4 @@ extern "C" { #endif /* LIB_SCSI_H_ */ -/** @} */ \ No newline at end of file +/** @} */ From 4eb67a0c5fb0516e640fda83d3d967c788002a29 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 17 Aug 2026 11:17:21 +1000 Subject: [PATCH 2/2] USB MSD: pipeline block and USB transfers --- os/hal/include/hal_usb_msd.h | 45 +++++-- os/hal/src/hal_usb_msd.c | 181 +++++++++++++++++++--------- os/various/scsi_bindings/lib_scsi.c | 136 ++++++++++++++++----- os/various/scsi_bindings/lib_scsi.h | 48 +++++++- 4 files changed, 311 insertions(+), 99 deletions(-) diff --git a/os/hal/include/hal_usb_msd.h b/os/hal/include/hal_usb_msd.h index 5187845dcd..1e1a3a228a 100644 --- a/os/hal/include/hal_usb_msd.h +++ b/os/hal/include/hal_usb_msd.h @@ -123,24 +123,49 @@ typedef struct { /** - * @brief Tx Thread working area. + * @brief Asynchronous I/O thread working area. */ - THD_WORKING_AREA( waMSDTxWorker, USB_MSD_THREAD_WA_SIZE); + THD_WORKING_AREA( waMSDIOWorker, USB_MSD_THREAD_WA_SIZE); /** - * @brief Tx Thread handler. + * @brief Asynchronous I/O thread handler. */ - thread_reference_t txworker; + thread_t *ioworker; /** - * @brief USB Transmit Buffer + * @brief Signals an asynchronous I/O request. */ - uint8_t *txbuf; + binary_semaphore_t io_start; /** - * @brief USB Transmit Length + * @brief Signals asynchronous I/O completion. */ - size_t txlen; + binary_semaphore_t io_done; + + /** + * @brief Asynchronous I/O buffer. + */ + uint8_t *iobuf; + + /** + * @brief Asynchronous I/O length. + */ + size_t iolen; + + /** + * @brief Result of the asynchronous I/O operation. + */ + uint32_t io_result; + + /** + * @brief True when the asynchronous operation is a transmit. + */ + bool io_is_transmit; + + /** + * @brief True while an asynchronous operation is pending. + */ + bool io_pending; /** * @brief USB Transmit mutex @@ -214,8 +239,8 @@ extern "C" { #endif void msdObjectInit(USBMassStorageDriver *msdp); void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, - BaseBlockDevice *blkdev, uint8_t *blkbuf, size_t blkbuf_size, - uint8_t *txbuf, + BaseBlockDevice *blkdev, uint8_t *blkbuf_a, uint8_t *blkbuf_b, + size_t blkbuf_size, const scsi_inquiry_response_t *scsi_inquiry_response, const scsi_unit_serial_number_inquiry_response_t *serialInquiry, scsi_block_filesystem_access_t blockFilesystemAccess, diff --git a/os/hal/src/hal_usb_msd.c b/os/hal/src/hal_usb_msd.c index a66afd1123..511d61ae43 100644 --- a/os/hal/src/hal_usb_msd.c +++ b/os/hal/src/hal_usb_msd.c @@ -172,52 +172,108 @@ static uint32_t scsi_transport_transmit(const SCSITransport *transport, } /** - * @brief SCSI transport transmit async function. - * + * @brief SCSI transport receive function. + * * @param[in] transport pointer to the @p SCSITransport object * @param[in] data payload - * @param[in] len number of bytes to be transmitted - * - * @return Number of bytes put into buffer. + * @param[in] len number bytes to be received + * + * @return Number of successfully received bytes. * @notapi */ -static uint32_t scsi_transport_transmit_async(const SCSITransport *transport, - const uint8_t *data, size_t len) { +static uint32_t scsi_transport_receive(const SCSITransport *transport, + uint8_t *data, size_t len) { usb_scsi_transport_handler_t *trp = transport->handler; - // wait for previous tx to finish, if unfinished osalMutexLock(&trp->txmtx); - memcpy(trp->txbuf, data, len); - trp->txlen = len; + msg_t status = usbReceive(trp->usbp, trp->ep, data, len); osalMutexUnlock(&trp->txmtx); - osalSysLock(); - osalThreadResumeS(&trp->txworker, MSG_OK); - osalSysUnlock(); + if (MSG_RESET != status) + return status; + else + return 0; +} + +/** + * @brief Starts an asynchronous SCSI transport operation. + * + * @param[in] transport pointer to the @p SCSITransport object + * @param[in] data payload buffer + * @param[in] len number of bytes to transfer + * @param[in] transmit true for transmit, false for receive + * + * @return Number of bytes accepted for transfer. + * @notapi + */ +static uint32_t scsi_transport_start(const SCSITransport *transport, + uint8_t *data, size_t len, + bool transmit) { + + usb_scsi_transport_handler_t *trp = transport->handler; + if (trp->io_pending) { + return 0; + } + + trp->iobuf = data; + trp->iolen = len; + trp->io_is_transmit = transmit; + trp->io_pending = true; + chBSemSignal(&trp->io_start); return len; } /** - * @brief SCSI transport receive function. + * @brief Starts an asynchronous SCSI transport transmit. * * @param[in] transport pointer to the @p SCSITransport object * @param[in] data payload - * @param[in] len number bytes to be received + * @param[in] len number of bytes to transmit * - * @return Number of successfully received bytes. + * @return Number of bytes accepted for transfer. * @notapi */ -static uint32_t scsi_transport_receive(const SCSITransport *transport, - uint8_t *data, size_t len) { +static uint32_t scsi_transport_transmit_start(const SCSITransport *transport, + const uint8_t *data, + size_t len) { + + return scsi_transport_start(transport, (uint8_t *)data, len, true); +} + +/** + * @brief Starts an asynchronous SCSI transport receive. + * + * @param[in] transport pointer to the @p SCSITransport object + * @param[out] data payload buffer + * @param[in] len number of bytes to receive + * + * @return Number of bytes accepted for transfer. + * @notapi + */ +static uint32_t scsi_transport_receive_start(const SCSITransport *transport, + uint8_t *data, size_t len) { + + return scsi_transport_start(transport, data, len, false); +} + +/** + * @brief Waits for an asynchronous SCSI transport operation. + * + * @param[in] transport pointer to the @p SCSITransport object + * + * @return Number of bytes transferred. + * @notapi + */ +static uint32_t scsi_transport_wait(const SCSITransport *transport) { usb_scsi_transport_handler_t *trp = transport->handler; - osalMutexLock(&trp->txmtx); - msg_t status = usbReceive(trp->usbp, trp->ep, data, len); - osalMutexUnlock(&trp->txmtx); - if (MSG_RESET != status) - return status; - else + if (!trp->io_pending) { return 0; + } + + chBSemWait(&trp->io_done); + trp->io_pending = false; + return trp->io_result; } /** @@ -285,29 +341,36 @@ static THD_FUNCTION(usb_msd_worker, arg) { } /** - * @brief USB Transmit worker thread. + * @brief USB asynchronous I/O worker thread. * * @param[in] arg pointer to the @p USBMassStorageDriver object * * @notapi */ -static THD_FUNCTION(usb_msd_tx_worker, arg) { +static THD_FUNCTION(usb_msd_io_worker, arg) { USBMassStorageDriver *msdp = arg; - chRegSetThreadName("usb_msd_tx_worker"); - + usb_scsi_transport_handler_t *trp = &msdp->usb_scsi_transport_handler; + chRegSetThreadName("usb_msd_io_worker"); + while(! chThdShouldTerminateX()) { - osalSysLock(); - osalThreadSuspendS(&msdp->usb_scsi_transport_handler.txworker); - osalSysUnlock(); - osalMutexLock(&msdp->usb_scsi_transport_handler.txmtx); - if (msdp->usb_scsi_transport_handler.txlen > 0) { - usbTransmit(msdp->usbp, USB_MSD_DATA_EP, msdp->usb_scsi_transport_handler.txbuf, - msdp->usb_scsi_transport_handler.txlen); - msdp->usb_scsi_transport_handler.txlen = 0; + chBSemWait(&trp->io_start); + if (chThdShouldTerminateX()) { + break; } - osalMutexUnlock(&msdp->usb_scsi_transport_handler.txmtx); + + osalMutexLock(&trp->txmtx); + if (trp->io_is_transmit) { + msg_t status = usbTransmit(trp->usbp, trp->ep, trp->iobuf, trp->iolen); + trp->io_result = status == MSG_OK ? trp->iolen : 0; + } + else { + msg_t status = usbReceive(trp->usbp, trp->ep, trp->iobuf, trp->iolen); + trp->io_result = status == MSG_RESET ? 0 : status; + } + osalMutexUnlock(&trp->txmtx); + chBSemSignal(&trp->io_done); } - + chThdExit(MSG_OK); } @@ -368,7 +431,7 @@ void msdObjectInit(USBMassStorageDriver *msdp) { msdp->state = USB_MSD_STOP; msdp->usbp = NULL; msdp->worker = NULL; - msdp->usb_scsi_transport_handler.txworker = NULL; + msdp->usb_scsi_transport_handler.ioworker = NULL; scsiObjectInit(&msdp->scsi_target); } @@ -388,18 +451,14 @@ void msdStop(USBMassStorageDriver *msdp) { chThdTerminate(msdp->worker); chThdWait(msdp->worker); - chThdTerminate(msdp->usb_scsi_transport_handler.txworker); - osalSysLock(); - thread_t* tp = msdp->usb_scsi_transport_handler.txworker; - // resume thread so it can terminate - osalThreadResumeS(&tp, MSG_OK); - osalSysUnlock(); - chThdWait(msdp->usb_scsi_transport_handler.txworker); + chThdTerminate(msdp->usb_scsi_transport_handler.ioworker); + chBSemSignal(&msdp->usb_scsi_transport_handler.io_start); + chThdWait(msdp->usb_scsi_transport_handler.ioworker); scsiStop(&msdp->scsi_target); msdp->worker = NULL; - msdp->usb_scsi_transport_handler.txworker = NULL; + msdp->usb_scsi_transport_handler.ioworker = NULL; msdp->state = USB_MSD_STOP; msdp->usbp = NULL; } @@ -410,8 +469,8 @@ void msdStop(USBMassStorageDriver *msdp) { * @param[in] msdp pointer to the @p USBMassStorageDriver object * @param[in] usbp pointer to the @p USBDriver object * @param[in] blkdev pointer to the @p BaseBlockDevice object - * @param[in] blkbuf pointer to the working area buffer, must be allocated - * by user, must be big enough to store 1 data block + * @param[in] blkbuf_a pointer to the first working area buffer + * @param[in] blkbuf_b pointer to the second working area buffer * @param[in] blkbuf_size size of the working area buffer in bytes * @param[in] inquiry pointer to the SCSI inquiry response structure, * set it to @p NULL to use default hardcoded value. @@ -419,30 +478,37 @@ void msdStop(USBMassStorageDriver *msdp) { * @api */ void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, - BaseBlockDevice *blkdev, uint8_t *blkbuf, size_t blkbuf_size, - uint8_t *txbuf, + BaseBlockDevice *blkdev, uint8_t *blkbuf_a, uint8_t *blkbuf_b, + size_t blkbuf_size, const scsi_inquiry_response_t *inquiry, const scsi_unit_serial_number_inquiry_response_t *serialInquiry, scsi_block_filesystem_access_t blockFilesystemAccess, scsi_free_filesystem_access_t freeFilesystemAccess) { osalDbgCheck((msdp != NULL) && (usbp != NULL) - && (blkdev != NULL) && (blkbuf != NULL) && (blkbuf_size > 0U)); + && (blkdev != NULL) && (blkbuf_a != NULL) && (blkbuf_b != NULL) + && (blkbuf_size > 0U)); osalDbgAssert((msdp->state == USB_MSD_STOP), "invalid state"); msdp->usbp = usbp; msdp->usb_scsi_transport_handler.usbp = msdp->usbp; msdp->usb_scsi_transport_handler.ep = USB_MSD_DATA_EP; - msdp->usb_scsi_transport_handler.txbuf = txbuf; osalMutexObjectInit(&msdp->usb_scsi_transport_handler.txmtx); - msdp->usb_scsi_transport_handler.txworker = chThdCreateStatic(msdp->usb_scsi_transport_handler.waMSDTxWorker, sizeof(msdp->usb_scsi_transport_handler.waMSDTxWorker), - MSD_THD_PRIO, usb_msd_tx_worker, msdp); + chBSemObjectInit(&msdp->usb_scsi_transport_handler.io_start, true); + chBSemObjectInit(&msdp->usb_scsi_transport_handler.io_done, true); + msdp->usb_scsi_transport_handler.io_pending = false; + msdp->usb_scsi_transport_handler.ioworker = chThdCreateStatic( + msdp->usb_scsi_transport_handler.waMSDIOWorker, + sizeof(msdp->usb_scsi_transport_handler.waMSDIOWorker), MSD_THD_PRIO, + usb_msd_io_worker, msdp); msdp->scsi_transport.handler = &msdp->usb_scsi_transport_handler; msdp->scsi_transport.transmit = scsi_transport_transmit; - msdp->scsi_transport.transmit_async = scsi_transport_transmit_async; msdp->scsi_transport.receive = scsi_transport_receive; + msdp->scsi_transport.transmit_start = scsi_transport_transmit_start; + msdp->scsi_transport.receive_start = scsi_transport_receive_start; + msdp->scsi_transport.wait = scsi_transport_wait; msdp->scsi_transport.block_filesystem_access = blockFilesystemAccess; msdp->scsi_transport.free_filesystem_access = freeFilesystemAccess; @@ -459,7 +525,8 @@ void msdStart(USBMassStorageDriver *msdp, USBDriver *usbp, else { msdp->scsi_config.unit_serial_number_inquiry_response = serialInquiry; } - msdp->scsi_config.blkbuf = blkbuf; + msdp->scsi_config.blkbuf[0] = blkbuf_a; + msdp->scsi_config.blkbuf[1] = blkbuf_b; msdp->scsi_config.blkbuf_size = blkbuf_size; msdp->scsi_config.blkdev = blkdev; msdp->scsi_config.transport = &msdp->scsi_transport; diff --git a/os/various/scsi_bindings/lib_scsi.c b/os/various/scsi_bindings/lib_scsi.c index 27a9b21c29..981bd05b42 100644 --- a/os/various/scsi_bindings/lib_scsi.c +++ b/os/various/scsi_bindings/lib_scsi.c @@ -363,66 +363,148 @@ static bool data_read_write10(SCSITarget *scsip, const uint8_t *cmd) { BlockDeviceInfo bdi; blkGetInfo(blkdev, &bdi); size_t bs = bdi.blk_size; - uint8_t *buf = scsip->config->blkbuf; size_t max_blocks = bs > 0U ? scsip->config->blkbuf_size / bs : 0U; + uint32_t total_len = req.blk_cnt * bs; if (max_blocks == 0U) { set_sense(scsip, SCSI_SENSE_KEY_HARDWARE_ERROR, SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER); - scsip->residue = req.blk_cnt * bs; + scsip->residue = total_len; return SCSI_FAILED; } - size_t i = 0; - while (i < req.blk_cnt) { - size_t n = req.blk_cnt - i; - if (n > max_blocks) { - n = max_blocks; - } - size_t len = n * bs; + if (cmd[0] == SCSI_CMD_READ_10) { + size_t i = 0; + size_t pending_len = 0; + unsigned buf_idx = 0; + uint32_t transferred = 0; + + while (i < req.blk_cnt) { + size_t n = req.blk_cnt - i; + if (n > max_blocks) { + n = max_blocks; + } + size_t len = n * bs; + uint8_t *buf = scsip->config->blkbuf[buf_idx]; - if (cmd[0] == SCSI_CMD_READ_10) { if (blkRead(blkdev, req.first_lba + i, buf, n) != HAL_SUCCESS) { + if (pending_len > 0U) { + uint32_t sent = tr->wait(tr); + transferred += sent < pending_len ? sent : pending_len; + } set_sense(scsip, SCSI_SENSE_KEY_MEDIUM_ERROR, SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER); - scsip->residue = (req.blk_cnt - i) * bs; + scsip->residue = total_len - transferred; return SCSI_FAILED; } - uint32_t sent = tr->transmit(tr, buf, len); - if (sent != len) { + + if (pending_len > 0U) { + uint32_t sent = tr->wait(tr); + transferred += sent < pending_len ? sent : pending_len; + if (sent != pending_len) { + set_sense(scsip, SCSI_SENSE_KEY_ABORTED_COMMAND, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = total_len - transferred; + return SCSI_FAILED; + } + } + + if (tr->transmit_start(tr, buf, len) != len) { set_sense(scsip, SCSI_SENSE_KEY_ABORTED_COMMAND, SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER); - scsip->residue = (req.blk_cnt - i) * bs; - if (sent < len) { - scsip->residue -= sent; - } + scsip->residue = total_len - transferred; return SCSI_FAILED; } + pending_len = len; + i += n; + buf_idx ^= 1U; } - else { - uint32_t received = tr->receive(tr, buf, len); - if (received != len) { + + if (pending_len > 0U) { + uint32_t sent = tr->wait(tr); + transferred += sent < pending_len ? sent : pending_len; + if (sent != pending_len) { set_sense(scsip, SCSI_SENSE_KEY_ABORTED_COMMAND, SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER); - scsip->residue = (req.blk_cnt - i) * bs; - if (received < len) { - scsip->residue -= received; - } + scsip->residue = total_len - transferred; return SCSI_FAILED; } - if (blkWrite(blkdev, req.first_lba + i, buf, n) != HAL_SUCCESS) { + } + } + else if (req.blk_cnt > 0U) { + size_t i = 0; + size_t n = req.blk_cnt > max_blocks ? max_blocks : req.blk_cnt; + size_t pending_len = n * bs; + unsigned buf_idx = 0; + uint32_t transferred = 0; + + if (tr->receive_start(tr, scsip->config->blkbuf[buf_idx], + pending_len) != pending_len) { + set_sense(scsip, SCSI_SENSE_KEY_ABORTED_COMMAND, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = total_len; + return SCSI_FAILED; + } + + while (i < req.blk_cnt) { + uint32_t received = tr->wait(tr); + transferred += received < pending_len ? received : pending_len; + if (received != pending_len) { + set_sense(scsip, SCSI_SENSE_KEY_ABORTED_COMMAND, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = total_len - transferred; + return SCSI_FAILED; + } + + size_t next_i = i + n; + bool next_pending = false; + size_t next_n = 0; + size_t next_len = 0; + if (next_i < req.blk_cnt) { + next_n = req.blk_cnt - next_i; + if (next_n > max_blocks) { + next_n = max_blocks; + } + next_len = next_n * bs; + if (tr->receive_start(tr, scsip->config->blkbuf[buf_idx ^ 1U], + next_len) == next_len) { + next_pending = true; + } + } + + if (blkWrite(blkdev, req.first_lba + i, + scsip->config->blkbuf[buf_idx], n) != HAL_SUCCESS) { + if (next_pending) { + received = tr->wait(tr); + transferred += received < next_len ? received : next_len; + } set_sense(scsip, SCSI_SENSE_KEY_MEDIUM_ERROR, SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER); - scsip->residue = (req.blk_cnt - i - n) * bs; + scsip->residue = total_len - transferred; + return SCSI_FAILED; + } + + if (next_i < req.blk_cnt && !next_pending) { + set_sense(scsip, SCSI_SENSE_KEY_ABORTED_COMMAND, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, + SCSI_ASENSEQ_NO_QUALIFIER); + scsip->residue = total_len - transferred; return SCSI_FAILED; } + + i = next_i; + n = next_n; + pending_len = next_len; + buf_idx ^= 1U; } - i += n; } } return SCSI_SUCCESS; diff --git a/os/various/scsi_bindings/lib_scsi.h b/os/various/scsi_bindings/lib_scsi.h index 95a05cd163..b2414c4e5a 100644 --- a/os/various/scsi_bindings/lib_scsi.h +++ b/os/various/scsi_bindings/lib_scsi.h @@ -198,6 +198,34 @@ typedef uint32_t (*scsi_transport_transmit_t)(const SCSITransport *transport, typedef uint32_t (*scsi_transport_receive_t)(const SCSITransport *transport, uint8_t *data, size_t len); +/** + * @brief Type of an asynchronous SCSI transport transmit start call. + * + * @param[in] usbp pointer to the @p SCSITransport object + * @param[in] data pointer to payload buffer + * @param[in] len payload length + */ +typedef uint32_t (*scsi_transport_transmit_start_t)(const SCSITransport *transport, + const uint8_t *data, + size_t len); + +/** + * @brief Type of an asynchronous SCSI transport receive start call. + * + * @param[in] usbp pointer to the @p SCSITransport object + * @param[out] data pointer to receive buffer + * @param[in] len number of bytes to be received + */ +typedef uint32_t (*scsi_transport_receive_start_t)(const SCSITransport *transport, + uint8_t *data, size_t len); + +/** + * @brief Type of an asynchronous SCSI transport completion call. + * + * @param[in] usbp pointer to the @p SCSITransport object + */ +typedef uint32_t (*scsi_transport_wait_t)(const SCSITransport *transport); + /** * @brief Type of block filesystem call. * @@ -220,14 +248,24 @@ struct SCSITransport { scsi_transport_transmit_t transmit; /** - * @brief Transmit asynchronous + * @brief Receive call provided by lower level driver. */ - scsi_transport_transmit_t transmit_async; + scsi_transport_receive_t receive; /** - * @brief Receive call provided by lower level driver. + * @brief Start an asynchronous transmit operation. */ - scsi_transport_receive_t receive; + scsi_transport_transmit_start_t transmit_start; + + /** + * @brief Start an asynchronous receive operation. + */ + scsi_transport_receive_start_t receive_start; + + /** + * @brief Wait for an asynchronous transport operation. + */ + scsi_transport_wait_t wait; /** * @brief Block Filesystem access. @@ -260,7 +298,7 @@ typedef struct { /** * @brief Pointer to block data buffer. */ - uint8_t *blkbuf; + uint8_t *blkbuf[2]; /** * @brief Size of the block data buffer in bytes. */