添加GD32H7的CMake工程
This commit is contained in:
@@ -0,0 +1,351 @@
|
||||
/*!
|
||||
\file usbh_msc_bbb.c
|
||||
\brief USB MSC BBB protocol related functions
|
||||
|
||||
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (c) 2025, GigaDevice Semiconductor Inc.
|
||||
|
||||
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 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 the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "usbh_pipe.h"
|
||||
#include "usbh_transc.h"
|
||||
#include "usbh_msc_core.h"
|
||||
#include "usbh_msc_bbb.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/*!
|
||||
\brief initialize the mass storage parameters
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void usbh_msc_bbb_init(usbh_host *uhost)
|
||||
{
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
msc->bbb.cbw.field.dCBWSignature = BBB_CBW_SIGNATURE;
|
||||
msc->bbb.cbw.field.dCBWTag = USBH_MSC_BBB_CBW_TAG;
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_SEND;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief manage the different states of BBB transfer and updates the status to upper layer
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[in] lun: logic unit number
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_bbb_process(usbh_host *uhost, uint8_t lun)
|
||||
{
|
||||
bbb_csw_status csw_status = BBB_CSW_CMD_FAILED;
|
||||
usbh_status status = USBH_BUSY;
|
||||
usbh_status error = USBH_BUSY;
|
||||
usb_urb_state urb_status = URB_IDLE;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->bbb.state) {
|
||||
case BBB_SEND_CBW:
|
||||
msc->bbb.cbw.field.bCBWLUN = lun;
|
||||
msc->bbb.state = BBB_SEND_CBW_WAIT;
|
||||
/* send CBW */
|
||||
usbh_data_send(uhost->data, \
|
||||
msc->bbb.cbw.CBWArray, \
|
||||
msc->pipe_out, \
|
||||
BBB_CBW_LENGTH);
|
||||
break;
|
||||
|
||||
case BBB_SEND_CBW_WAIT:
|
||||
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_out);
|
||||
|
||||
if(URB_DONE == urb_status) {
|
||||
if(0U != msc->bbb.cbw.field.dCBWDataTransferLength) {
|
||||
if(USB_TRX_IN == (msc->bbb.cbw.field.bmCBWFlags & USB_TRX_MASK)) {
|
||||
msc->bbb.state = BBB_DATA_IN;
|
||||
} else {
|
||||
msc->bbb.state = BBB_DATA_OUT;
|
||||
}
|
||||
} else {
|
||||
msc->bbb.state = BBB_RECEIVE_CSW;
|
||||
}
|
||||
|
||||
} else if(URB_NOTREADY == urb_status) {
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
} else {
|
||||
if(URB_STALL == urb_status) {
|
||||
msc->bbb.state = BBB_ERROR_OUT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BBB_DATA_IN:
|
||||
usbh_data_recev(uhost->data, \
|
||||
msc->bbb.pbuf, \
|
||||
msc->pipe_in, \
|
||||
msc->ep_size_in);
|
||||
|
||||
msc->bbb.state = BBB_DATA_IN_WAIT;
|
||||
break;
|
||||
|
||||
case BBB_DATA_IN_WAIT:
|
||||
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_in);
|
||||
|
||||
/* BBB DATA IN stage */
|
||||
if(URB_DONE == urb_status) {
|
||||
if(msc->bbb.cbw.field.dCBWDataTransferLength > msc->ep_size_in) {
|
||||
msc->bbb.pbuf += msc->ep_size_in;
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength -= msc->ep_size_in;
|
||||
} else {
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = 0U;
|
||||
}
|
||||
|
||||
if(msc->bbb.cbw.field.dCBWDataTransferLength > 0U) {
|
||||
usbh_data_recev(uhost->data, \
|
||||
msc->bbb.pbuf, \
|
||||
msc->pipe_in, \
|
||||
msc->ep_size_in);
|
||||
} else {
|
||||
msc->bbb.state = BBB_RECEIVE_CSW;
|
||||
}
|
||||
} else if(URB_STALL == urb_status) {
|
||||
/* this is data stage STALL condition */
|
||||
msc->bbb.state = BBB_ERROR_IN;
|
||||
} else {
|
||||
/* no operation */
|
||||
}
|
||||
break;
|
||||
|
||||
case BBB_DATA_OUT:
|
||||
usbh_data_send(uhost->data, \
|
||||
msc->bbb.pbuf, \
|
||||
msc->pipe_out, \
|
||||
msc->ep_size_out);
|
||||
|
||||
msc->bbb.state = BBB_DATA_OUT_WAIT;
|
||||
break;
|
||||
|
||||
case BBB_DATA_OUT_WAIT:
|
||||
/* BBB DATA OUT stage */
|
||||
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_out);
|
||||
if(URB_DONE == urb_status) {
|
||||
if(msc->bbb.cbw.field.dCBWDataTransferLength > msc->ep_size_out) {
|
||||
msc->bbb.pbuf += msc->ep_size_out;
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength -= msc->ep_size_out;
|
||||
} else {
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = 0U; /* reset this value and keep in same state */
|
||||
}
|
||||
|
||||
if(msc->bbb.cbw.field.dCBWDataTransferLength > 0U) {
|
||||
usbh_data_send(uhost->data, \
|
||||
msc->bbb.pbuf, \
|
||||
msc->pipe_out, \
|
||||
msc->ep_size_out);
|
||||
} else {
|
||||
msc->bbb.state = BBB_RECEIVE_CSW;
|
||||
}
|
||||
} else if(URB_NOTREADY == urb_status) {
|
||||
msc->bbb.state = BBB_DATA_OUT;
|
||||
} else if(URB_STALL == urb_status) {
|
||||
msc->bbb.state = BBB_ERROR_OUT;
|
||||
} else {
|
||||
/* no operation */
|
||||
}
|
||||
break;
|
||||
|
||||
case BBB_RECEIVE_CSW:
|
||||
/* BBB CSW stage */
|
||||
usbh_data_recev(uhost->data, \
|
||||
msc->bbb.csw.CSWArray, \
|
||||
msc->pipe_in, \
|
||||
BBB_CSW_LENGTH);
|
||||
|
||||
msc->bbb.state = BBB_RECEIVE_CSW_WAIT;
|
||||
break;
|
||||
|
||||
case BBB_RECEIVE_CSW_WAIT:
|
||||
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_in);
|
||||
|
||||
/* decode CSW */
|
||||
if(URB_DONE == urb_status) {
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_SEND;
|
||||
|
||||
csw_status = usbh_msc_csw_decode(uhost);
|
||||
if(BBB_CSW_CMD_PASSED == csw_status) {
|
||||
status = USBH_OK;
|
||||
} else {
|
||||
status = USBH_FAIL;
|
||||
}
|
||||
} else if(URB_STALL == urb_status) {
|
||||
msc->bbb.state = BBB_ERROR_IN;
|
||||
} else {
|
||||
/* no operation */
|
||||
}
|
||||
break;
|
||||
|
||||
case BBB_ERROR_IN:
|
||||
error = usbh_msc_bbb_abort(uhost, USBH_MSC_DIR_IN);
|
||||
|
||||
if(USBH_OK == error) {
|
||||
msc->bbb.state = BBB_RECEIVE_CSW;
|
||||
} else if(USBH_UNRECOVERED_ERROR == status) {
|
||||
/* this means that there is a STALL error limit, do reset recovery */
|
||||
msc->bbb.state = BBB_UNRECOVERED_ERROR;
|
||||
} else {
|
||||
/* no operation */
|
||||
}
|
||||
break;
|
||||
|
||||
case BBB_ERROR_OUT:
|
||||
status = usbh_msc_bbb_abort(uhost, USBH_MSC_DIR_OUT);
|
||||
|
||||
if(USBH_OK == status) {
|
||||
uint8_t toggle = usbh_pipe_toggle_get(uhost->data, msc->pipe_out);
|
||||
usbh_pipe_toggle_set(uhost->data, msc->pipe_out, 1U - toggle);
|
||||
usbh_pipe_toggle_set(uhost->data, msc->pipe_in, 0U);
|
||||
msc->bbb.state = BBB_ERROR_IN;
|
||||
} else {
|
||||
if(USBH_UNRECOVERED_ERROR == status) {
|
||||
msc->bbb.state = BBB_UNRECOVERED_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BBB_UNRECOVERED_ERROR:
|
||||
status = usbh_msc_bbb_reset(uhost);
|
||||
if(USBH_OK == status) {
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief manages the different error handling for stall
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] direction: data IN or OUT
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_bbb_abort(usbh_host *uhost, uint8_t direction)
|
||||
{
|
||||
usbh_status status = USBH_BUSY;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(direction) {
|
||||
case USBH_MSC_DIR_IN :
|
||||
/* send clrfeture command on bulk IN endpoint */
|
||||
status = usbh_clrfeature(uhost, msc->ep_in, msc->pipe_in);
|
||||
break;
|
||||
|
||||
case USBH_MSC_DIR_OUT :
|
||||
/*send clrfeature command on bulk OUT endpoint */
|
||||
status = usbh_clrfeature(uhost, msc->ep_out, msc->pipe_out);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief reset MSC BBB transfer
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_bbb_reset(usbh_host *uhost)
|
||||
{
|
||||
usbh_status status = USBH_BUSY;
|
||||
|
||||
if(CTL_IDLE == uhost->control.ctl_state) {
|
||||
uhost->control.setup.req = (usb_req) {
|
||||
.bmRequestType = USB_TRX_OUT | USB_REQTYPE_CLASS | USB_RECPTYPE_ITF,
|
||||
.bRequest = BBB_RESET,
|
||||
.wValue = 0U,
|
||||
.wIndex = 0U,
|
||||
.wLength = 0U
|
||||
};
|
||||
|
||||
usbh_ctlstate_config(uhost, NULL, 0U);
|
||||
}
|
||||
|
||||
status = usbh_ctl_handler(uhost);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief decode the CSW received by the device and updates the same to upper layer
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[out] none
|
||||
\retval on success USBH_MSC_OK, on failure USBH_MSC_FAIL
|
||||
*/
|
||||
bbb_csw_status usbh_msc_csw_decode(usbh_host *uhost)
|
||||
{
|
||||
bbb_csw_status status = BBB_CSW_CMD_FAILED;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
/* checking if the transfer length is different than 13 */
|
||||
if(BBB_CSW_LENGTH != usbh_xfercount_get(uhost->data, msc->pipe_in)) {
|
||||
status = BBB_CSW_PHASE_ERROR;
|
||||
} else {
|
||||
/* CSW length is correct */
|
||||
|
||||
/* check validity of the CSW Signature and CSWStatus */
|
||||
if(BBB_CSW_SIGNATURE == msc->bbb.csw.field.dCSWSignature) {
|
||||
/* check condition 1. dCSWSignature is equal to 53425355h */
|
||||
if(msc->bbb.csw.field.dCSWTag == msc->bbb.cbw.field.dCBWTag) {
|
||||
/* check condition 3. dCSWTag matches the dCBWTag from the corresponding CBW */
|
||||
if(0U == msc->bbb.csw.field.bCSWStatus) {
|
||||
status = BBB_CSW_CMD_PASSED;
|
||||
} else if(1U == msc->bbb.csw.field.bCSWStatus) {
|
||||
status = BBB_CSW_CMD_FAILED;
|
||||
} else if(2U == msc->bbb.csw.field.bCSWStatus) {
|
||||
status = BBB_CSW_PHASE_ERROR;
|
||||
} else {
|
||||
/* no operation */
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* if the CSW signature is not valid, we shall return the phase error to
|
||||
upper layers for reset recovery */
|
||||
status = BBB_CSW_PHASE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,552 @@
|
||||
/*!
|
||||
\file usbh_core.c
|
||||
\brief USB MSC(mass storage device) class driver
|
||||
|
||||
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (c) 2025, GigaDevice Semiconductor Inc.
|
||||
|
||||
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 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 the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "usbh_pipe.h"
|
||||
#include "usbh_transc.h"
|
||||
#include "usbh_msc_core.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* local function prototypes ('static') */
|
||||
static void usbh_msc_itf_deinit(usbh_host *uhost);
|
||||
static usbh_status usbh_msc_itf_init(usbh_host *uhost);
|
||||
static usbh_status usbh_msc_req(usbh_host *uhost);
|
||||
static usbh_status usbh_msc_handle(usbh_host *uhost);
|
||||
static usbh_status usbh_msc_maxlun_get(usbh_host *uhost, uint8_t *maxlun);
|
||||
static usbh_status usbh_msc_rdwr_process(usbh_host *uhost, uint8_t lun);
|
||||
|
||||
usbh_class usbh_msc = {
|
||||
USB_CLASS_MSC,
|
||||
usbh_msc_itf_init,
|
||||
usbh_msc_itf_deinit,
|
||||
usbh_msc_req,
|
||||
usbh_msc_handle
|
||||
};
|
||||
|
||||
/*!
|
||||
\brief get MSC logic unit information
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] info: pointer to logic unit information
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_lun_info_get(usbh_host *uhost, uint8_t lun, msc_lun *info)
|
||||
{
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
if(HOST_CLASS_HANDLER == uhost->cur_state) {
|
||||
memcpy(info, &msc->unit[lun], sizeof(msc_lun));
|
||||
|
||||
return USBH_OK;
|
||||
} else {
|
||||
return USBH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief MSC read interface
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] address: address to be read
|
||||
\param[in] pbuf: pointer to user buffer
|
||||
\param[in] length: length to be read
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_read(usbh_host *uhost, \
|
||||
uint8_t lun, \
|
||||
uint32_t address, \
|
||||
uint8_t *pbuf, \
|
||||
uint32_t length)
|
||||
{
|
||||
uint32_t timeout = 0U;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
usb_core_driver *udev = (usb_core_driver *)uhost->data;
|
||||
|
||||
if((0U == udev->host.connect_status) || \
|
||||
(HOST_CLASS_HANDLER != uhost->cur_state) || \
|
||||
(MSC_IDLE != msc->unit[lun].state)) {
|
||||
return USBH_FAIL;
|
||||
}
|
||||
|
||||
msc->state = MSC_READ;
|
||||
msc->unit[lun].state = MSC_READ;
|
||||
msc->rw_lun = lun;
|
||||
|
||||
usbh_msc_read10(uhost, lun, pbuf, address, length);
|
||||
|
||||
timeout = uhost->control.timer;
|
||||
|
||||
while(USBH_BUSY == usbh_msc_rdwr_process(uhost, lun)) {
|
||||
if(((uhost->control.timer - timeout) > (10000U * length)) || (0U == udev->host.connect_status)) {
|
||||
msc->state = MSC_IDLE;
|
||||
return USBH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
msc->state = MSC_IDLE;
|
||||
|
||||
return USBH_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief MSC write interface
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] address: address to be written
|
||||
\param[in] pbuf: pointer to user buffer
|
||||
\param[in] length: length to be written
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_write(usbh_host *uhost, \
|
||||
uint8_t lun, \
|
||||
uint32_t address, \
|
||||
uint8_t *pbuf, \
|
||||
uint32_t length)
|
||||
{
|
||||
uint32_t timeout = 0U;
|
||||
usb_core_driver *udev = (usb_core_driver *)uhost->data;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
if((0U == udev->host.connect_status) || \
|
||||
(HOST_CLASS_HANDLER != uhost->cur_state) || \
|
||||
(MSC_IDLE != msc->unit[lun].state)) {
|
||||
return USBH_FAIL;
|
||||
}
|
||||
|
||||
msc->state = MSC_WRITE;
|
||||
msc->unit[lun].state = MSC_WRITE;
|
||||
msc->rw_lun = lun;
|
||||
|
||||
usbh_msc_write10(uhost, lun, pbuf, address, length);
|
||||
|
||||
timeout = uhost->control.timer;
|
||||
|
||||
while(USBH_BUSY == usbh_msc_rdwr_process(uhost, lun)) {
|
||||
if(((uhost->control.timer - timeout) > (10000U * length)) || (0U == udev->host.connect_status)) {
|
||||
msc->state = MSC_IDLE;
|
||||
return USBH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
msc->state = MSC_IDLE;
|
||||
|
||||
return USBH_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief de-initialize interface by freeing host channels allocated to interface
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
static void usbh_msc_itf_deinit(usbh_host *uhost)
|
||||
{
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
if(msc->pipe_out) {
|
||||
usb_pipe_halt(uhost->data, msc->pipe_out);
|
||||
usbh_pipe_free(uhost->data, msc->pipe_out);
|
||||
|
||||
msc->pipe_out = 0U;
|
||||
}
|
||||
|
||||
if(msc->pipe_in) {
|
||||
usb_pipe_halt(uhost->data, msc->pipe_in);
|
||||
usbh_pipe_free(uhost->data, msc->pipe_in);
|
||||
|
||||
msc->pipe_in = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief interface initialization for MSC class
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
static usbh_status usbh_msc_itf_init(usbh_host *uhost)
|
||||
{
|
||||
usbh_status status = USBH_OK;
|
||||
|
||||
uint8_t interface = usbh_interface_find(&uhost->dev_prop, MSC_CLASS, USB_MSC_SUBCLASS_SCSI, MSC_PROTOCOL);
|
||||
|
||||
if(0xFFU == interface) {
|
||||
uhost->usr_cb->dev_not_supported();
|
||||
|
||||
status = USBH_FAIL;
|
||||
} else {
|
||||
static usbh_msc_handler msc_handler;
|
||||
|
||||
memset((void *)&msc_handler, 0U, sizeof(usbh_msc_handler));
|
||||
|
||||
uhost->active_class->class_data = (void *)&msc_handler;
|
||||
|
||||
usbh_interface_select(&uhost->dev_prop, interface);
|
||||
|
||||
usb_desc_ep *ep_desc = &uhost->dev_prop.cfg_desc_set.itf_desc_set[interface][0].ep_desc[0];
|
||||
|
||||
if(ep_desc->bEndpointAddress & 0x80U) {
|
||||
msc_handler.ep_in = ep_desc->bEndpointAddress;
|
||||
msc_handler.ep_size_in = ep_desc->wMaxPacketSize;
|
||||
} else {
|
||||
msc_handler.ep_out = ep_desc->bEndpointAddress;
|
||||
msc_handler.ep_size_out = ep_desc->wMaxPacketSize;
|
||||
}
|
||||
|
||||
ep_desc = &uhost->dev_prop.cfg_desc_set.itf_desc_set[interface][0].ep_desc[1];
|
||||
|
||||
if(ep_desc->bEndpointAddress & 0x80U) {
|
||||
msc_handler.ep_in = ep_desc->bEndpointAddress;
|
||||
msc_handler.ep_size_in = ep_desc->wMaxPacketSize;
|
||||
} else {
|
||||
msc_handler.ep_out = ep_desc->bEndpointAddress;
|
||||
msc_handler.ep_size_out = ep_desc->wMaxPacketSize;
|
||||
}
|
||||
|
||||
msc_handler.state = MSC_INIT;
|
||||
msc_handler.error = MSC_OK;
|
||||
msc_handler.req_state = MSC_REQ_IDLE;
|
||||
msc_handler.pipe_out = usbh_pipe_allocate(uhost->data, msc_handler.ep_out);
|
||||
msc_handler.pipe_in = usbh_pipe_allocate(uhost->data, msc_handler.ep_in);
|
||||
|
||||
usbh_msc_bbb_init(uhost);
|
||||
|
||||
/* open the new channels */
|
||||
usbh_pipe_create(uhost->data, \
|
||||
&uhost->dev_prop, \
|
||||
msc_handler.pipe_out, \
|
||||
USB_EPTYPE_BULK, \
|
||||
msc_handler.ep_size_out);
|
||||
|
||||
usbh_pipe_create(uhost->data, \
|
||||
&uhost->dev_prop, \
|
||||
msc_handler.pipe_in, \
|
||||
USB_EPTYPE_BULK, \
|
||||
msc_handler.ep_size_in);
|
||||
|
||||
usbh_pipe_toggle_set(uhost->data, msc_handler.pipe_out, 0U);
|
||||
usbh_pipe_toggle_set(uhost->data, msc_handler.pipe_in, 0U);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief initialize the MSC state machine
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
static usbh_status usbh_msc_req(usbh_host *uhost)
|
||||
{
|
||||
usbh_status status = USBH_BUSY;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->req_state) {
|
||||
case MSC_REQ_IDLE:
|
||||
case MSC_REQ_GET_MAX_LUN:
|
||||
/* issue Get_MaxLun request */
|
||||
status = usbh_msc_maxlun_get(uhost, (uint8_t *)&msc->max_lun);
|
||||
|
||||
if(USBH_OK == status) {
|
||||
msc->max_lun = ((uint8_t)msc->max_lun > MSC_MAX_SUPPORTED_LUN) ? MSC_MAX_SUPPORTED_LUN : (uint8_t)msc->max_lun + 1U;
|
||||
|
||||
for(uint8_t i = 0U; i < msc->max_lun; i++) {
|
||||
msc->unit[i].prev_ready_state = USBH_FAIL;
|
||||
msc->unit[i].state_changed = 0U;
|
||||
}
|
||||
} else {
|
||||
if(USBH_NOT_SUPPORTED == status) {
|
||||
msc->max_lun = 0U;
|
||||
status = USBH_OK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_REQ_ERROR:
|
||||
/* issue clear feature request */
|
||||
if(USBH_OK == usbh_clrfeature(uhost, 0x00U, uhost->control.pipe_out_num)) {
|
||||
msc->req_state = msc->prev_req_state;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief MSC state machine handler
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
static usbh_status usbh_msc_handle(usbh_host *uhost)
|
||||
{
|
||||
usbh_status status = USBH_BUSY;
|
||||
uint8_t scsi_status = USBH_BUSY;
|
||||
uint8_t ready_status = USBH_BUSY;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->state) {
|
||||
case MSC_INIT:
|
||||
if(msc->cur_lun < msc->max_lun) {
|
||||
msc->unit[msc->cur_lun].error = MSC_NOT_READY;
|
||||
|
||||
switch(msc->unit[msc->cur_lun].state) {
|
||||
case MSC_INIT:
|
||||
msc->unit[msc->cur_lun].state = MSC_READ_INQUIRY;
|
||||
msc->timer = uhost->control.timer;
|
||||
break;
|
||||
|
||||
case MSC_READ_INQUIRY:
|
||||
scsi_status = usbh_msc_scsi_inquiry(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].inquiry);
|
||||
|
||||
if(USBH_OK == scsi_status) {
|
||||
msc->unit[msc->cur_lun].state = MSC_TEST_UNIT_READY;
|
||||
} else if(USBH_FAIL == scsi_status) {
|
||||
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
|
||||
} else {
|
||||
if(USBH_UNRECOVERED_ERROR == scsi_status) {
|
||||
msc->unit[msc->cur_lun].state = MSC_IDLE;
|
||||
msc->unit[msc->cur_lun].error = MSC_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_TEST_UNIT_READY:
|
||||
/* issue SCSI command TestUnitReady */
|
||||
ready_status = usbh_msc_test_unitready(uhost, msc->cur_lun);
|
||||
|
||||
if(USBH_OK == ready_status) {
|
||||
if(USBH_OK != msc->unit[msc->cur_lun].prev_ready_state) {
|
||||
msc->unit[msc->cur_lun].state_changed = 1U;
|
||||
} else {
|
||||
msc->unit[msc->cur_lun].state_changed = 0U;
|
||||
}
|
||||
|
||||
msc->unit[msc->cur_lun].state = MSC_READ_CAPACITY10;
|
||||
msc->unit[msc->cur_lun].error = MSC_OK;
|
||||
msc->unit[msc->cur_lun].prev_ready_state = USBH_OK;
|
||||
} else if(USBH_FAIL == ready_status) {
|
||||
if(USBH_FAIL != msc->unit[msc->cur_lun].prev_ready_state) {
|
||||
msc->unit[msc->cur_lun].state_changed = 1U;
|
||||
} else {
|
||||
msc->unit[msc->cur_lun].state_changed = 0U;
|
||||
}
|
||||
|
||||
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
|
||||
msc->unit[msc->cur_lun].error = MSC_NOT_READY;
|
||||
msc->unit[msc->cur_lun].prev_ready_state = USBH_FAIL;
|
||||
} else {
|
||||
if(USBH_UNRECOVERED_ERROR == ready_status) {
|
||||
msc->unit[msc->cur_lun].state = MSC_IDLE;
|
||||
msc->unit[msc->cur_lun].error = MSC_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_READ_CAPACITY10:
|
||||
/* issue READ_CAPACITY10 SCSI command */
|
||||
scsi_status = usbh_msc_read_capacity10(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].capacity);
|
||||
|
||||
if(USBH_OK == scsi_status) {
|
||||
if(1U == msc->unit[msc->cur_lun].state_changed) {
|
||||
}
|
||||
msc->unit[msc->cur_lun].state = MSC_IDLE;
|
||||
msc->unit[msc->cur_lun].error = MSC_OK;
|
||||
msc->cur_lun ++;
|
||||
} else if(USBH_FAIL == scsi_status) {
|
||||
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
|
||||
} else {
|
||||
if(USBH_UNRECOVERED_ERROR == scsi_status) {
|
||||
msc->unit[msc->cur_lun].state = MSC_IDLE;
|
||||
msc->unit[msc->cur_lun].error = MSC_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_REQUEST_SENSE:
|
||||
/* issue RequestSense SCSI command for receive error code */
|
||||
scsi_status = usbh_msc_request_sense(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].sense);
|
||||
if(USBH_OK == scsi_status) {
|
||||
if((UNIT_ATTENTION == msc->unit[msc->cur_lun].sense.SenseKey) || (NOT_READY == msc->unit[msc->cur_lun].sense.SenseKey)) {
|
||||
if((uhost->control.timer - msc->timer) < 10000U) {
|
||||
msc->unit[msc->cur_lun].state = MSC_TEST_UNIT_READY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
msc->unit[msc->cur_lun].state = MSC_IDLE;
|
||||
msc->cur_lun++;
|
||||
} else if(USBH_FAIL == scsi_status) {
|
||||
msc->unit[msc->cur_lun].state = MSC_UNRECOVERED_ERROR;
|
||||
} else {
|
||||
if(MSC_UNRECOVERED_ERROR == scsi_status) {
|
||||
msc->unit[msc->cur_lun].state = MSC_IDLE;
|
||||
msc->unit[msc->cur_lun].error = MSC_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_UNRECOVERED_ERROR:
|
||||
msc->cur_lun++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
msc->cur_lun = 0U;
|
||||
msc->state = MSC_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_IDLE:
|
||||
uhost->usr_cb->dev_user_app();
|
||||
status = USBH_OK;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief get max lun of the mass storage device
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[in] maxlun: pointer to max lun
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
static usbh_status usbh_msc_maxlun_get(usbh_host *uhost, uint8_t *maxlun)
|
||||
{
|
||||
usbh_status status = USBH_BUSY;
|
||||
|
||||
if(CTL_IDLE == uhost->control.ctl_state) {
|
||||
uhost->control.setup.req = (usb_req) {
|
||||
.bmRequestType = USB_TRX_IN | USB_REQTYPE_CLASS | USB_RECPTYPE_ITF,
|
||||
.bRequest = BBB_GET_MAX_LUN,
|
||||
.wValue = 0U,
|
||||
.wIndex = 0U,
|
||||
.wLength = 1U
|
||||
};
|
||||
|
||||
usbh_ctlstate_config(uhost, maxlun, 1U);
|
||||
}
|
||||
|
||||
status = usbh_ctl_handler(uhost);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief get max lun of the mass storage device
|
||||
\param[in] uhost: pointer to USB host
|
||||
\param[in] lun: logic unit number
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
static usbh_status usbh_msc_rdwr_process(usbh_host *uhost, uint8_t lun)
|
||||
{
|
||||
usbh_status error = USBH_BUSY;
|
||||
usbh_status scsi_status = USBH_BUSY;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
/* switch MSC REQ state machine */
|
||||
switch(msc->unit[lun].state) {
|
||||
case MSC_READ:
|
||||
scsi_status = usbh_msc_read10(uhost, lun, NULL, 0U, 0U);
|
||||
|
||||
if(USBH_OK == scsi_status) {
|
||||
msc->unit[lun].state = MSC_IDLE;
|
||||
error = USBH_OK;
|
||||
} else if(USBH_FAIL == scsi_status) {
|
||||
msc->unit[lun].state = MSC_REQUEST_SENSE;
|
||||
} else {
|
||||
if(USBH_UNRECOVERED_ERROR == scsi_status) {
|
||||
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
|
||||
error = USBH_FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_WRITE:
|
||||
scsi_status = usbh_msc_write10(uhost, lun, NULL, 0U, 0U);
|
||||
|
||||
if(USBH_OK == scsi_status) {
|
||||
msc->unit[lun].state = MSC_IDLE;
|
||||
error = USBH_OK;
|
||||
} else if(USBH_FAIL == scsi_status) {
|
||||
msc->unit[lun].state = MSC_REQUEST_SENSE;
|
||||
} else {
|
||||
if(USBH_UNRECOVERED_ERROR == scsi_status) {
|
||||
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
|
||||
error = USBH_FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MSC_REQUEST_SENSE:
|
||||
scsi_status = usbh_msc_request_sense(uhost, lun, &msc->unit[lun].sense);
|
||||
|
||||
if(USBH_OK == scsi_status) {
|
||||
msc->unit[lun].state = MSC_IDLE;
|
||||
msc->unit[lun].error = MSC_ERROR;
|
||||
|
||||
error = USBH_FAIL;
|
||||
}
|
||||
|
||||
if(USBH_FAIL == scsi_status) {
|
||||
} else {
|
||||
if(USBH_UNRECOVERED_ERROR == scsi_status) {
|
||||
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
|
||||
error = USBH_FAIL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
/*!
|
||||
\file usbh_msc_fatfs.c
|
||||
\brief USB MSC host FATFS related functions
|
||||
|
||||
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (c) 2025, GigaDevice Semiconductor Inc.
|
||||
|
||||
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 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 the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "diskio.h"
|
||||
#include "usbh_msc_core.h"
|
||||
|
||||
static volatile DSTATUS state = STA_NOINIT; /* disk status */
|
||||
|
||||
extern usbh_host usb_host_msc;
|
||||
|
||||
/*!
|
||||
\brief initialize the disk drive
|
||||
\param[in] drv: physical drive number (0)
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
DSTATUS disk_initialize(BYTE drv)
|
||||
{
|
||||
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
|
||||
|
||||
if(udev->host.connect_status) {
|
||||
state &= ~STA_NOINIT;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief get disk status
|
||||
\param[in] drv: physical drive number (0)
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
DSTATUS disk_status(BYTE drv)
|
||||
{
|
||||
if(drv) {
|
||||
return STA_NOINIT; /* supports only single drive */
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief read sectors
|
||||
\param[in] drv: physical drive number (0)
|
||||
\param[in] buff: pointer to the data buffer to store read data
|
||||
\param[in] sector: start sector number (LBA)
|
||||
\param[in] count: sector count (1..255)
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
DRESULT disk_read(BYTE drv, BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
BYTE status = USBH_OK;
|
||||
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
|
||||
|
||||
if(drv || (!count)) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
if(state & STA_NOINIT) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
if(udev->host.connect_status) {
|
||||
do {
|
||||
status = usbh_msc_read(&usb_host_msc, drv, sector, buff, count);
|
||||
|
||||
if(!udev->host.connect_status) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
} while(USBH_BUSY == status);
|
||||
}
|
||||
|
||||
if(USBH_OK == status) {
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
#if _READONLY == 0U
|
||||
|
||||
/*!
|
||||
\brief write sectors
|
||||
\param[in] drv: physical drive number (0)
|
||||
\param[in] buff: pointer to the data buffer to store read data
|
||||
\param[in] sector: start sector number (LBA)
|
||||
\param[in] count: sector count (1..255)
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
DRESULT disk_write(BYTE drv, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
BYTE status = USBH_OK;
|
||||
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
|
||||
|
||||
if((!count) || drv) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
if(state & STA_NOINIT) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
if(state & STA_PROTECT) {
|
||||
return RES_WRPRT;
|
||||
}
|
||||
|
||||
if(udev->host.connect_status) {
|
||||
do {
|
||||
status = usbh_msc_write(&usb_host_msc, drv, sector, (BYTE *)buff, count);
|
||||
|
||||
if(!udev->host.connect_status) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
} while(USBH_BUSY == status);
|
||||
}
|
||||
|
||||
if(USBH_OK == status) {
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
#endif /* _READONLY == 0 */
|
||||
|
||||
/*!
|
||||
\brief I/O control function
|
||||
\param[in] drv: physical drive number (0)
|
||||
\param[in] ctrl: control code
|
||||
\param[in] buff: pointer to the data buffer to store read data
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void *buff)
|
||||
{
|
||||
DRESULT res = RES_OK;
|
||||
msc_lun info;
|
||||
|
||||
if(drv) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
res = RES_ERROR;
|
||||
|
||||
if(state & STA_NOINIT) {
|
||||
return RES_NOTRDY;
|
||||
}
|
||||
|
||||
switch(ctrl) {
|
||||
/* make sure that no pending write process */
|
||||
case CTRL_SYNC:
|
||||
res = RES_OK;
|
||||
break;
|
||||
|
||||
/* get number of sectors on the disk (dword) */
|
||||
case GET_SECTOR_COUNT:
|
||||
if(USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, drv, &info)) {
|
||||
*(DWORD *)buff = (DWORD)info.capacity.block_nbr;
|
||||
res = RES_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
/* get r/w sector size (word) */
|
||||
case GET_SECTOR_SIZE:
|
||||
if(USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, drv, &info)) {
|
||||
*(WORD *)buff = (DWORD)info.capacity.block_size;
|
||||
res = RES_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
/* get erase block size in unit of sector (dword) */
|
||||
case GET_BLOCK_SIZE:
|
||||
*(DWORD *)buff = 512U;
|
||||
break;
|
||||
|
||||
default:
|
||||
res = RES_PARERR;
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief get fat time
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval time value
|
||||
*/
|
||||
DWORD get_fattime(void)
|
||||
{
|
||||
return ((DWORD)(2019U - 1980U) << 25) /* year 2019 */
|
||||
| ((DWORD)1U << 21) /* month 1 */
|
||||
| ((DWORD)1U << 16) /* day 1 */
|
||||
| ((DWORD)0U << 11) /* hour 0 */
|
||||
| ((DWORD)0U << 5) /* min 0 */
|
||||
| ((DWORD)0U >> 1); /* sec 0 */
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
/*!
|
||||
\file usbh_msc_scsi.c
|
||||
\brief USB MSC SCSI commands implemention
|
||||
|
||||
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (c) 2025, GigaDevice Semiconductor Inc.
|
||||
|
||||
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 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 the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "usbh_msc_core.h"
|
||||
#include "usbh_msc_scsi.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*!
|
||||
\brief send 'Inquiry' command to the device
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] inquiry: pointer to the inquiry structure
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_scsi_inquiry(usbh_host *uhost, uint8_t lun, scsi_std_inquiry_data *inquiry)
|
||||
{
|
||||
usbh_status error = USBH_FAIL;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->bbb.cmd_state) {
|
||||
case BBB_CMD_SEND:
|
||||
/* prepare the CBW and relevant field*/
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = STANDARD_INQUIRY_DATA_LEN;
|
||||
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
|
||||
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
|
||||
|
||||
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_LENGTH);
|
||||
|
||||
msc->bbb.cbw.field.CBWCB[0] = SCSI_INQUIRY;
|
||||
msc->bbb.cbw.field.CBWCB[1] = (lun << 5);
|
||||
msc->bbb.cbw.field.CBWCB[4] = 0x24U;
|
||||
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
|
||||
error = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
error = usbh_msc_bbb_process(uhost, lun);
|
||||
|
||||
if(USBH_OK == error) {
|
||||
memset(inquiry, 0U, sizeof(scsi_std_inquiry_data));
|
||||
|
||||
/* assign inquiry data */
|
||||
inquiry->device_type = msc->bbb.pbuf[0] & 0x1FU;
|
||||
inquiry->peripheral_qualifier = msc->bbb.pbuf[0] >> 5;
|
||||
|
||||
if(0x80U == ((uint32_t)msc->bbb.pbuf[1] & 0x80U)) {
|
||||
inquiry->removable_media = 1U;
|
||||
} else {
|
||||
inquiry->removable_media = 0U;
|
||||
}
|
||||
|
||||
memcpy(inquiry->vendor_id, &msc->bbb.pbuf[8], 8U);
|
||||
memcpy(inquiry->product_id, &msc->bbb.pbuf[16], 16U);
|
||||
memcpy(inquiry->revision_id, &msc->bbb.pbuf[32], 4U);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send 'Test unit ready' command to the device
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] lun: logic unit number
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_test_unitready(usbh_host *uhost, uint8_t lun)
|
||||
{
|
||||
usbh_status status = USBH_FAIL;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->bbb.cmd_state) {
|
||||
case BBB_CMD_SEND:
|
||||
/* prepare the CBW and relevant field */
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = CBW_LENGTH_TEST_UNIT_READY;
|
||||
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_OUT;
|
||||
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
|
||||
|
||||
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
|
||||
|
||||
msc->bbb.cbw.field.CBWCB[0] = SCSI_TEST_UNIT_READY;
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
|
||||
status = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
status = usbh_msc_bbb_process(uhost, lun);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send the read capacity command to the device
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] capacity: pointer to SCSI capacity
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_read_capacity10(usbh_host *uhost, uint8_t lun, scsi_capacity *capacity)
|
||||
{
|
||||
usbh_status status = USBH_FAIL;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->bbb.cmd_state) {
|
||||
case BBB_CMD_SEND:
|
||||
/* prepare the CBW and relevant field */
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = READ_CAPACITY10_DATA_LEN;
|
||||
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
|
||||
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
|
||||
|
||||
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
|
||||
|
||||
msc->bbb.cbw.field.CBWCB[0] = SCSI_READ_CAPACITY10;
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
|
||||
|
||||
status = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
status = usbh_msc_bbb_process(uhost, lun);
|
||||
|
||||
if(USBH_OK == status) {
|
||||
capacity->block_nbr = msc->bbb.pbuf[3] | \
|
||||
((uint32_t)msc->bbb.pbuf[2] << 8) | \
|
||||
((uint32_t)msc->bbb.pbuf[1] << 16) | \
|
||||
((uint32_t)msc->bbb.pbuf[0] << 24);
|
||||
|
||||
capacity->block_size = (uint16_t)(msc->bbb.pbuf[7] | ((uint32_t)msc->bbb.pbuf[6] << 8));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send the mode sense6 command to the device
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] lun: logic unit number
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_mode_sense6(usbh_host *uhost, uint8_t lun)
|
||||
{
|
||||
usbh_status status = USBH_FAIL;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
|
||||
switch(msc->bbb.cmd_state) {
|
||||
case BBB_CMD_SEND:
|
||||
/* prepare the CBW and relevant field */
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = XFER_LEN_MODE_SENSE6;
|
||||
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
|
||||
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
|
||||
|
||||
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
|
||||
|
||||
msc->bbb.cbw.field.CBWCB[0] = SCSI_MODE_SENSE6;
|
||||
msc->bbb.cbw.field.CBWCB[2] = MODE_SENSE_PAGE_CONTROL_FIELD | MODE_SENSE_PAGE_CODE;
|
||||
msc->bbb.cbw.field.CBWCB[4] = XFER_LEN_MODE_SENSE6;
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
|
||||
|
||||
status = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
status = usbh_msc_bbb_process(uhost, lun);
|
||||
|
||||
if(USBH_OK == status) {
|
||||
if(msc->bbb.data[2] & MASK_MODE_SENSE_WRITE_PROTECT) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send the request sense command to the device
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] sense_data: pointer to sense data
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_request_sense(usbh_host *uhost, uint8_t lun, msc_scsi_sense *sense_data)
|
||||
{
|
||||
usbh_status status = USBH_FAIL;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->bbb.cmd_state) {
|
||||
case BBB_CMD_SEND:
|
||||
/* prepare the CBW and relevant field */
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = ALLOCATION_LENGTH_REQUEST_SENSE;
|
||||
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
|
||||
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
|
||||
|
||||
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
|
||||
|
||||
msc->bbb.cbw.field.CBWCB[0] = SCSI_REQUEST_SENSE;
|
||||
msc->bbb.cbw.field.CBWCB[1] = (lun << 5);
|
||||
msc->bbb.cbw.field.CBWCB[4] = ALLOCATION_LENGTH_REQUEST_SENSE;
|
||||
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
|
||||
|
||||
status = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
status = usbh_msc_bbb_process(uhost, lun);
|
||||
|
||||
if(USBH_OK == status) {
|
||||
/* get sense data */
|
||||
sense_data->SenseKey = msc->bbb.pbuf[2] & 0x0FU;
|
||||
sense_data->ASC = msc->bbb.pbuf[12];
|
||||
sense_data->ASCQ = msc->bbb.pbuf[13];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send the write10 command to the device
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] data_buf: data buffer contains the data to write
|
||||
\param[in] addr: address to which the data will be written
|
||||
\param[in] sector_num: number of sector to be written
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_write10(usbh_host *uhost, uint8_t lun, uint8_t *data_buf, uint32_t addr, uint32_t sector_num)
|
||||
{
|
||||
usbh_status status = USBH_FAIL;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->bbb.cmd_state) {
|
||||
case BBB_CMD_SEND:
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = sector_num * msc->unit[lun].capacity.block_size;
|
||||
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_OUT;
|
||||
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
|
||||
|
||||
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
|
||||
|
||||
msc->bbb.cbw.field.CBWCB[0] = SCSI_WRITE10;
|
||||
|
||||
/* logical block address */
|
||||
msc->bbb.cbw.field.CBWCB[2] = (((uint8_t *)&addr)[3]);
|
||||
msc->bbb.cbw.field.CBWCB[3] = (((uint8_t *)&addr)[2]);
|
||||
msc->bbb.cbw.field.CBWCB[4] = (((uint8_t *)&addr)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[5] = (((uint8_t *)&addr)[0]);
|
||||
|
||||
/* transfer length */
|
||||
msc->bbb.cbw.field.CBWCB[7] = (((uint8_t *)§or_num)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)§or_num)[0]);
|
||||
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
msc->bbb.pbuf = data_buf;
|
||||
|
||||
status = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
status = usbh_msc_bbb_process(uhost, lun);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send the read10 command to the device
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] data_buf: data buffer contains the data to write
|
||||
\param[in] addr: address to which the data will be read
|
||||
\param[in] sector_num: number of sector to be read
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_read10(usbh_host *uhost, uint8_t lun, uint8_t *data_buf, uint32_t addr, uint32_t sector_num)
|
||||
{
|
||||
usbh_status status = USBH_FAIL;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->bbb.cmd_state) {
|
||||
case BBB_CMD_SEND:
|
||||
/* prepare the CBW and relevant field */
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = sector_num * msc->unit[lun].capacity.block_size;
|
||||
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
|
||||
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
|
||||
|
||||
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
|
||||
|
||||
msc->bbb.cbw.field.CBWCB[0] = SCSI_READ10;
|
||||
|
||||
/* logical block address */
|
||||
msc->bbb.cbw.field.CBWCB[2] = (((uint8_t *)&addr)[3]);
|
||||
msc->bbb.cbw.field.CBWCB[3] = (((uint8_t *)&addr)[2]);
|
||||
msc->bbb.cbw.field.CBWCB[4] = (((uint8_t *)&addr)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[5] = (((uint8_t *)&addr)[0]);
|
||||
|
||||
/* transfer length */
|
||||
msc->bbb.cbw.field.CBWCB[7] = (((uint8_t *)§or_num)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)§or_num)[0]);
|
||||
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
msc->bbb.pbuf = data_buf;
|
||||
|
||||
status = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
status = usbh_msc_bbb_process(uhost, lun);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
Reference in New Issue
Block a user