diff options
Diffstat (limited to 'thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/class/msc/app_usbd_msc.c')
-rw-r--r-- | thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/class/msc/app_usbd_msc.c | 2365 |
1 files changed, 2365 insertions, 0 deletions
diff --git a/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/class/msc/app_usbd_msc.c b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/class/msc/app_usbd_msc.c new file mode 100644 index 0000000..a19de11 --- /dev/null +++ b/thirdparty/nRF5_SDK_15.0.0_a53641a/components/libraries/usbd/class/msc/app_usbd_msc.c @@ -0,0 +1,2365 @@ +/** + * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic + * Semiconductor ASA integrated circuit in a product or a software update for + * such product, must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#include "sdk_common.h" +#if NRF_MODULE_ENABLED(APP_USBD_MSC) + +#include <string.h> +#include <ctype.h> +#include "app_usbd.h" +#include "app_usbd_msc.h" +#include "app_usbd_string_desc.h" + +/** + * @defgroup app_usbd_msc_internal USBD MSC internals + * @{ + * @ingroup app_usbd_msc + * @internal + */ + +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_requestsense_t) == 6); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_requestsense_resp_t) == 18); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_inquiry_t) == 6); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_inquiry_resp_t) == 36); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_read6_t) == 6); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_write6_t) == 6); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_modesense6_t) == 6); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_modesense6_resp_t) == 4); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_readcapacity10_t) == 10); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_readcapacity10_resp_t) == 8); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_read10_t) == 10); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_write10_t) == 10); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_modesense10_t) == 10); +STATIC_ASSERT(sizeof(app_usbd_scsi_cmd_modesense10_resp_t) == 8); + +STATIC_ASSERT(sizeof(app_usbd_msc_cbw_t) == 31); +STATIC_ASSERT(sizeof(app_usbd_msc_csw_t) == 13); + +#define NRF_LOG_MODULE_NAME usbd_msc + +#if APP_USBD_MSC_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL APP_USBD_MSC_CONFIG_LOG_LEVEL +#define NRF_LOG_INFO_COLOR APP_USBD_MSC_CONFIG_INFO_COLOR +#define NRF_LOG_DEBUG_COLOR APP_USBD_MSC_CONFIG_DEBUG_COLOR +#else // APP_USBD_MSC_CONFIG_LOG_ENABLED +#define NRF_LOG_LEVEL 0 +#endif // APP_USBD_MSC_CONFIG_LOG_ENABLED +#include "nrf_log.h" +NRF_LOG_MODULE_REGISTER(); + +#define APP_USBD_MSC_IFACE_IDX 0 /**< Mass storage class interface index */ +#define APP_USBD_MSC_EPIN_IDX 0 /**< Mass storage class endpoint IN index */ +#define APP_USBD_MSC_EPOUT_IDX 1 /**< Mass storage class endpoint OUT index */ + +/** + * @brief Set request buffer busy flag + * + * @param[in] val Bitmask to set + * @param[in] id Buffer id + * */ +#define APP_USBD_MSC_REQ_BUSY_SET(val, id) SET_BIT(val, id) + +/** + * @brief Clear request buffer busy flag + * + * @param[in] val Bitmask to set + * @param[in] id Buffer id + * */ +#define APP_USBD_MSC_REQ_BUSY_CLR(val, id) CLR_BIT(val, id) + +#define APP_USBD_MSC_REQ_BUSY_FULL_MASK (0x03) /**< Request busy mask */ + +static void msc_blockdev_ev_handler(nrf_block_dev_t const * p_blk_dev, + nrf_block_dev_event_t const * p_event); + +/** + * @brief Auxiliary function to access MSC instance data + * + * @param[in] p_inst Class instance data + * + * @return MSC instance data @ref app_usbd_msc_t + */ +static inline app_usbd_msc_t const * msc_get(app_usbd_class_inst_t const * p_inst) +{ + ASSERT(p_inst != NULL); + return (app_usbd_msc_t const *)p_inst; +} + + +/** + * @brief Auxiliary function to access MSC context data + * + * @param[in] p_msc MSC instance data + * @return MSC context data @ref app_usbd_msc_ctx_t + */ +static inline app_usbd_msc_ctx_t * msc_ctx_get(app_usbd_msc_t const * p_msc) +{ + ASSERT(p_msc != NULL); + ASSERT(p_msc->specific.p_data != NULL); + return &p_msc->specific.p_data->ctx; +} + + +/** + * @brief Auxiliary function to access MSC IN endpoint address + * + * @param[in] p_inst Class instance data + * + * @return IN endpoint address + */ +static inline nrf_drv_usbd_ep_t ep_in_addr_get(app_usbd_class_inst_t const * p_inst) +{ + app_usbd_class_iface_conf_t const * class_iface; + + class_iface = app_usbd_class_iface_get(p_inst, APP_USBD_MSC_IFACE_IDX); + + app_usbd_class_ep_conf_t const * ep_cfg; + ep_cfg = app_usbd_class_iface_ep_get(class_iface, APP_USBD_MSC_EPIN_IDX); + + return app_usbd_class_ep_address_get(ep_cfg); +} + + +/** + * @brief Auxiliary function to access MSC OUT endpoint address + * + * @param[in] p_inst Class instance data + * + * @return OUT endpoint address + */ +static inline nrf_drv_usbd_ep_t ep_out_addr_get(app_usbd_class_inst_t const * p_inst) +{ + app_usbd_class_iface_conf_t const * class_iface; + + class_iface = app_usbd_class_iface_get(p_inst, APP_USBD_MSC_IFACE_IDX); + + app_usbd_class_ep_conf_t const * ep_cfg; + ep_cfg = app_usbd_class_iface_ep_get(class_iface, APP_USBD_MSC_EPOUT_IDX); + + return app_usbd_class_ep_address_get(ep_cfg); +} + + +/** + * @brief Command Block Wrapper trigger + * + * @param[in] p_inst Generic class instance + * @param[in] state Next state transition + * + * @return Standard error code + * */ +static ret_code_t cbw_wait_start(app_usbd_class_inst_t const * p_inst) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + nrf_drv_usbd_ep_t ep_addr_out = ep_out_addr_get(p_inst); + + NRF_LOG_DEBUG("cbw_wait_start"); + memset(&p_msc_ctx->cbw, 0, sizeof(app_usbd_msc_cbw_t)); + NRF_DRV_USBD_TRANSFER_OUT(cbw, &p_msc_ctx->cbw, sizeof(app_usbd_msc_cbw_t)); + ret_code_t ret = app_usbd_ep_transfer(ep_addr_out, &cbw); + if (ret == NRF_SUCCESS) + { + p_msc_ctx->state = APP_USBD_MSC_STATE_CBW; + } + + return ret; +} + + +/** + * @brief Command Status Wrapper trigger + * + * @param[in] p_inst Generic class instance + * @param[in] state Next state transition + * + * @return Standard error code + * */ +static ret_code_t csw_wait_start(app_usbd_class_inst_t const * p_inst, uint8_t status) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + nrf_drv_usbd_ep_t ep_addr_in = ep_in_addr_get(p_inst); + + memset(&p_msc_ctx->csw, 0, sizeof(app_usbd_msc_csw_t)); + memcpy(p_msc_ctx->csw.signature, APP_USBD_MSC_CSW_SIGNATURE, sizeof(p_msc_ctx->csw.signature)); + memcpy(p_msc_ctx->csw.tag, p_msc_ctx->cbw.tag, sizeof(p_msc_ctx->csw.tag)); + memcpy(p_msc_ctx->csw.residue, &p_msc_ctx->current.residue, sizeof(uint32_t)); + p_msc_ctx->csw.status = status; + + NRF_DRV_USBD_TRANSFER_IN(csw, &p_msc_ctx->csw, sizeof(app_usbd_msc_csw_t)); + ret_code_t ret = app_usbd_ep_transfer(ep_addr_in, &csw); + if (ret == NRF_SUCCESS) + { + p_msc_ctx->state = APP_USBD_MSC_STATE_CSW; + } + + return ret; +} + + +/** + * @brief IN transfer trigger + * + * @param[in] p_inst Generic class instance + * @param[in] p_buff IN transfer data buffer + * @param[in] size IN transfer size + * @param[in] state Next state transition + * + * @return Standard error code + * */ +static ret_code_t transfer_in_start(app_usbd_class_inst_t const * p_inst, + void const * p_buff, + size_t size, + app_usbd_msc_state_t state) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + NRF_LOG_DEBUG("transfer_in_start: p_buff: %p, size: %u", (uint32_t)p_buff, size); + + nrf_drv_usbd_ep_t ep_addr_in = ep_in_addr_get(p_inst); + + NRF_DRV_USBD_TRANSFER_IN(resp, p_buff, size); + ret_code_t ret = app_usbd_ep_transfer(ep_addr_in, &resp); + if (ret == NRF_SUCCESS) + { + p_msc_ctx->state = state; + } + return ret; +} + + +/** + * @brief MSC reset request handler @ref APP_USBD_MSC_REQ_BULK_RESET + * + * @param[in] p_inst Generic class instance + * + * */ +static void bulk_ep_reset(app_usbd_class_inst_t const * p_inst) +{ + nrf_drv_usbd_ep_t ep_addr_in = ep_in_addr_get(p_inst); + nrf_drv_usbd_ep_t ep_addr_out = ep_out_addr_get(p_inst); + + nrf_drv_usbd_ep_abort(ep_addr_in); + nrf_drv_usbd_ep_abort(ep_addr_out); +} + + +/** + * @brief OUT transfer trigger + * + * @param[in] p_inst Generic class instance + * @param[in] p_buff OUT transfer data buffer + * @param[in] size OUT transfer size + * @param[in] state Next state transition + * + * @return Standard error code + * */ +static ret_code_t transfer_out_start(app_usbd_class_inst_t const * p_inst, + void * p_buff, + size_t size, + app_usbd_msc_state_t state) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + NRF_LOG_DEBUG("transfer_out_start: p_buff: %p, size: %u", (uint32_t)p_buff, size); + nrf_drv_usbd_ep_t ep_addr_out = ep_out_addr_get(p_inst); + + NRF_DRV_USBD_TRANSFER_OUT(resp, p_buff, size); + ret_code_t ret = app_usbd_ep_transfer(ep_addr_out, &resp); + if (ret == NRF_SUCCESS) + { + p_msc_ctx->state = state; + } + return ret; +} + + +/** + * @brief Generic function to stall communication endpoints and mark error state + * + * Function used internally to stall all communication endpoints and mark current state. + * + * @param p_inst Generic class instance + * @param state State to set + */ +static void status_generic_error_stall(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_state_t state) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + nrf_drv_usbd_ep_t ep_in = ep_in_addr_get(p_inst); + nrf_drv_usbd_ep_t ep_out = ep_out_addr_get(p_inst); + + nrf_drv_usbd_ep_stall(ep_in); + nrf_drv_usbd_ep_stall(ep_out); + nrf_drv_usbd_ep_abort(ep_in); + nrf_drv_usbd_ep_abort(ep_out); + + p_msc_ctx->state = state; +} + + +/** + * @brief Start status stage of unsupported SCSI command + * + * @param[in,out] p_inst Generic class instance + * + * @return Standard error code + * + * @sa status_generic_error_stall + */ +static ret_code_t status_unsupported_start(app_usbd_class_inst_t const * p_inst) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + bool data_stage = uint32_decode(p_msc_ctx->cbw.datlen) != 0; + + if (!data_stage) + { + /* Try to transfer the response now */ + ret_code_t ret; + ret = csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_FAIL); + if (ret == NRF_SUCCESS) + { + return ret; + } + } + + /* Cannot transfer failed response in current command - stall the endpoints and postpone the answer */ + status_generic_error_stall(p_inst, APP_USBD_MSC_STATE_UNSUPPORTED); + if (data_stage) + { + if (!(p_msc_ctx->cbw.flags & APP_USBD_MSC_CBW_DIRECTION_IN)) + { + nrf_drv_usbd_transfer_out_drop(ep_out_addr_get(p_inst)); + } + /* Unsupported command so we did not process any data - mark it in current residue value */ + p_msc_ctx->current.residue = uint32_decode(p_msc_ctx->cbw.datlen); + } + return NRF_SUCCESS; +} + + +/** + * @brief Start status stage of CBW invalid + * + * @param[in,out] p_inst Generic class instance + * + * @sa status_generic_error_stall + */ +static void status_cbwinvalid_start(app_usbd_class_inst_t const * p_inst) +{ + status_generic_error_stall(p_inst, APP_USBD_MSC_STATE_CBW_INVALID); +} + + +/** + * @brief Start status stage of internal device error + * + * Kind of error that requires bulk reset but does not stall endpoint permanently - the correct + * answer is possible. + * + * @param[in] p_inst Generic class instance + * + * @sa status_generic_error_stall + */ +static void status_deverror_start(app_usbd_class_inst_t const * p_inst) +{ + status_generic_error_stall(p_inst, APP_USBD_MSC_STATE_DEVICE_ERROR); +} + + +/** + * @brief Internal SETUP standard IN request handler + * + * @param[in] p_inst Generic class instance + * @param[in] p_setup_ev Setup event + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + */ +static ret_code_t setup_req_std_in(app_usbd_class_inst_t const * p_inst, + app_usbd_setup_evt_t const * p_setup_ev) +{ + /* Only Get Descriptor standard IN request is supported by MSC class */ + if ((app_usbd_setup_req_rec(p_setup_ev->setup.bmRequestType) == APP_USBD_SETUP_REQREC_INTERFACE) + && + (p_setup_ev->setup.bmRequest == APP_USBD_SETUP_STDREQ_GET_DESCRIPTOR)) + { + size_t dsc_len = 0; + size_t max_size; + + uint8_t * p_trans_buff = app_usbd_core_setup_transfer_buff_get(&max_size); + + /* Try to find descriptor in class internals*/ + ret_code_t ret = app_usbd_class_descriptor_find( + p_inst, + p_setup_ev->setup.wValue.hb, + p_setup_ev->setup.wValue.lb, + p_trans_buff, + &dsc_len); + + if (ret != NRF_ERROR_NOT_FOUND) + { + ASSERT(dsc_len < NRF_DRV_USBD_EPSIZE); + return app_usbd_core_setup_rsp(&(p_setup_ev->setup), p_trans_buff, dsc_len); + } + } + return NRF_ERROR_NOT_SUPPORTED; +} + + +/** + * @brief Internal SETUP standard OUT request handler + * + * @param[in] p_inst Generic class instance + * @param[in] p_setup_ev Setup event + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + * @retval NRF_ERROR_FORBIDDEN if endpoint stall cannot be cleared because of internal state + */ +static ret_code_t setup_req_std_out(app_usbd_class_inst_t const * p_inst, + app_usbd_setup_evt_t const * p_setup_ev) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + app_usbd_setup_reqrec_t req_rec = app_usbd_setup_req_rec(p_setup_ev->setup.bmRequestType); + + if ((req_rec == APP_USBD_SETUP_REQREC_ENDPOINT) && + (p_setup_ev->setup.bmRequest == APP_USBD_SETUP_STDREQ_CLEAR_FEATURE) && + (p_setup_ev->setup.wValue.w == APP_USBD_SETUP_STDFEATURE_ENDPOINT_HALT)) + { + if (p_msc_ctx->state == APP_USBD_MSC_STATE_CBW_INVALID) + { + return NRF_ERROR_FORBIDDEN; + } + ret_code_t ret = NRF_SUCCESS; + /* Clearing endpoint here. It is done normally inside app_usbd, but we are overwritting this functionality */ + nrf_drv_usbd_ep_t ep_addr = (nrf_drv_usbd_ep_t)(p_setup_ev->setup.wIndex.lb); + nrf_drv_usbd_ep_dtoggle_clear(ep_addr); + nrf_drv_usbd_ep_stall_clear(ep_addr); + if (NRF_USBD_EPIN_CHECK(ep_addr)) + { + switch (p_msc_ctx->state) + { + case APP_USBD_MSC_STATE_UNSUPPORTED: + { + nrf_drv_usbd_ep_stall_clear(ep_out_addr_get(p_inst)); + /*Unsupported command handle: status stage*/ + ret = csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_FAIL); + if (ret != NRF_SUCCESS) + { + NRF_LOG_ERROR("Unexpected csw_wait_start on ep clear: %d", ret); + } + break; + } + + case APP_USBD_MSC_STATE_DEVICE_ERROR: + { + nrf_drv_usbd_ep_stall_clear(ep_out_addr_get(p_inst)); + /*Unsupported command handle: status stage*/ + ret = csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PE); + if (ret != NRF_SUCCESS) + { + NRF_LOG_ERROR("Unexpected csw_wait_start on ep clear: %d", ret); + } + break; + } + + default: + break; + } + } + return ret; + } + return NRF_ERROR_NOT_SUPPORTED; +} + + +/** + * @brief Internal SETUP class IN request handler + * + * @param[in] p_inst Generic class instance + * @param[in] p_setup_ev Setup event + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + */ +static ret_code_t setup_req_class_in(app_usbd_class_inst_t const * p_inst, + app_usbd_setup_evt_t const * p_setup_ev) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + + switch (p_setup_ev->setup.bmRequest) + { + case APP_USBD_MSC_REQ_GET_MAX_LUN: + { + + if (p_setup_ev->setup.wValue.w != 0) + { + break; + } + + if (p_setup_ev->setup.wLength.w != 1) + { + break; + } + + size_t tx_size; + uint16_t * p_tx_buff = app_usbd_core_setup_transfer_buff_get(&tx_size); + ASSERT(p_msc->specific.inst.block_devs_count != 0); + p_tx_buff[0] = p_msc->specific.inst.block_devs_count - 1; + + ret_code_t ret = cbw_wait_start(p_inst); + UNUSED_VARIABLE(ret); + return app_usbd_core_setup_rsp(&(p_setup_ev->setup), p_tx_buff, sizeof(uint8_t)); + } + + default: + break; + + } + + return NRF_ERROR_NOT_SUPPORTED; +} + + +/** + * @brief Internal SETUP class OUT request handler + * + * @param[in] p_inst Generic class instance + * @param[in] p_setup_ev Setup event + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + */ +static ret_code_t setup_req_class_out(app_usbd_class_inst_t const * p_inst, + app_usbd_setup_evt_t const * p_setup_ev) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + switch (p_setup_ev->setup.bmRequest) + { + case APP_USBD_MSC_REQ_BULK_RESET: + { + if (p_setup_ev->setup.wValue.w != 0) + { + break; + } + + if (p_setup_ev->setup.wLength.w != 0) + { + break; + } + + /* + * Reset internal state to be ready for next CBW + */ + NRF_LOG_DEBUG("bulk ep reset"); + bulk_ep_reset(p_inst); + + if (p_msc_ctx->state != APP_USBD_MSC_STATE_CBW) + { + ret_code_t ret = cbw_wait_start(p_inst); + UNUSED_VARIABLE(ret); + } + + return NRF_SUCCESS; + } + + default: + break; + } + + return NRF_ERROR_NOT_SUPPORTED; +} + + +/** + * @brief Control endpoint handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_setup_ev Setup event + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + */ +static ret_code_t setup_event_handler(app_usbd_class_inst_t const * p_inst, + app_usbd_setup_evt_t const * p_setup_ev) +{ + ASSERT(p_inst != NULL); + ASSERT(p_setup_ev != NULL); + + if (app_usbd_setup_req_dir(p_setup_ev->setup.bmRequestType) == APP_USBD_SETUP_REQDIR_IN) + { + switch (app_usbd_setup_req_typ(p_setup_ev->setup.bmRequestType)) + { + case APP_USBD_SETUP_REQTYPE_STD: + return setup_req_std_in(p_inst, p_setup_ev); + + case APP_USBD_SETUP_REQTYPE_CLASS: + return setup_req_class_in(p_inst, p_setup_ev); + + default: + break; + } + } + else /* APP_USBD_SETUP_REQDIR_OUT */ + { + switch (app_usbd_setup_req_typ(p_setup_ev->setup.bmRequestType)) + { + case APP_USBD_SETUP_REQTYPE_STD: + return setup_req_std_out(p_inst, p_setup_ev); + + case APP_USBD_SETUP_REQTYPE_CLASS: + return setup_req_class_out(p_inst, p_setup_ev); + + default: + break; + } + } + + return NRF_ERROR_NOT_SUPPORTED; +} + + +/** + * @brief Handle read6/read10 command data stage + * + * @param[in] p_inst Generic class instance + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + */ +static ret_code_t state_data_in_handle(app_usbd_class_inst_t const * p_inst) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + ret_code_t ret = NRF_SUCCESS; + + p_msc_ctx->current.trans_in_progress = false; + APP_USBD_MSC_REQ_BUSY_CLR(p_msc_ctx->current.req_busy_mask, p_msc_ctx->current.trans_req_id); + + if (p_msc_ctx->current.blk_datasize == 0 && p_msc_ctx->current.req_busy_mask == 0) + { + p_msc_ctx->current.blk_idx = p_msc_ctx->current.blk_size = 0; + ret = csw_wait_start(p_inst, p_msc_ctx->current.csw_status); + return ret; + } + + ASSERT(p_msc_ctx->current.blk_size != 0); + + if (p_msc_ctx->current.req_busy_mask == 0) + { + nrf_block_dev_t const * p_blkd = + p_msc->specific.inst.pp_block_devs[p_msc_ctx->current.lun]; + + /*Trigger new block read request*/ + p_msc_ctx->current.workbuff_pos ^= p_msc->specific.inst.block_buff_size; + uint32_t req_pos = p_msc_ctx->current.workbuff_pos != 0 ? 1 : 0; + APP_USBD_MSC_REQ_BUSY_SET(p_msc_ctx->current.req_busy_mask, req_pos); + + NRF_BLOCK_DEV_REQUEST(req, + p_msc_ctx->current.blk_idx, + p_msc_ctx->current.blk_count, + ((uint8_t *)p_msc->specific.inst.p_block_buff + + p_msc_ctx->current.workbuff_pos)); + + NRF_LOG_DEBUG("nrf_blk_dev_read_req 3: id: %u, count: %u, left: %u, ptr: %p", + req.blk_id, + req.blk_count, + p_msc_ctx->current.blk_datasize, + (uint32_t)req.p_buff); + + ret = nrf_blk_dev_read_req(p_blkd, &req); + NRF_LOG_DEBUG("nrf_blk_dev_read_req 3: ret: %u", ret); + return ret; + } + + uint32_t blk_size = p_msc_ctx->current.blk_size; + p_msc_ctx->current.trans_req_id ^= 1; + + nrf_block_req_t * p_req = &p_msc_ctx->current.req; + if (p_req->p_buff == NULL) + { + p_msc_ctx->current.trans_req_id ^= 1; + return NRF_SUCCESS; + } + + ret = transfer_in_start(p_inst, + p_req->p_buff, + p_req->blk_count * blk_size, + APP_USBD_MSC_STATE_DATA_IN); + + if (ret == NRF_SUCCESS) + { + /*Clear processed block request.*/ + memset(p_req, 0, sizeof(nrf_block_req_t)); + p_msc_ctx->current.trans_in_progress = true; + } + + return ret; +} + + +/** + * @brief Endpoint IN event handler + * + * @param[in] p_inst Generic class instance + * @param[in] p_setup_ev Setup event + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + */ +static ret_code_t endpoint_in_event_handler(app_usbd_class_inst_t const * p_inst, + app_usbd_complex_evt_t const * p_event) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + NRF_LOG_DEBUG("state: %d, ep in event, status: %d", + p_msc_ctx->state, + p_event->drv_evt.data.eptransfer.status); + + if (p_event->drv_evt.data.eptransfer.status != NRF_USBD_EP_OK) + { + return NRF_SUCCESS; + } + + ret_code_t ret = NRF_SUCCESS; + + switch (p_msc_ctx->state) + { + case APP_USBD_MSC_STATE_CMD_IN: + { + ret = csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); + break; + } + + case APP_USBD_MSC_STATE_DATA_IN: + { + ret = state_data_in_handle(p_inst); + break; + } + + case APP_USBD_MSC_STATE_CSW: + { + break; + } + + case APP_USBD_MSC_STATE_DATA_OUT: + { + break; + } + + case APP_USBD_MSC_STATE_UNSUPPORTED: + { + ret = NRF_ERROR_NOT_SUPPORTED; + break; + } + + default: + { + ret = NRF_ERROR_INTERNAL; + break; + } + } + + return ret; +} + + +/** + * @brief Helper function to calculate next block transfer block count + * + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * + * @return Blocks to transfer + * */ +static uint32_t next_transfer_blkcnt_calc(app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + uint32_t blkcnt = p_msc->specific.inst.block_buff_size / p_msc_ctx->current.blk_size; + + if (blkcnt > (p_msc_ctx->current.blk_datasize / p_msc_ctx->current.blk_size)) + { + blkcnt = p_msc_ctx->current.blk_datasize / p_msc_ctx->current.blk_size; + } + + return blkcnt; +} + + +/** + * @brief Helper function to calculate next transfer size + * + * @param[in] p_msc_ctx MSC context + * + * @return Blocks to transfer + * */ +static uint32_t next_transfer_size_calc(app_usbd_msc_ctx_t * p_msc_ctx) +{ + uint32_t blk_cnt = p_msc_ctx->current.blk_count; + uint32_t blk_size = p_msc_ctx->current.blk_size; + + return p_msc_ctx->current.blk_datasize > blk_size * blk_cnt ? + blk_size * blk_cnt : p_msc_ctx->current.blk_datasize; +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_TESTUNITREADY handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_testunitready(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: TESTUNITREADY"); + if (uint32_decode(p_msc_ctx->cbw.datlen) != 0) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.cdb_length != APP_USBD_SCSI_CMD_TESTUNITREADY_LEN) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_REQUESTSENSE handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_requestsense(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: REQUESTSENSE"); + app_usbd_scsi_cmd_requestsense_t const * p_reqs = (const void *)p_msc_ctx->cbw.cdb; + UNUSED_VARIABLE(p_reqs); + + if ((p_msc_ctx->cbw.cdb_length < sizeof(app_usbd_scsi_cmd_requestsense_t))) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + if (uint32_decode(p_msc_ctx->cbw.datlen) > sizeof(app_usbd_scsi_cmd_requestsense_resp_t)) + { + return status_unsupported_start(p_inst); + } + + p_msc_ctx->resp_len = uint32_decode(p_msc_ctx->cbw.datlen); + + if (p_msc_ctx->resp_len == 0) + { + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); + } + + memset(&p_msc_ctx->scsi_resp, 0, sizeof(app_usbd_scsi_cmd_requestsense_resp_t)); + p_msc_ctx->scsi_resp.requestsense.code = APP_USBD_SCSI_CMD_REQSENSE_CODE_VALID | + APP_USBD_SCSI_CMD_REQSENSE_CODE_CURRENT; + + p_msc_ctx->scsi_resp.requestsense.len = sizeof(app_usbd_scsi_cmd_requestsense_resp_t) - + offsetof(app_usbd_scsi_cmd_requestsense_resp_t, len); + + return transfer_in_start(p_inst, + &p_msc_ctx->scsi_resp, + p_msc_ctx->resp_len, + APP_USBD_MSC_STATE_CMD_IN); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_FORMAT_UNIT handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_formatunit(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: FORMAT_UNIT"); + return status_unsupported_start(p_inst); +} + + +static ret_code_t cmd_read_start(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + nrf_block_dev_t const * p_blkd = p_msc->specific.inst.pp_block_devs[p_msc_ctx->cbw.lun]; + + p_msc_ctx->current.trans_in_progress = false; + p_msc_ctx->current.req_busy_mask = 0; + p_msc_ctx->current.workbuff_pos = 0; + + APP_USBD_MSC_REQ_BUSY_SET(p_msc_ctx->current.req_busy_mask, 0); + NRF_BLOCK_DEV_REQUEST(req, + p_msc_ctx->current.blk_idx, + p_msc_ctx->current.blk_count, + ((uint8_t *)p_msc->specific.inst.p_block_buff + + p_msc_ctx->current.workbuff_pos)); + + NRF_LOG_DEBUG("cmd_read_start"); + NRF_LOG_DEBUG("nrf_blk_dev_read_req 1: id: %u, count: %u, left: %u, ptr: %p", + req.blk_id, + req.blk_count, + p_msc_ctx->current.blk_datasize, + (uint32_t)req.p_buff); + + ret_code_t ret = nrf_blk_dev_read_req(p_blkd, &req); + NRF_LOG_DEBUG("nrf_blk_dev_read_req 1: ret: %u", ret); + + return ret; +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_READ6 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_read6(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: READ6"); + app_usbd_scsi_cmd_read6_t const * p_read6 = (const void *)p_msc_ctx->cbw.cdb; + if (p_msc_ctx->cbw.cdb_length < sizeof(app_usbd_scsi_cmd_read6_t)) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + nrf_block_dev_t const * p_blkd = p_msc->specific.inst.pp_block_devs[p_msc_ctx->cbw.lun]; + + p_msc_ctx->current.lun = p_msc_ctx->cbw.lun; + p_msc_ctx->current.blk_idx = ((p_read6->mslba & 0x1F) << 16) | + uint16_big_decode(p_read6->lslba); + p_msc_ctx->current.blk_datasize = uint32_decode(p_msc_ctx->cbw.datlen); + p_msc_ctx->current.blk_size = nrf_blk_dev_geometry(p_blkd)->blk_size; + p_msc_ctx->current.blk_count = next_transfer_blkcnt_calc(p_msc, p_msc_ctx); + + if (p_msc_ctx->current.blk_datasize > p_msc_ctx->current.blk_size * p_read6->xfrlen) + { + p_msc_ctx->current.residue = p_msc_ctx->current.blk_datasize - + p_msc_ctx->current.blk_size * p_read6->xfrlen; + } + + return cmd_read_start(p_inst, p_msc, p_msc_ctx); +} + + +static ret_code_t cmd_write_start(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("cmd_write_start"); + ret_code_t ret = transfer_out_start(p_inst, + ((uint8_t *)p_msc->specific.inst.p_block_buff + + p_msc_ctx->current.workbuff_pos), + next_transfer_size_calc(p_msc_ctx), + APP_USBD_MSC_STATE_DATA_OUT); + + if (ret == NRF_SUCCESS) + { + p_msc_ctx->current.trans_req_id = 0; + APP_USBD_MSC_REQ_BUSY_SET(p_msc_ctx->current.req_busy_mask, + p_msc_ctx->current.trans_req_id); + p_msc_ctx->current.workbuff_pos ^= p_msc->specific.inst.block_buff_size; + p_msc_ctx->current.trans_in_progress = true; + } + + return ret; +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_WRITE6 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_write6(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: WRITE6"); + app_usbd_scsi_cmd_write6_t const * p_write6 = (const void *)p_msc_ctx->cbw.cdb; + if (p_msc_ctx->cbw.cdb_length < sizeof(app_usbd_scsi_cmd_write6_t)) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + nrf_block_dev_t const * p_blkd = p_msc->specific.inst.pp_block_devs[p_msc_ctx->cbw.lun]; + + p_msc_ctx->current.lun = p_msc_ctx->cbw.lun; + p_msc_ctx->current.blk_idx = (p_write6->mslba & 0x1F << 16) | + uint16_big_decode(p_write6->lslba); + p_msc_ctx->current.blk_datasize = uint32_decode(p_msc_ctx->cbw.datlen); + p_msc_ctx->current.blk_size = nrf_blk_dev_geometry(p_blkd)->blk_size; + p_msc_ctx->current.blk_count = next_transfer_blkcnt_calc(p_msc, p_msc_ctx); + + if (p_msc_ctx->current.blk_datasize > p_msc_ctx->current.blk_size * p_write6->xfrlen) + { + p_msc_ctx->current.residue = p_msc_ctx->current.blk_datasize - + p_msc_ctx->current.blk_size * p_write6->xfrlen; + } + + return cmd_write_start(p_inst, p_msc, p_msc_ctx); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_INQUIRY handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_inquiry(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: INQUIRY"); + app_usbd_scsi_cmd_inquiry_t const * p_inq = (const void *)p_msc_ctx->cbw.cdb; + if (p_inq->pagecode != 0) + { + NRF_LOG_WARNING("unsupported pagecode"); + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + NRF_LOG_WARNING("unsupported LUN"); + return status_unsupported_start(p_inst); + } + + if (uint32_decode(p_msc_ctx->cbw.datlen) > sizeof(app_usbd_scsi_cmd_inquiry_resp_t)) + { + return status_unsupported_start(p_inst); + } + + p_msc_ctx->resp_len = uint32_decode(p_msc_ctx->cbw.datlen); + + if (p_msc_ctx->resp_len == 0) + { + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); + } + + + p_msc_ctx->scsi_resp.inquiry.qualtype = APP_USBD_MSC_SCSI_INQ_QUAL_CONNECTED | + APP_USBD_MSC_SCSI_INQ_TYPE_DIR_ACCESS; + p_msc_ctx->scsi_resp.inquiry.flags1 = APP_USBD_MSC_SCSI_INQ_FLAG1_RMB; + p_msc_ctx->scsi_resp.inquiry.version = APP_USBD_SCSI_INQ_VER_SPC4; + p_msc_ctx->scsi_resp.inquiry.flags2 = APP_USBD_MSC_SCSI_INQ_FLAG2_RSP_SPC2 | + APP_USBD_MSC_SCSI_INQ_FLAG2_HISUP; + p_msc_ctx->scsi_resp.inquiry.len = sizeof(app_usbd_scsi_cmd_inquiry_resp_t) - + offsetof(app_usbd_scsi_cmd_inquiry_resp_t, len); + + nrf_block_dev_t const * p_blkd = + p_msc->specific.inst.pp_block_devs[p_msc_ctx->cbw.lun]; + nrf_block_dev_info_strings_t * p_strings = NULL; + UNUSED_RETURN_VALUE(nrf_blk_dev_ioctl(p_blkd, + NRF_BLOCK_DEV_IOCTL_REQ_INFO_STRINGS, + &p_strings)); + + if (p_strings) + { + UNUSED_RETURN_VALUE(strncpy((char *)p_msc_ctx->scsi_resp.inquiry.vendorid, + p_strings->p_vendor, + sizeof(p_msc_ctx->scsi_resp.inquiry.vendorid))); + + UNUSED_RETURN_VALUE(strncpy((char *)p_msc_ctx->scsi_resp.inquiry.productid, + p_strings->p_product, + sizeof(p_msc_ctx->scsi_resp.inquiry.productid))); + + UNUSED_RETURN_VALUE(strncpy((char *)p_msc_ctx->scsi_resp.inquiry.revision, + p_strings->p_revision, + sizeof(p_msc_ctx->scsi_resp.inquiry.revision))); + } + else + { + memset(p_msc_ctx->scsi_resp.inquiry.vendorid, + 0, + sizeof(p_msc_ctx->scsi_resp.inquiry.vendorid)); + memset(p_msc_ctx->scsi_resp.inquiry.productid, + 0, + sizeof(p_msc_ctx->scsi_resp.inquiry.productid)); + memset(p_msc_ctx->scsi_resp.inquiry.revision, + 0, + sizeof(p_msc_ctx->scsi_resp.inquiry.revision)); + } + + return transfer_in_start(p_inst, + &p_msc_ctx->scsi_resp, + p_msc_ctx->resp_len, + APP_USBD_MSC_STATE_CMD_IN); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_MODESELECT6 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_modeselect6(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: MODESELECT6"); + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_MODESENSE6 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_modesense6(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: MODESENSE6"); + app_usbd_scsi_cmd_modesense6_t const * p_sense6 = (const void *)p_msc_ctx->cbw.cdb; + UNUSED_VARIABLE(p_sense6); + if (p_msc_ctx->cbw.cdb_length < sizeof(app_usbd_scsi_cmd_modesense6_t)) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + if (uint32_decode(p_msc_ctx->cbw.datlen) > sizeof(app_usbd_scsi_cmd_modesense6_resp_t)) + { + return status_unsupported_start(p_inst); + } + + p_msc_ctx->resp_len = uint32_decode(p_msc_ctx->cbw.datlen); + + app_usbd_scsi_cmd_modesense6_resp_t * p_resp = &p_msc_ctx->scsi_resp.modesense6; + p_resp->mdlen = sizeof(app_usbd_scsi_cmd_modesense6_resp_t) - 1; + p_resp->type = 0; + p_resp->param = 0; + p_resp->bdlen = 0; + + return transfer_in_start(p_inst, + &p_msc_ctx->scsi_resp, + p_msc_ctx->resp_len, + APP_USBD_MSC_STATE_CMD_IN); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_STARTSTOPUNIT handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_startstopunit(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: STARTSTOPUNIT"); + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_SENDDIAGNOSTIC handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_senddiagnostic(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: SENDDIAGNOSTIC"); + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_PREVENTMEDIAREMOVAL handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_preventremoval(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: PREVENTMEDIAREMOVAL"); + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_READCAPACITY10 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_readcapacity10(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: READCAPACITY10"); + + app_usbd_scsi_cmd_readcapacity10_t const * p_cap10 = (const void *)p_msc_ctx->cbw.cdb; + UNUSED_VARIABLE(p_cap10); + if (p_msc_ctx->cbw.cdb_length < sizeof(app_usbd_scsi_cmd_readcapacity10_t)) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + if (uint32_decode(p_msc_ctx->cbw.datlen) > sizeof(app_usbd_scsi_cmd_readcapacity10_resp_t)) + { + return status_unsupported_start(p_inst); + } + + p_msc_ctx->resp_len = uint32_decode(p_msc_ctx->cbw.datlen); + + if (p_msc_ctx->resp_len == 0) + { + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); + } + + nrf_block_dev_t const * p_blkd = + p_msc->specific.inst.pp_block_devs[p_msc_ctx->cbw.lun]; + nrf_block_dev_geometry_t const * p_geometry = nrf_blk_dev_geometry(p_blkd); + + (void)uint32_big_encode(p_geometry->blk_count - 1, p_msc_ctx->scsi_resp.readcapacity10.lba); + (void)uint32_big_encode(p_geometry->blk_size, p_msc_ctx->scsi_resp.readcapacity10.blklen); + + return transfer_in_start(p_inst, + &p_msc_ctx->scsi_resp, + p_msc_ctx->resp_len, + APP_USBD_MSC_STATE_CMD_IN); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_READ10 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_read10(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: READ10"); + app_usbd_scsi_cmd_read10_t const * p_read10 = (const void *)p_msc_ctx->cbw.cdb; + if (p_msc_ctx->cbw.cdb_length < sizeof(app_usbd_scsi_cmd_read10_t)) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + if (uint32_decode(p_msc_ctx->cbw.datlen) == 0) + { + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_FAIL); + } + + if ((p_msc_ctx->cbw.flags & APP_USBD_MSC_CBW_DIRECTION_IN) == 0) + { + return status_unsupported_start(p_inst); + } + + nrf_block_dev_t const * p_blkd = p_msc->specific.inst.pp_block_devs[p_msc_ctx->cbw.lun]; + + p_msc_ctx->current.lun = p_msc_ctx->cbw.lun; + p_msc_ctx->current.blk_idx = uint32_big_decode(p_read10->lba); + p_msc_ctx->current.blk_datasize = uint32_decode(p_msc_ctx->cbw.datlen); + p_msc_ctx->current.blk_size = nrf_blk_dev_geometry(p_blkd)->blk_size; + p_msc_ctx->current.blk_count = next_transfer_blkcnt_calc(p_msc, p_msc_ctx); + + uint16_t blocks = uint16_big_decode(p_read10->xfrlen); + p_msc_ctx->current.csw_status = + uint32_decode(p_msc_ctx->cbw.datlen) < p_msc_ctx->current.blk_size * blocks ? + APP_USBD_MSC_CSW_STATUS_FAIL : APP_USBD_MSC_CSW_STATUS_PASS; + + if (p_msc_ctx->current.blk_datasize > p_msc_ctx->current.blk_size * blocks) + { + p_msc_ctx->current.residue = p_msc_ctx->current.blk_datasize - + p_msc_ctx->current.blk_size * blocks; + } + + return cmd_read_start(p_inst, p_msc, p_msc_ctx); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_WRITE10 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_write10(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: WRITE10"); + app_usbd_scsi_cmd_write10_t const * p_write10 = (const void *)p_msc_ctx->cbw.cdb; + if (p_msc_ctx->cbw.cdb_length < sizeof(app_usbd_scsi_cmd_write10_t)) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + if (uint32_decode(p_msc_ctx->cbw.datlen) == 0) + { + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_FAIL); + } + + + if ((p_msc_ctx->cbw.flags & APP_USBD_MSC_CBW_DIRECTION_IN) != 0) + { + return status_unsupported_start(p_inst); + } + + nrf_block_dev_t const * p_blkd = p_msc->specific.inst.pp_block_devs[p_msc_ctx->cbw.lun]; + + p_msc_ctx->current.lun = p_msc_ctx->cbw.lun; + p_msc_ctx->current.blk_idx = uint32_big_decode(p_write10->lba); + p_msc_ctx->current.blk_datasize = uint32_decode(p_msc_ctx->cbw.datlen); + p_msc_ctx->current.blk_size = nrf_blk_dev_geometry(p_blkd)->blk_size; + p_msc_ctx->current.blk_count = next_transfer_blkcnt_calc(p_msc, p_msc_ctx); + + uint16_t blocks = uint16_big_decode(p_write10->xfrlen); + p_msc_ctx->current.csw_status = + uint32_decode(p_msc_ctx->cbw.datlen) < p_msc_ctx->current.blk_size * blocks ? + APP_USBD_MSC_CSW_STATUS_FAIL : APP_USBD_MSC_CSW_STATUS_PASS; + + if (p_msc_ctx->current.blk_datasize > p_msc_ctx->current.blk_size * blocks) + { + p_msc_ctx->current.residue = p_msc_ctx->current.blk_datasize - + p_msc_ctx->current.blk_size * blocks; + } + + return cmd_write_start(p_inst, p_msc, p_msc_ctx); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_MODESELECT10 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_modeselect10(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: MODESELECT10"); + return csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); +} + + +/** + * @brief SCSI Command: @ref APP_USBD_SCSI_CMD_MODESENSE10 handle + * + * @param[in] p_inst Generic class instance + * @param[in] p_msc MSC instance + * @param[in] p_msc_ctx MSC context + * @return Standard error code + * */ +static ret_code_t cmd_modesense10(app_usbd_class_inst_t const * p_inst, + app_usbd_msc_t const * p_msc, + app_usbd_msc_ctx_t * p_msc_ctx) +{ + NRF_LOG_DEBUG("CMD: MODESENSE10"); + app_usbd_scsi_cmd_modesense10_t const * p_sense10 = (const void *)p_msc_ctx->cbw.cdb; + UNUSED_VARIABLE(p_sense10); + if (p_msc_ctx->cbw.cdb_length < sizeof(app_usbd_scsi_cmd_modesense10_t)) + { + return status_unsupported_start(p_inst); + } + + if (p_msc_ctx->cbw.lun >= p_msc->specific.inst.block_devs_count) + { + return status_unsupported_start(p_inst); + } + + if (uint32_decode(p_msc_ctx->cbw.datlen) > sizeof(app_usbd_scsi_cmd_modesense6_resp_t)) + { + return status_unsupported_start(p_inst); + } + + p_msc_ctx->resp_len = uint32_decode(p_msc_ctx->cbw.datlen); + + app_usbd_scsi_cmd_modesense10_resp_t * p_resp = &p_msc_ctx->scsi_resp.modesense10; + + memset(p_resp, 0, sizeof(app_usbd_scsi_cmd_modesense10_resp_t)); + uint16_t len = sizeof(app_usbd_scsi_cmd_modesense10_resp_t) - sizeof(p_resp->mdlen); + p_resp->mdlen[1] = len & 0xff; + p_resp->mdlen[0] = (len >> 8) & 0xff; + + return transfer_in_start(p_inst, + &p_msc_ctx->scsi_resp, + p_msc_ctx->resp_len, + APP_USBD_MSC_STATE_CMD_IN); +} + + +/** + * @brief Get the size of the last OUT transfer + * + * @param p_inst Generic class instance + * + * @return Number of received bytes or 0 if none. + */ +static size_t get_last_out_size(app_usbd_class_inst_t const * p_inst) +{ + nrf_drv_usbd_ep_t ep = ep_out_addr_get(p_inst); + size_t size; + ret_code_t ret = nrf_drv_usbd_ep_status_get(ep, &size); + + if (ret != NRF_SUCCESS) + { + size = 0; + } + + return size; +} + + +/** + * @brief SCSI Command Block Wrapper handler + * + * @param[in] p_inst Generic class instance + * @return Standard error code + * */ +static ret_code_t state_cbw(app_usbd_class_inst_t const * p_inst) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + memset(&p_msc_ctx->current, 0, sizeof(p_msc_ctx->current)); + + /*Verify the transfer size*/ + if (get_last_out_size(p_inst) != sizeof(app_usbd_msc_cbw_t)) + { + NRF_LOG_DEBUG("CMD: size error: %u", get_last_out_size(p_inst)); + status_cbwinvalid_start(p_inst); + return NRF_SUCCESS; + } + + /*Verify CBW signature*/ + if (memcmp(p_msc_ctx->cbw.signature, + APP_USBD_MSC_CBW_SIGNATURE, + sizeof(p_msc_ctx->cbw.signature)) != 0) + { + NRF_LOG_DEBUG("CMD: header error: 0x%02x%02x%02x%02x", + p_msc_ctx->cbw.signature[0], p_msc_ctx->cbw.signature[1], + p_msc_ctx->cbw.signature[2], p_msc_ctx->cbw.signature[3]); + + status_cbwinvalid_start(p_inst); + return NRF_SUCCESS; + } + + ret_code_t ret = NRF_SUCCESS; + + switch (p_msc_ctx->cbw.cdb[0]) + { + case APP_USBD_SCSI_CMD_TESTUNITREADY: + ret = cmd_testunitready(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_REQUESTSENSE: + ret = cmd_requestsense(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_FORMAT_UNIT: + ret = cmd_formatunit(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_READ6: + ret = cmd_read6(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_WRITE6: + ret = cmd_write6(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_INQUIRY: + ret = cmd_inquiry(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_MODESELECT6: + ret = cmd_modeselect6(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_MODESENSE6: + ret = cmd_modesense6(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_STARTSTOPUNIT: + ret = cmd_startstopunit(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_SENDDIAGNOSTIC: + ret = cmd_senddiagnostic(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_PREVENTMEDIAREMOVAL: + ret = cmd_preventremoval(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_READCAPACITY10: + ret = cmd_readcapacity10(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_READ10: + ret = cmd_read10(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_WRITE10: + ret = cmd_write10(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_MODESELECT10: + ret = cmd_modeselect10(p_inst, p_msc, p_msc_ctx); + break; + + case APP_USBD_SCSI_CMD_MODESENSE10: + ret = cmd_modesense10(p_inst, p_msc, p_msc_ctx); + break; + + default: + NRF_LOG_DEBUG("CMD: UNSUPPORTED"); + NRF_LOG_HEXDUMP_DEBUG(&(p_msc_ctx->cbw), sizeof(p_msc_ctx->cbw)); + if (uint32_decode(p_msc_ctx->cbw.datlen) != 0) + { + ret = status_unsupported_start(p_inst); + } + else + { + ret = csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_FAIL); + } + break; + } + + return ret; +} + + +/** + * @brief Handle write6/write10 command data stage + * + * @param[in] p_inst Generic class instance + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + */ +static ret_code_t state_data_out_handle(app_usbd_class_inst_t const * p_inst) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + ret_code_t ret = NRF_SUCCESS; + uint32_t blk_size = p_msc_ctx->current.blk_size; + + NRF_LOG_DEBUG("APP_USBD_MSC_STATE_DATA_OUT"); + + p_msc_ctx->current.trans_in_progress = false; + if ((p_msc_ctx->current.req_busy_mask != APP_USBD_MSC_REQ_BUSY_FULL_MASK) && + (p_msc_ctx->current.blk_datasize > p_msc_ctx->current.blk_count * blk_size)) + { + size_t size = p_msc_ctx->current.blk_datasize - p_msc_ctx->current.blk_count * blk_size; + if (size > p_msc_ctx->current.blk_count * blk_size) + { + size = p_msc_ctx->current.blk_count * blk_size; + } + + if (size) + { + ret = transfer_out_start(p_inst, + ((uint8_t *)p_msc->specific.inst.p_block_buff + + p_msc_ctx->current.workbuff_pos), + size, + APP_USBD_MSC_STATE_DATA_OUT); + if (ret == NRF_SUCCESS) + { + p_msc_ctx->current.trans_req_id ^= 1; + APP_USBD_MSC_REQ_BUSY_SET(p_msc_ctx->current.req_busy_mask, + p_msc_ctx->current.trans_req_id); + p_msc_ctx->current.workbuff_pos ^= p_msc->specific.inst.block_buff_size; + p_msc_ctx->current.trans_in_progress = true; + } + } + } + + if (!p_msc_ctx->current.block_req_in_progress) + { + nrf_block_dev_t const * p_blkd = + p_msc->specific.inst.pp_block_devs[p_msc_ctx->current.lun]; + + size_t pos = p_msc_ctx->current.workbuff_pos; + if (p_msc_ctx->current.req_busy_mask != APP_USBD_MSC_REQ_BUSY_FULL_MASK) + { + pos ^= p_msc->specific.inst.block_buff_size; + } + + NRF_BLOCK_DEV_REQUEST(req, + p_msc_ctx->current.blk_idx, + p_msc_ctx->current.blk_count, + ((uint8_t *)p_msc->specific.inst.p_block_buff + pos)); + + NRF_LOG_DEBUG("nrf_blk_dev_write_req 1: id: %u, count: %u, left: %u, ptr: %p", + req.blk_id, + req.blk_count, + p_msc_ctx->current.blk_datasize, + (uint32_t)req.p_buff); + + p_msc_ctx->current.block_req_in_progress = true; + ret = nrf_blk_dev_write_req(p_blkd, &req); + NRF_LOG_DEBUG("nrf_blk_dev_write_req 1: ret: %u", ret); + } + + return ret; +} + + +/** + * @brief Endpoint OUT event handler + * + * @param[in] p_inst Generic class instance + * @param[in] p_setup_ev Setup event + * + * @return Standard error code + * @retval NRF_SUCCESS if request handled correctly + * @retval NRF_ERROR_NOT_SUPPORTED if request is not supported + */ +static ret_code_t endpoint_out_event_handler(app_usbd_class_inst_t const * p_inst, + app_usbd_complex_evt_t const * p_event) +{ + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + NRF_LOG_DEBUG("state: %d, ep out event, status: %d", + p_msc_ctx->state, + p_event->drv_evt.data.eptransfer.status); + + ret_code_t ret = NRF_SUCCESS; + if (p_event->drv_evt.data.eptransfer.status == NRF_USBD_EP_WAITING) + { + if (p_msc_ctx->state == APP_USBD_MSC_STATE_DATA_OUT) + { + NRF_LOG_DEBUG("NRF_USBD_EP_WAITING"); + } + else if (p_msc_ctx->state == APP_USBD_MSC_STATE_CSW || + p_msc_ctx->state == APP_USBD_MSC_STATE_IDLE) + { + ret = cbw_wait_start(p_inst); + } + + return ret; + } + else if (p_event->drv_evt.data.eptransfer.status == NRF_USBD_EP_ABORTED) + { + p_msc_ctx->state = APP_USBD_MSC_STATE_IDLE; + return NRF_SUCCESS; + } + else if (p_event->drv_evt.data.eptransfer.status == NRF_USBD_EP_OVERLOAD) + { + NRF_LOG_ERROR("Data overload"); + + switch (p_msc_ctx->state) + { + case APP_USBD_MSC_STATE_DATA_OUT: + { + status_deverror_start(p_inst); + } + + /* Default action is the same like CBW - stall totally until bulk reset */ + case APP_USBD_MSC_STATE_CBW: + default: + { + status_cbwinvalid_start(p_inst); + } + } + return NRF_SUCCESS; + } + else /*NRF_USBD_EP_OK*/ + { + switch (p_msc_ctx->state) + { + case APP_USBD_MSC_STATE_CBW: + { + ret = state_cbw(p_inst); + break; + } + + case APP_USBD_MSC_STATE_CMD_OUT: + { + ret = csw_wait_start(p_inst, APP_USBD_MSC_CSW_STATUS_PASS); + break; + } + + case APP_USBD_MSC_STATE_DATA_OUT: + { + CRITICAL_REGION_ENTER(); + ret = state_data_out_handle(p_inst); + CRITICAL_REGION_EXIT(); + break; + } + + case APP_USBD_MSC_STATE_UNSUPPORTED: + { + ret = NRF_ERROR_NOT_SUPPORTED; + break; + } + + case APP_USBD_MSC_STATE_CSW: + break; + + default: + { + ASSERT(0); + ret = NRF_ERROR_NOT_SUPPORTED; + break; + } + } + } + + NRF_LOG_DEBUG("Ep proc status: %d", ret); + return ret; +} + + +/** + * @brief @ref app_usbd_class_methods_t::event_handler + */ +static ret_code_t msc_event_handler(app_usbd_class_inst_t const * p_inst, + app_usbd_complex_evt_t const * p_event) +{ + ASSERT(p_inst != NULL); + ASSERT(p_event != NULL); + + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + UNUSED_VARIABLE(p_msc); + UNUSED_VARIABLE(p_msc_ctx); + + ret_code_t ret = NRF_SUCCESS; + + switch (p_event->app_evt.type) + { + case APP_USBD_EVT_DRV_SOF: + break; + + case APP_USBD_EVT_DRV_RESET: + break; + + case APP_USBD_EVT_DRV_SETUP: + ret = setup_event_handler(p_inst, (app_usbd_setup_evt_t const *)p_event); + break; + + case APP_USBD_EVT_DRV_EPTRANSFER: + if (NRF_USBD_EPIN_CHECK(p_event->drv_evt.data.eptransfer.ep)) + { + ret = endpoint_in_event_handler(p_inst, p_event); + } + else + { + ret = endpoint_out_event_handler(p_inst, p_event); + } + + break; + + case APP_USBD_EVT_DRV_SUSPEND: + { + /*Flush all block devices cache on suspend*/ + + for (size_t i = 0; i < p_msc->specific.inst.block_devs_count; ++i) + { + nrf_block_dev_t const * p_blkd = p_msc->specific.inst.pp_block_devs[i]; + (void)nrf_blk_dev_ioctl(p_blkd, NRF_BLOCK_DEV_IOCTL_REQ_CACHE_FLUSH, NULL); + } + + break; + } + + case APP_USBD_EVT_DRV_RESUME: + break; + + case APP_USBD_EVT_INST_APPEND: + { + /*Verify serial number string*/ + uint16_t const * p_serial_str = app_usbd_string_desc_get(APP_USBD_STRING_ID_SERIAL, 0); + if (p_serial_str == NULL) + { + return NRF_ERROR_NOT_SUPPORTED; + } + + size_t len = app_usbd_string_desc_length(p_serial_str) / sizeof(uint16_t); + if (len < APP_USBD_MSC_MINIMAL_SERIAL_STRING_SIZE) + { + return NRF_ERROR_NOT_SUPPORTED; + } + + for (size_t i = 1; i < len; ++i) + { + if (isxdigit(p_serial_str[i]) == 0) + { + return NRF_ERROR_NOT_SUPPORTED; + } + } + + break; + } + + case APP_USBD_EVT_INST_REMOVE: + { + break; + } + + case APP_USBD_EVT_STARTED: + { + /*Initialize all block devices*/ + ASSERT(p_msc->specific.inst.block_devs_count <= 16); + + for (size_t i = 0; i < p_msc->specific.inst.block_devs_count; ++i) + { + nrf_block_dev_t const * p_blk_dev = p_msc->specific.inst.pp_block_devs[i]; + ret = nrf_blk_dev_init(p_blk_dev, msc_blockdev_ev_handler, p_msc); + if (ret != NRF_SUCCESS) + { + continue; + } + + p_msc_ctx->blk_dev_init_mask |= 1u << i; + ASSERT(nrf_blk_dev_geometry(p_blk_dev)->blk_size <= + p_msc->specific.inst.block_buff_size); + } + + break; + } + + case APP_USBD_EVT_STOPPED: + { + /*Un-initialize all block devices*/ + ASSERT(p_msc->specific.inst.block_devs_count <= 16); + size_t i; + + for (i = 0; i < p_msc->specific.inst.block_devs_count; ++i) + { + nrf_block_dev_t const * p_blk_dev = p_msc->specific.inst.pp_block_devs[i]; + ret = nrf_blk_dev_uninit(p_blk_dev); + if (ret != NRF_SUCCESS) + { + continue; + } + + p_msc_ctx->blk_dev_init_mask &= ~(1u << i); + } + + break; + } + + default: + ret = NRF_ERROR_NOT_SUPPORTED; + break; + } + + return ret; +} + + +/** + * @brief @ref app_usbd_class_methods_t::feed_descriptors + */ + +static bool msc_feed_descriptors(app_usbd_class_descriptor_ctx_t * p_ctx, + app_usbd_class_inst_t const * p_inst, + uint8_t * p_buff, + size_t max_size) +{ + static uint8_t ifaces = 0; + ifaces = app_usbd_class_iface_count_get(p_inst); + app_usbd_msc_t const * p_msc = msc_get(p_inst); + + APP_USBD_CLASS_DESCRIPTOR_BEGIN(p_ctx, p_buff, max_size); + + static uint8_t i = 0; + + for (i = 0; i < ifaces; i++) + { + /* INTERFACE DESCRIPTOR */ + APP_USBD_CLASS_DESCRIPTOR_WRITE(0x09); // bLength + APP_USBD_CLASS_DESCRIPTOR_WRITE(APP_USBD_DESCRIPTOR_INTERFACE); // bDescriptorType = Interface + + static app_usbd_class_iface_conf_t const * p_cur_iface = NULL; + p_cur_iface = app_usbd_class_iface_get(p_inst, i); + + APP_USBD_CLASS_DESCRIPTOR_WRITE(app_usbd_class_iface_number_get(p_cur_iface)); // bInterfaceNumber + APP_USBD_CLASS_DESCRIPTOR_WRITE(0x00); // bAlternateSetting + APP_USBD_CLASS_DESCRIPTOR_WRITE(app_usbd_class_iface_ep_count_get(p_cur_iface)); // bNumEndpoints + APP_USBD_CLASS_DESCRIPTOR_WRITE(APP_USBD_MSC_CLASS); // bInterfaceClass = MSC + APP_USBD_CLASS_DESCRIPTOR_WRITE(p_msc->specific.inst.subclass); // bInterfaceSubclass (Industry Standard Command Block) + APP_USBD_CLASS_DESCRIPTOR_WRITE(p_msc->specific.inst.protocol); // bInterfaceProtocol + APP_USBD_CLASS_DESCRIPTOR_WRITE(0x00); // iInterface + + + static uint8_t endpoints = 0; + endpoints = app_usbd_class_iface_ep_count_get(p_cur_iface); + + static uint8_t j = 0; + + for (j = 0; j < endpoints; j++) + { + // ENDPOINT DESCRIPTOR + APP_USBD_CLASS_DESCRIPTOR_WRITE(0x07); // bLength + APP_USBD_CLASS_DESCRIPTOR_WRITE(APP_USBD_DESCRIPTOR_ENDPOINT); // bDescriptorType = Endpoint + + static app_usbd_class_ep_conf_t const * p_cur_ep = NULL; + p_cur_ep = app_usbd_class_iface_ep_get(p_cur_iface, j); + APP_USBD_CLASS_DESCRIPTOR_WRITE(app_usbd_class_ep_address_get(p_cur_ep)); // bEndpointAddress + APP_USBD_CLASS_DESCRIPTOR_WRITE(APP_USBD_DESCRIPTOR_EP_ATTR_TYPE_BULK); // bmAttributes + APP_USBD_CLASS_DESCRIPTOR_WRITE(LSB_16(NRF_DRV_USBD_EPSIZE)); // wMaxPacketSize LSB + APP_USBD_CLASS_DESCRIPTOR_WRITE(MSB_16(NRF_DRV_USBD_EPSIZE)); // wMaxPacketSize MSB + APP_USBD_CLASS_DESCRIPTOR_WRITE(0x00); // bInterval + } + + } + + APP_USBD_CLASS_DESCRIPTOR_END(); +} + +const app_usbd_class_methods_t app_usbd_msc_class_methods = { + .event_handler = msc_event_handler, + .feed_descriptors = msc_feed_descriptors, +}; + + +/** + * @brief Block device read done event handler. + * + * @ref NRF_BLOCK_DEV_EVT_BLK_READ_DONE + * + * @param p_blk_dev Block device handle + * @param p_event Block device event + * + * */ +static void msc_blockdev_read_done_handler(nrf_block_dev_t const * p_blk_dev, + nrf_block_dev_event_t const * p_event) +{ + ret_code_t ret; + + app_usbd_class_inst_t const * p_inst = p_event->p_context; + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + uint32_t blk_cnt = p_msc_ctx->current.blk_count; + uint32_t blk_size = p_msc_ctx->current.blk_size; + + /*Save actual request*/ + size_t req_pos = (p_msc_ctx->current.workbuff_pos != 0) ? 1 : 0; + + p_msc_ctx->current.req = *p_event->p_blk_req; + + p_msc_ctx->current.block_req_in_progress = false; + + /*Decrement transfer counter*/ + p_msc_ctx->current.blk_idx += blk_cnt; + if (p_msc_ctx->current.blk_datasize > blk_size * blk_cnt) + { + p_msc_ctx->current.blk_datasize -= blk_size * blk_cnt; + } + else + { + p_msc_ctx->current.blk_datasize = 0; + } + + NRF_LOG_DEBUG("read_done_handler: p_buff: %p, size: %u data size: %u req_pos: %u", + (uint32_t)p_event->p_blk_req->p_buff, + p_event->p_blk_req->blk_count, + p_msc_ctx->current.blk_datasize, + req_pos); + + /*Calculate next block request size*/ + p_msc_ctx->current.blk_count = next_transfer_blkcnt_calc(p_msc, p_msc_ctx); + + if (!p_msc_ctx->current.trans_in_progress) + { + /*Trigger new transfer.*/ + p_msc_ctx->current.trans_req_id = req_pos; + nrf_block_req_t * p_req = &p_msc_ctx->current.req; + + ret = transfer_in_start(p_inst, + p_req->p_buff, + p_req->blk_count * blk_size, + APP_USBD_MSC_STATE_DATA_IN); + if (ret != NRF_SUCCESS) + { + UNUSED_RETURN_VALUE(status_unsupported_start(p_inst)); + } + else + { + /*Clear processed block request.*/ + memset(p_req, 0, sizeof(nrf_block_req_t)); + p_msc_ctx->current.trans_in_progress = true; + } + + } + + if (p_msc_ctx->current.req_busy_mask == APP_USBD_MSC_REQ_BUSY_FULL_MASK) + { + /* No need to perform next block read request. USB transfers need to catch-up + * block device readings */ + return; + } + + if (p_msc_ctx->current.blk_count == 0) + { + /*All data has been read. No need to trigger new block request.*/ + return; + } + + /*Trigger new block read request*/ + p_msc_ctx->current.workbuff_pos ^= p_msc->specific.inst.block_buff_size; + req_pos = p_msc_ctx->current.workbuff_pos != 0 ? 1 : 0; + + APP_USBD_MSC_REQ_BUSY_SET(p_msc_ctx->current.req_busy_mask, req_pos); + + NRF_BLOCK_DEV_REQUEST(req, + p_msc_ctx->current.blk_idx, + p_msc_ctx->current.blk_count, + ((uint8_t *)p_msc->specific.inst.p_block_buff + + p_msc_ctx->current.workbuff_pos)); + + + NRF_LOG_DEBUG("nrf_blk_dev_read_req 2: id: %u, count: %u, left: %u, ptr: %p", + req.blk_id, + req.blk_count, + p_msc_ctx->current.blk_datasize, + (uint32_t)req.p_buff); + + ret = nrf_blk_dev_read_req(p_blk_dev, &req); + NRF_LOG_DEBUG("nrf_blk_dev_read_req 2: ret: %u", ret); + + if (ret != NRF_SUCCESS) + { + UNUSED_RETURN_VALUE(status_unsupported_start(p_inst)); + } +} + + +/** + * @brief Block device write done event handler. + * + * @ref NRF_BLOCK_DEV_EVT_BLK_WRITE_DONE + * + * @param p_blk_dev Block device handle + * @param p_event Block device event + * + * */ +static void msc_blockdev_write_done_handler(nrf_block_dev_t const * p_blk_dev, + nrf_block_dev_event_t const * p_event) +{ + app_usbd_class_inst_t const * p_inst = p_event->p_context; + app_usbd_msc_t const * p_msc = msc_get(p_inst); + app_usbd_msc_ctx_t * p_msc_ctx = msc_ctx_get(p_msc); + + uint32_t blk_cnt = p_msc_ctx->current.blk_count; + uint32_t blk_size = p_msc_ctx->current.blk_size; + + /*Save actual request*/ + size_t req_pos = (p_event->p_blk_req->p_buff == p_msc->specific.inst.p_block_buff) ? 0 : 1; + + p_msc_ctx->current.req = *p_event->p_blk_req; + + APP_USBD_MSC_REQ_BUSY_CLR(p_msc_ctx->current.req_busy_mask, req_pos); + p_msc_ctx->current.block_req_in_progress = false; + + p_msc_ctx->current.blk_idx += blk_cnt; + + if (p_msc_ctx->current.blk_datasize > blk_size * blk_cnt) + { + p_msc_ctx->current.blk_datasize -= blk_size * blk_cnt; + } + else + { + p_msc_ctx->current.blk_datasize = 0; + } + + NRF_LOG_DEBUG("write_done_handler: p_buff: %p, size: %u data size: %u req_pos: %u", + (uint32_t)p_event->p_blk_req->p_buff, + p_event->p_blk_req->blk_count, + p_msc_ctx->current.blk_datasize, + req_pos); + + if (p_msc_ctx->current.blk_datasize == 0) + { + p_msc_ctx->current.blk_idx = p_msc_ctx->current.blk_size = 0; + UNUSED_RETURN_VALUE(csw_wait_start(p_inst, p_msc_ctx->current.csw_status)); + return; + } + + if (p_msc_ctx->current.blk_datasize <= p_msc_ctx->current.blk_count * blk_size) + { + size_t pos = p_msc_ctx->current.workbuff_pos; + if (p_msc_ctx->current.req_busy_mask != APP_USBD_MSC_REQ_BUSY_FULL_MASK) + { + pos ^= p_msc->specific.inst.block_buff_size; + } + + NRF_BLOCK_DEV_REQUEST(req, + p_msc_ctx->current.blk_idx, + p_msc_ctx->current.blk_count, + ((uint8_t *)p_msc->specific.inst.p_block_buff + pos)); + + NRF_LOG_DEBUG("nrf_blk_dev_write_req 2: id: %u, count: %u, left: %u, ptr: %p", + req.blk_id, + req.blk_count, + p_msc_ctx->current.blk_datasize, + (uint32_t)req.p_buff); + + p_msc_ctx->current.block_req_in_progress = true; + ret_code_t ret = nrf_blk_dev_write_req(p_blk_dev, &req); + NRF_LOG_DEBUG("nrf_blk_dev_write_req 2: ret: %u", ret); + + if (ret != NRF_SUCCESS) + { + UNUSED_RETURN_VALUE(status_unsupported_start(p_inst)); + } + + return; + } + + if (!p_msc_ctx->current.trans_in_progress && + (p_msc_ctx->current.blk_datasize > p_msc_ctx->current.blk_count * blk_size)) + { + size_t size = p_msc_ctx->current.blk_datasize - p_msc_ctx->current.blk_count * blk_size; + if (size > p_msc_ctx->current.blk_count * blk_size) + { + size = p_msc_ctx->current.blk_count * blk_size; + } + + if (size > 0) + { + /*Trigger new transfer.*/ + p_msc_ctx->current.blk_count = next_transfer_blkcnt_calc(p_msc, p_msc_ctx); + ret_code_t ret = transfer_out_start(p_inst, + ((uint8_t *)p_msc->specific.inst.p_block_buff + + p_msc_ctx->current.workbuff_pos), + size, + APP_USBD_MSC_STATE_DATA_OUT); + if (ret == NRF_SUCCESS) + { + p_msc_ctx->current.trans_req_id ^= 1; + APP_USBD_MSC_REQ_BUSY_SET(p_msc_ctx->current.req_busy_mask, + p_msc_ctx->current.trans_req_id); + p_msc_ctx->current.workbuff_pos ^= p_msc->specific.inst.block_buff_size; + p_msc_ctx->current.trans_in_progress = true; + } + else + { + UNUSED_RETURN_VALUE(status_unsupported_start(p_inst)); + } + } + } +} + + +/** + * @brief Block device event handler. + * + * Mass storage block device event handler. Need to be pined to all block devices + * from initializer list. + * + * @param p_blk_dev Block device handle + * @param p_event Block device event + * + * */ +static void msc_blockdev_ev_handler(nrf_block_dev_t const * p_blk_dev, + nrf_block_dev_event_t const * p_event) +{ + switch (p_event->ev_type) + { + case NRF_BLOCK_DEV_EVT_INIT: + break; + + case NRF_BLOCK_DEV_EVT_UNINIT: + break; + + case NRF_BLOCK_DEV_EVT_BLK_READ_DONE: + CRITICAL_REGION_ENTER(); + msc_blockdev_read_done_handler(p_blk_dev, p_event); + CRITICAL_REGION_EXIT(); + break; + + case NRF_BLOCK_DEV_EVT_BLK_WRITE_DONE: + CRITICAL_REGION_ENTER(); + msc_blockdev_write_done_handler(p_blk_dev, p_event); + CRITICAL_REGION_EXIT(); + break; + + default: + break; + } +} + +/** @} */ + +bool app_usbd_msc_sync(app_usbd_msc_t const * p_msc) +{ + bool rc = true; + ret_code_t ret = NRF_SUCCESS; + + for (size_t i = 0; i < p_msc->specific.inst.block_devs_count; ++i) + { + nrf_block_dev_t const * p_blkd = p_msc->specific.inst.pp_block_devs[i]; + bool flush_in_progress = true; + + ret = nrf_blk_dev_ioctl(p_blkd, + NRF_BLOCK_DEV_IOCTL_REQ_CACHE_FLUSH, + &flush_in_progress); + + if ((ret != NRF_SUCCESS) || flush_in_progress) + { + rc = false; + } + } + + return rc; +} + + +#endif //NRF_MODULE_ENABLED(APP_USBD_MSC) |