添加GD32H7的CMake工程
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
/*!
|
||||
\file usbd_msc_bbb.h
|
||||
\brief the header file of the usbd_msc_bot.c file
|
||||
|
||||
\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.
|
||||
*/
|
||||
|
||||
#ifndef USBD_MSC_BBB_H
|
||||
#define USBD_MSC_BBB_H
|
||||
|
||||
#include "msc_bbb.h"
|
||||
#include "msc_scsi.h"
|
||||
#include "usbd_msc_scsi.h"
|
||||
|
||||
/* MSC BBB state */
|
||||
enum msc_bbb_state {
|
||||
BBB_IDLE = 0U, /*!< idle state */
|
||||
BBB_DATA_OUT, /*!< data OUT state */
|
||||
BBB_DATA_IN, /*!< data IN state */
|
||||
BBB_LAST_DATA_IN, /*!< last data IN state */
|
||||
BBB_SEND_DATA /*!< send immediate data state */
|
||||
};
|
||||
|
||||
/* MSC BBB status */
|
||||
enum msc_bbb_status {
|
||||
BBB_STATUS_NORMAL = 0U, /*!< normal status */
|
||||
BBB_STATUS_RECOVERY, /*!< recovery status*/
|
||||
BBB_STATUS_ERROR /*!< error status */
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
__ALIGN_BEGIN uint8_t bbb_data[MSC_MEDIA_PACKET_SIZE] __ALIGN_END; /*!< MSC BBB data buff */
|
||||
|
||||
uint8_t max_lun; /*!< maximum LUN */
|
||||
|
||||
uint8_t bbb_state; /*!< BBB state */
|
||||
uint8_t bbb_status; /*!< BBB status */
|
||||
|
||||
uint32_t bbb_datalen; /*!< BBB data length */
|
||||
|
||||
__ALIGN_BEGIN msc_bbb_cbw bbb_cbw __ALIGN_END; /*!< MSC BBB CBW structural */
|
||||
__ALIGN_BEGIN msc_bbb_csw bbb_csw __ALIGN_END; /*!< MSC BBB CSW structural */
|
||||
|
||||
uint8_t scsi_sense_head; /*!< SCSI sense header */
|
||||
uint8_t scsi_sense_tail; /*!< SCSI sense tail */
|
||||
|
||||
uint32_t scsi_blk_size[MEM_LUN_NUM]; /*!< SCSI block size */
|
||||
uint32_t scsi_blk_nbr[MEM_LUN_NUM]; /*!< SCSI block number */
|
||||
|
||||
uint32_t scsi_blk_addr; /*!< SCSI block address */
|
||||
uint32_t scsi_blk_len; /*!< SCSI block length */
|
||||
uint32_t scsi_disk_pop; /*!< SCSI disk pop */
|
||||
|
||||
__ALIGN_BEGIN msc_scsi_sense scsi_sense[SENSE_LIST_DEEPTH] __ALIGN_END; /*!< MSC SCSI sense structural buffer */
|
||||
} usbd_msc_handler;
|
||||
|
||||
/* function declarations */
|
||||
/* initialize the BBB process */
|
||||
void msc_bbb_init(usb_core_driver *udev);
|
||||
/* reset the BBB machine */
|
||||
void msc_bbb_reset(usb_core_driver *udev);
|
||||
/* deinitialize the BBB machine */
|
||||
void msc_bbb_deinit(usb_core_driver *udev);
|
||||
/* handle BBB data IN stage */
|
||||
void msc_bbb_data_in(usb_core_driver *udev, uint8_t ep_num);
|
||||
/* handle BBB data OUT stage */
|
||||
void msc_bbb_data_out(usb_core_driver *udev, uint8_t ep_num);
|
||||
/* send the CSW(command status wrapper) */
|
||||
void msc_bbb_csw_send(usb_core_driver *udev, uint8_t csw_status);
|
||||
/* complete the clear feature request */
|
||||
void msc_bbb_clrfeature(usb_core_driver *udev, uint8_t ep_num);
|
||||
|
||||
#endif /* USBD_MSC_BBB_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
/*!
|
||||
\file usbd_msc_core.h
|
||||
\brief the header file of USB MSC device class core 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.
|
||||
*/
|
||||
|
||||
#ifndef USBD_MSC_CORE_H
|
||||
#define USBD_MSC_CORE_H
|
||||
|
||||
#include "usbd_core.h"
|
||||
#include "usb_msc.h"
|
||||
|
||||
#define USB_MSC_CONFIG_DESC_SIZE 32U /*!< MSC configuration descriptor size */
|
||||
|
||||
#define MSC_EPIN_SIZE MSC_DATA_PACKET_SIZE /*!< MSC endpoint IN size */
|
||||
#define MSC_EPOUT_SIZE MSC_DATA_PACKET_SIZE /*!< MSC endpoint OUT size */
|
||||
|
||||
/* USB configuration descriptor structure */
|
||||
typedef struct {
|
||||
usb_desc_config config; /*!< configuration descriptor */
|
||||
|
||||
usb_desc_itf msc_itf; /*!< interface descriptor */
|
||||
usb_desc_ep msc_epin; /*!< endpoint IN descriptor */
|
||||
usb_desc_ep msc_epout; /*!< endpoint OUT descriptor */
|
||||
} usb_desc_config_set;
|
||||
|
||||
extern usb_desc msc_desc;
|
||||
extern usb_class_core msc_class;
|
||||
|
||||
#endif /* USBD_MSC_CORE_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
/*!
|
||||
\file usbd_msc_mem.h
|
||||
\brief header file for storage memory
|
||||
|
||||
\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.
|
||||
*/
|
||||
|
||||
#ifndef USBD_MSC_MEM_H
|
||||
#define USBD_MSC_MEM_H
|
||||
|
||||
#include "usbd_conf.h"
|
||||
|
||||
#define USBD_STD_INQUIRY_LENGTH 36U /*!< standard inquiry length */
|
||||
|
||||
typedef struct {
|
||||
int8_t (*mem_init)(uint8_t lun);
|
||||
int8_t (*mem_ready)(uint8_t lun);
|
||||
int8_t (*mem_protected)(uint8_t lun);
|
||||
int8_t (*mem_read)(uint8_t lun, uint8_t *buf, uint32_t block_addr, uint16_t block_len);
|
||||
int8_t (*mem_write)(uint8_t lun, uint8_t *buf, uint32_t block_addr, uint16_t block_len);
|
||||
int8_t (*mem_maxlun)(void);
|
||||
|
||||
uint8_t *mem_toc_data; /*!< memory TOC command data pointer */
|
||||
uint8_t *mem_inquiry_data[MEM_LUN_NUM]; /*!< memory inquiry data buff */
|
||||
uint32_t mem_block_size[MEM_LUN_NUM]; /*!< memory block size buff */
|
||||
uint32_t mem_block_len[MEM_LUN_NUM]; /*!< memory block length buff */
|
||||
}usbd_mem_cb;
|
||||
|
||||
extern usbd_mem_cb *usbd_mem_fops;
|
||||
|
||||
#endif /* USBD_MSC_MEM_H */
|
||||
@@ -0,0 +1,53 @@
|
||||
/*!
|
||||
\file usbd_msc_scsi.h
|
||||
\brief the header file of the usbd_msc_scsi.c file
|
||||
|
||||
\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.
|
||||
*/
|
||||
|
||||
#ifndef USBD_MSC_SCSI_H
|
||||
#define USBD_MSC_SCSI_H
|
||||
|
||||
#include "usbd_core.h"
|
||||
|
||||
#define SENSE_LIST_DEEPTH 4U /*!< sense list deepth */
|
||||
|
||||
#define MODE_SENSE6_LENGTH 8U /*!< sense6 mode length */
|
||||
#define MODE_SENSE10_LENGTH 8U /*!< sense10 mode length */
|
||||
#define INQUIRY_PAGE00_LENGTH 96U /*!< sense page 0 inquiry length */
|
||||
#define FORMAT_CAPACITIES_LENGTH 20U /*!< format capacities length */
|
||||
|
||||
/* function declarations */
|
||||
/* process SCSI commands */
|
||||
int8_t scsi_process_cmd(usb_core_driver *udev, uint8_t lun, uint8_t *cmd);
|
||||
/* load the last error code in the error list */
|
||||
void scsi_sense_code(usb_core_driver *udev, uint8_t lun, uint8_t skey, uint8_t asc);
|
||||
|
||||
#endif /* USBD_MSC_SCSI_H */
|
||||
@@ -0,0 +1,286 @@
|
||||
/*!
|
||||
\file usbd_msc_bbb.c
|
||||
\brief USB BBB(Bulk/Bulk/Bulk) protocol core 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 "usbd_msc_bbb.h"
|
||||
#include "usbd_msc_mem.h"
|
||||
|
||||
/* local function prototypes ('static') */
|
||||
static void msc_bbb_cbw_decode(usb_core_driver *udev);
|
||||
static void msc_bbb_data_send(usb_core_driver *udev, uint8_t *pbuf, uint32_t Len);
|
||||
static void msc_bbb_abort(usb_core_driver *udev);
|
||||
|
||||
/*!
|
||||
\brief initialize the BBB process
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void msc_bbb_init(usb_core_driver *udev)
|
||||
{
|
||||
uint8_t lun_num;
|
||||
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_state = BBB_IDLE;
|
||||
msc->bbb_status = BBB_STATUS_NORMAL;
|
||||
|
||||
/* initializes the storage logic unit */
|
||||
for(lun_num = 0U; lun_num < MEM_LUN_NUM; lun_num++) {
|
||||
usbd_mem_fops->mem_init(lun_num);
|
||||
}
|
||||
|
||||
/* flush the RX FIFO */
|
||||
usbd_fifo_flush(udev, MSC_OUT_EP);
|
||||
|
||||
/* flush the TX FIFO */
|
||||
usbd_fifo_flush(udev, MSC_IN_EP);
|
||||
|
||||
/* prepare endpoint to receive the first BBB CBW */
|
||||
usbd_ep_recev(udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief reset the BBB machine
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void msc_bbb_reset(usb_core_driver *udev)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_state = BBB_IDLE;
|
||||
msc->bbb_status = BBB_STATUS_RECOVERY;
|
||||
|
||||
/* prepare endpoint to receive the first BBB command */
|
||||
usbd_ep_recev(udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief de-initialize the BBB machine
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void msc_bbb_deinit(usb_core_driver *udev)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_state = BBB_IDLE;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief handle BBB data IN stage
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] ep_num: endpoint number
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void msc_bbb_data_in(usb_core_driver *udev, uint8_t ep_num)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
switch(msc->bbb_state) {
|
||||
case BBB_DATA_IN:
|
||||
if(scsi_process_cmd(udev, msc->bbb_cbw.bCBWLUN, &msc->bbb_cbw.CBWCB[0]) < 0) {
|
||||
msc_bbb_csw_send(udev, CSW_CMD_FAILED);
|
||||
}
|
||||
break;
|
||||
|
||||
case BBB_SEND_DATA:
|
||||
case BBB_LAST_DATA_IN:
|
||||
msc_bbb_csw_send(udev, CSW_CMD_PASSED);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief handle BBB data OUT stage
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] ep_num: endpoint number
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void msc_bbb_data_out(usb_core_driver *udev, uint8_t ep_num)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
switch(msc->bbb_state) {
|
||||
case BBB_IDLE:
|
||||
msc_bbb_cbw_decode(udev);
|
||||
break;
|
||||
|
||||
case BBB_DATA_OUT:
|
||||
if(scsi_process_cmd(udev, msc->bbb_cbw.bCBWLUN, &msc->bbb_cbw.CBWCB[0]) < 0) {
|
||||
msc_bbb_csw_send(udev, CSW_CMD_FAILED);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send the CSW(command status wrapper)
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] csw_status: CSW status
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void msc_bbb_csw_send(usb_core_driver *udev, uint8_t csw_status)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_csw.dCSWSignature = BBB_CSW_SIGNATURE;
|
||||
msc->bbb_csw.bCSWStatus = csw_status;
|
||||
msc->bbb_state = BBB_IDLE;
|
||||
|
||||
usbd_ep_send(udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
|
||||
|
||||
/* prepare endpoint to receive next command */
|
||||
usbd_ep_recev(udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief complete the clear feature request
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] ep_num: endpoint number
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void msc_bbb_clrfeature(usb_core_driver *udev, uint8_t ep_num)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
if(BBB_STATUS_ERROR == msc->bbb_status) { /* bad CBW signature */
|
||||
usbd_ep_stall(udev, MSC_IN_EP);
|
||||
|
||||
msc->bbb_status = BBB_STATUS_NORMAL;
|
||||
} else if((0x80U == (ep_num & 0x80U)) && (BBB_STATUS_RECOVERY != msc->bbb_status)) {
|
||||
msc_bbb_csw_send(udev, CSW_CMD_FAILED);
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief decode the CBW command and set the BBB state machine accordingly
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
static void msc_bbb_cbw_decode(usb_core_driver *udev)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_csw.dCSWTag = msc->bbb_cbw.dCBWTag;
|
||||
msc->bbb_csw.dCSWDataResidue = msc->bbb_cbw.dCBWDataTransferLength;
|
||||
|
||||
if((BBB_CBW_LENGTH != usbd_rxcount_get(udev, MSC_OUT_EP)) || \
|
||||
(BBB_CBW_SIGNATURE != msc->bbb_cbw.dCBWSignature) || \
|
||||
(msc->bbb_cbw.bCBWLUN > 1U) || \
|
||||
(msc->bbb_cbw.bCBWCBLength < 1U) || \
|
||||
(msc->bbb_cbw.bCBWCBLength > 16U)) {
|
||||
/* illegal command handler */
|
||||
scsi_sense_code(udev, msc->bbb_cbw.bCBWLUN, ILLEGAL_REQUEST, INVALID_CDB);
|
||||
|
||||
msc->bbb_status = BBB_STATUS_ERROR;
|
||||
|
||||
msc_bbb_abort(udev);
|
||||
} else {
|
||||
if(scsi_process_cmd(udev, msc->bbb_cbw.bCBWLUN, &msc->bbb_cbw.CBWCB[0]) < 0) {
|
||||
msc_bbb_abort(udev);
|
||||
} else if((BBB_DATA_IN != msc->bbb_state) && \
|
||||
(BBB_DATA_OUT != msc->bbb_state) && \
|
||||
(BBB_LAST_DATA_IN != msc->bbb_state)) { /* burst transfer handled internally */
|
||||
if(msc->bbb_datalen > 0U) {
|
||||
msc_bbb_data_send(udev, msc->bbb_data, msc->bbb_datalen);
|
||||
} else if(0U == msc->bbb_datalen) {
|
||||
msc_bbb_csw_send(udev, CSW_CMD_PASSED);
|
||||
} else {
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send the requested data
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] buf: pointer to data buffer
|
||||
\param[in] len: data length
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
static void msc_bbb_data_send(usb_core_driver *udev, uint8_t *buf, uint32_t len)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
len = USB_MIN(msc->bbb_cbw.dCBWDataTransferLength, len);
|
||||
|
||||
msc->bbb_csw.dCSWDataResidue -= len;
|
||||
msc->bbb_csw.bCSWStatus = CSW_CMD_PASSED;
|
||||
msc->bbb_state = BBB_SEND_DATA;
|
||||
|
||||
usbd_ep_send(udev, MSC_IN_EP, buf, len);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief abort the current transfer
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
static void msc_bbb_abort(usb_core_driver *udev)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
if((0U == msc->bbb_cbw.bmCBWFlags) && \
|
||||
(0U != msc->bbb_cbw.dCBWDataTransferLength) && \
|
||||
(BBB_STATUS_NORMAL == msc->bbb_status)) {
|
||||
usbd_ep_stall(udev, MSC_OUT_EP);
|
||||
}
|
||||
|
||||
usbd_ep_stall(udev, MSC_IN_EP);
|
||||
|
||||
if(BBB_STATUS_ERROR == msc->bbb_status) {
|
||||
usbd_ep_recev(udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
/*!
|
||||
\file usbd_msc_core.c
|
||||
\brief USB MSC device class core 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 "usbd_enum.h"
|
||||
#include "usbd_msc_bbb.h"
|
||||
#include "usbd_msc_core.h"
|
||||
#include "usbd_msc_mem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define USBD_VID 0x28E9U
|
||||
#define USBD_PID 0x028FU
|
||||
|
||||
/* local function prototypes ('static') */
|
||||
static uint8_t msc_core_init(usb_dev *udev, uint8_t config_index);
|
||||
static uint8_t msc_core_deinit(usb_dev *udev, uint8_t config_index);
|
||||
static uint8_t msc_core_req(usb_dev *udev, usb_req *req);
|
||||
static uint8_t msc_core_in(usb_dev *udev, uint8_t ep_num);
|
||||
static uint8_t msc_core_out(usb_dev *udev, uint8_t ep_num);
|
||||
|
||||
usb_class_core msc_class = {
|
||||
.init = msc_core_init,
|
||||
.deinit = msc_core_deinit,
|
||||
|
||||
.req_proc = msc_core_req,
|
||||
|
||||
.data_in = msc_core_in,
|
||||
.data_out = msc_core_out
|
||||
};
|
||||
|
||||
/* note: it should use the C99 standard when compiling the below codes */
|
||||
/* USB standard device descriptor */
|
||||
__ALIGN_BEGIN const usb_desc_dev msc_dev_desc __ALIGN_END = {
|
||||
.header = {
|
||||
.bLength = USB_DEV_DESC_LEN,
|
||||
.bDescriptorType = USB_DESCTYPE_DEV
|
||||
},
|
||||
.bcdUSB = 0x0200U,
|
||||
.bDeviceClass = 0x00U,
|
||||
.bDeviceSubClass = 0x00U,
|
||||
.bDeviceProtocol = 0x00U,
|
||||
.bMaxPacketSize0 = USB_FS_EP0_MAX_LEN,
|
||||
.idVendor = USBD_VID,
|
||||
.idProduct = USBD_PID,
|
||||
.bcdDevice = 0x0100U,
|
||||
.iManufacturer = STR_IDX_MFC,
|
||||
.iProduct = STR_IDX_PRODUCT,
|
||||
.iSerialNumber = STR_IDX_SERIAL,
|
||||
.bNumberConfigurations = USBD_CFG_MAX_NUM
|
||||
};
|
||||
|
||||
/* USB device configuration descriptor */
|
||||
__ALIGN_BEGIN const usb_desc_config_set msc_config_desc __ALIGN_END = {
|
||||
.config =
|
||||
{
|
||||
.header = {
|
||||
.bLength = sizeof(usb_desc_config),
|
||||
.bDescriptorType = USB_DESCTYPE_CONFIG
|
||||
},
|
||||
.wTotalLength = USB_MSC_CONFIG_DESC_SIZE,
|
||||
.bNumInterfaces = 0x01U,
|
||||
.bConfigurationValue = 0x01U,
|
||||
.iConfiguration = 0x00U,
|
||||
.bmAttributes = 0xC0U,
|
||||
.bMaxPower = 0x32U
|
||||
},
|
||||
|
||||
.msc_itf =
|
||||
{
|
||||
.header = {
|
||||
.bLength = sizeof(usb_desc_itf),
|
||||
.bDescriptorType = USB_DESCTYPE_ITF
|
||||
},
|
||||
.bInterfaceNumber = 0x00U,
|
||||
.bAlternateSetting = 0x00U,
|
||||
.bNumEndpoints = 0x02U,
|
||||
.bInterfaceClass = USB_CLASS_MSC,
|
||||
.bInterfaceSubClass = USB_MSC_SUBCLASS_SCSI,
|
||||
.bInterfaceProtocol = USB_MSC_PROTOCOL_BBB,
|
||||
.iInterface = 0x00U
|
||||
},
|
||||
|
||||
.msc_epin =
|
||||
{
|
||||
.header = {
|
||||
.bLength = sizeof(usb_desc_ep),
|
||||
.bDescriptorType = USB_DESCTYPE_EP
|
||||
},
|
||||
.bEndpointAddress = MSC_IN_EP,
|
||||
.bmAttributes = USB_EP_ATTR_BULK,
|
||||
.wMaxPacketSize = MSC_EPIN_SIZE,
|
||||
.bInterval = 0x00U
|
||||
},
|
||||
|
||||
.msc_epout =
|
||||
{
|
||||
.header = {
|
||||
.bLength = sizeof(usb_desc_ep),
|
||||
.bDescriptorType = USB_DESCTYPE_EP
|
||||
},
|
||||
.bEndpointAddress = MSC_OUT_EP,
|
||||
.bmAttributes = USB_EP_ATTR_BULK,
|
||||
.wMaxPacketSize = MSC_EPOUT_SIZE,
|
||||
.bInterval = 0x00U
|
||||
}
|
||||
};
|
||||
|
||||
/* USB device other speed configuration descriptor */
|
||||
__ALIGN_BEGIN const usb_desc_config_set other_speed_msc_config_desc __ALIGN_END = {
|
||||
.config =
|
||||
{
|
||||
.header = {
|
||||
.bLength = sizeof(usb_desc_config),
|
||||
.bDescriptorType = USB_DESCTYPE_OTHER_SPD_CONFIG
|
||||
},
|
||||
.wTotalLength = USB_MSC_CONFIG_DESC_SIZE,
|
||||
.bNumInterfaces = 0x01U,
|
||||
.bConfigurationValue = 0x01U,
|
||||
.iConfiguration = 0x00U,
|
||||
.bmAttributes = 0xC0U,
|
||||
.bMaxPower = 0x32U
|
||||
},
|
||||
|
||||
.msc_itf =
|
||||
{
|
||||
.header = {
|
||||
.bLength = sizeof(usb_desc_itf),
|
||||
.bDescriptorType = USB_DESCTYPE_ITF
|
||||
},
|
||||
.bInterfaceNumber = 0x00U,
|
||||
.bAlternateSetting = 0x00U,
|
||||
.bNumEndpoints = 0x02U,
|
||||
.bInterfaceClass = USB_CLASS_MSC,
|
||||
.bInterfaceSubClass = USB_MSC_SUBCLASS_SCSI,
|
||||
.bInterfaceProtocol = USB_MSC_PROTOCOL_BBB,
|
||||
.iInterface = 0x00U
|
||||
},
|
||||
|
||||
.msc_epin =
|
||||
{
|
||||
.header = {
|
||||
.bLength = sizeof(usb_desc_ep),
|
||||
.bDescriptorType = USB_DESCTYPE_EP
|
||||
},
|
||||
.bEndpointAddress = MSC_IN_EP,
|
||||
.bmAttributes = USB_EP_ATTR_BULK,
|
||||
.wMaxPacketSize = 64U,
|
||||
.bInterval = 0x00U
|
||||
},
|
||||
|
||||
.msc_epout =
|
||||
{
|
||||
.header = {
|
||||
.bLength = sizeof(usb_desc_ep),
|
||||
.bDescriptorType = USB_DESCTYPE_EP
|
||||
},
|
||||
.bEndpointAddress = MSC_OUT_EP,
|
||||
.bmAttributes = USB_EP_ATTR_BULK,
|
||||
.wMaxPacketSize = 64U,
|
||||
.bInterval = 0x00U
|
||||
}
|
||||
};
|
||||
|
||||
__ALIGN_BEGIN const uint8_t usbd_qualifier_desc[10] __ALIGN_END =
|
||||
{
|
||||
0x0AU,
|
||||
0x06U,
|
||||
0x00U,
|
||||
0x02U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x40U,
|
||||
0x01U,
|
||||
0x00U
|
||||
};
|
||||
|
||||
/* USB language ID descriptor */
|
||||
__ALIGN_BEGIN const usb_desc_LANGID usbd_language_id_desc __ALIGN_END = {
|
||||
.header =
|
||||
{
|
||||
.bLength = sizeof(usb_desc_LANGID),
|
||||
.bDescriptorType = USB_DESCTYPE_STR
|
||||
},
|
||||
.wLANGID = ENG_LANGID
|
||||
};
|
||||
|
||||
/* USB manufacture string */
|
||||
__ALIGN_BEGIN static const usb_desc_str manufacturer_string __ALIGN_END = {
|
||||
.header =
|
||||
{
|
||||
.bLength = USB_STRING_LEN(10U),
|
||||
.bDescriptorType = USB_DESCTYPE_STR
|
||||
},
|
||||
.unicode_string = {'G', 'i', 'g', 'a', 'D', 'e', 'v', 'i', 'c', 'e'}
|
||||
};
|
||||
|
||||
/* USB product string */
|
||||
__ALIGN_BEGIN static const usb_desc_str product_string __ALIGN_END = {
|
||||
.header =
|
||||
{
|
||||
.bLength = USB_STRING_LEN(12U),
|
||||
.bDescriptorType = USB_DESCTYPE_STR
|
||||
},
|
||||
.unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'M', 'S', 'C'}
|
||||
};
|
||||
|
||||
/* USBD serial string */
|
||||
__ALIGN_BEGIN static usb_desc_str serial_string __ALIGN_END = {
|
||||
.header =
|
||||
{
|
||||
.bLength = USB_STRING_LEN(12U),
|
||||
.bDescriptorType = USB_DESCTYPE_STR
|
||||
}
|
||||
};
|
||||
|
||||
/* USB string descriptor */
|
||||
void *const usbd_msc_strings[] = {
|
||||
[STR_IDX_LANGID] = (uint8_t *)&usbd_language_id_desc,
|
||||
[STR_IDX_MFC] = (uint8_t *)&manufacturer_string,
|
||||
[STR_IDX_PRODUCT] = (uint8_t *)&product_string,
|
||||
[STR_IDX_SERIAL] = (uint8_t *)&serial_string
|
||||
};
|
||||
|
||||
usb_desc msc_desc = {
|
||||
.dev_desc = (uint8_t *)&msc_dev_desc,
|
||||
.config_desc = (uint8_t *)&msc_config_desc,
|
||||
.strings = usbd_msc_strings,
|
||||
#ifdef USE_USB_HS
|
||||
.other_speed_config_desc = (uint8_t *)&other_speed_msc_config_desc,
|
||||
.qualifier_desc = (uint8_t *)&usbd_qualifier_desc,
|
||||
#endif /* USE_USB_HS */
|
||||
};
|
||||
|
||||
static uint8_t usbd_msc_maxlun = 0U;
|
||||
|
||||
/*!
|
||||
\brief initialize the MSC device
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] config_index: configuration index
|
||||
\param[out] none
|
||||
\retval USB device operation status
|
||||
*/
|
||||
static uint8_t msc_core_init(usb_dev *udev, uint8_t config_index)
|
||||
{
|
||||
static usbd_msc_handler msc_handler;
|
||||
|
||||
memset((void *)&msc_handler, 0U, sizeof(usbd_msc_handler));
|
||||
|
||||
udev->dev.class_data[USBD_MSC_INTERFACE] = (void *)&msc_handler;
|
||||
|
||||
/* configure MSC TX endpoint */
|
||||
usbd_ep_setup(udev, &(msc_config_desc.msc_epin));
|
||||
|
||||
/* configure MSC RX endpoint */
|
||||
usbd_ep_setup(udev, &(msc_config_desc.msc_epout));
|
||||
|
||||
/* initialize the BBB layer */
|
||||
msc_bbb_init(udev);
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief de-initialize the MSC device
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] config_index: configuration index
|
||||
\param[out] none
|
||||
\retval USB device operation status
|
||||
*/
|
||||
static uint8_t msc_core_deinit(usb_dev *udev, uint8_t config_index)
|
||||
{
|
||||
/* clear MSC endpoints */
|
||||
usbd_ep_clear(udev, MSC_IN_EP);
|
||||
usbd_ep_clear(udev, MSC_OUT_EP);
|
||||
|
||||
/* deinitialize the BBB layer */
|
||||
msc_bbb_deinit(udev);
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief handle the MSC class-specific and standard requests
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] req: device class-specific request
|
||||
\param[out] none
|
||||
\retval USB device operation status
|
||||
*/
|
||||
static uint8_t msc_core_req(usb_dev *udev, usb_req *req)
|
||||
{
|
||||
usb_transc *transc = &udev->dev.transc_in[0];
|
||||
|
||||
switch(req->bRequest) {
|
||||
case BBB_GET_MAX_LUN :
|
||||
if((0U == req->wValue) && \
|
||||
(1U == req->wLength) && \
|
||||
(0x80U == (req->bmRequestType & 0x80U))) {
|
||||
usbd_msc_maxlun = (uint8_t)usbd_mem_fops->mem_maxlun();
|
||||
|
||||
transc->xfer_buf = &usbd_msc_maxlun;
|
||||
transc->remain_len = 1U;
|
||||
} else {
|
||||
return USBD_FAIL;
|
||||
}
|
||||
break;
|
||||
|
||||
case BBB_RESET :
|
||||
if((0U == req->wValue) && \
|
||||
(0U == req->wLength) && \
|
||||
(0x80U != (req->bmRequestType & 0x80U))) {
|
||||
msc_bbb_reset(udev);
|
||||
} else {
|
||||
return USBD_FAIL;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_CLEAR_FEATURE:
|
||||
msc_bbb_clrfeature(udev, (uint8_t)req->wIndex);
|
||||
break;
|
||||
|
||||
default:
|
||||
return USBD_FAIL;
|
||||
}
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief handle data IN stage
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] ep_num: the endpoint number
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
static uint8_t msc_core_in(usb_dev *udev, uint8_t ep_num)
|
||||
{
|
||||
msc_bbb_data_in(udev, ep_num);
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief handle data OUT stage
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] ep_num: the endpoint number
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
static uint8_t msc_core_out(usb_dev *udev, uint8_t ep_num)
|
||||
{
|
||||
msc_bbb_data_out(udev, ep_num);
|
||||
|
||||
return USBD_OK;
|
||||
}
|
||||
@@ -0,0 +1,760 @@
|
||||
/*!
|
||||
\file usbd_msc_scsi.c
|
||||
\brief USB SCSI layer 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 "usbd_msc_mem.h"
|
||||
#include "usbd_msc_bbb.h"
|
||||
#include "usbd_msc_scsi.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* USB mass storage page 0 inquiry data */
|
||||
const uint8_t msc_page00_inquiry_data[] = {
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
(INQUIRY_PAGE00_LENGTH - 4U),
|
||||
0x80U,
|
||||
0x83U,
|
||||
};
|
||||
|
||||
/* USB mass storage sense 6 data */
|
||||
const uint8_t msc_mode_sense6_data[] = {
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U
|
||||
};
|
||||
|
||||
/* USB mass storage sense 10 data */
|
||||
const uint8_t msc_mode_sense10_data[] = {
|
||||
0x00U,
|
||||
0x06U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U,
|
||||
0x00U
|
||||
};
|
||||
|
||||
/* local function prototypes ('static') */
|
||||
static int8_t scsi_test_unit_ready(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_mode_select6(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_mode_select10(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_inquiry(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_read_format_capacity(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_read_capacity10(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_request_sense(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_mode_sense6(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_toc_cmd_read(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_mode_sense10(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_write10(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_read10(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static int8_t scsi_verify10(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
|
||||
static int8_t scsi_process_read(usb_core_driver *udev, uint8_t lun);
|
||||
static int8_t scsi_process_write(usb_core_driver *udev, uint8_t lun);
|
||||
|
||||
static inline int8_t scsi_check_address_range(usb_core_driver *udev, uint8_t lun, uint32_t blk_offset, uint16_t blk_nbr);
|
||||
static inline int8_t scsi_format_cmd(usb_core_driver *udev, uint8_t lun);
|
||||
static inline int8_t scsi_start_stop_unit(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
static inline int8_t scsi_allow_medium_removal(usb_core_driver *udev, uint8_t lun, uint8_t *params);
|
||||
|
||||
/*!
|
||||
\brief process SCSI commands
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
int8_t scsi_process_cmd(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
switch(params[0]) {
|
||||
case SCSI_TEST_UNIT_READY:
|
||||
return scsi_test_unit_ready(udev, lun, params);
|
||||
|
||||
case SCSI_REQUEST_SENSE:
|
||||
return scsi_request_sense(udev, lun, params);
|
||||
|
||||
case SCSI_INQUIRY:
|
||||
return scsi_inquiry(udev, lun, params);
|
||||
|
||||
case SCSI_START_STOP_UNIT:
|
||||
return scsi_start_stop_unit(udev, lun, params);
|
||||
|
||||
case SCSI_ALLOW_MEDIUM_REMOVAL:
|
||||
return scsi_allow_medium_removal(udev, lun, params);
|
||||
|
||||
case SCSI_MODE_SENSE6:
|
||||
return scsi_mode_sense6(udev, lun, params);
|
||||
|
||||
case SCSI_MODE_SENSE10:
|
||||
return scsi_mode_sense10(udev, lun, params);
|
||||
|
||||
case SCSI_READ_FORMAT_CAPACITIES:
|
||||
return scsi_read_format_capacity(udev, lun, params);
|
||||
|
||||
case SCSI_READ_CAPACITY10:
|
||||
return scsi_read_capacity10(udev, lun, params);
|
||||
|
||||
case SCSI_READ10:
|
||||
return scsi_read10(udev, lun, params);
|
||||
|
||||
case SCSI_WRITE10:
|
||||
return scsi_write10(udev, lun, params);
|
||||
|
||||
case SCSI_VERIFY10:
|
||||
return scsi_verify10(udev, lun, params);
|
||||
|
||||
case SCSI_FORMAT_UNIT:
|
||||
return scsi_format_cmd(udev, lun);
|
||||
|
||||
case SCSI_READ_TOC_DATA:
|
||||
return scsi_toc_cmd_read(udev, lun, params);
|
||||
|
||||
case SCSI_MODE_SELECT6:
|
||||
return scsi_mode_select6(udev, lun, params);
|
||||
|
||||
case SCSI_MODE_SELECT10:
|
||||
return scsi_mode_select10(udev, lun, params);
|
||||
|
||||
default:
|
||||
scsi_sense_code(udev, lun, ILLEGAL_REQUEST, INVALID_CDB);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief load the last error code in the error list
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] skey: sense key
|
||||
\param[in] asc: additional sense key
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void scsi_sense_code(usb_core_driver *udev, uint8_t lun, uint8_t skey, uint8_t asc)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->scsi_sense[msc->scsi_sense_tail].SenseKey = skey;
|
||||
msc->scsi_sense[msc->scsi_sense_tail].ASC = asc;
|
||||
msc->scsi_sense_tail++;
|
||||
|
||||
if(SENSE_LIST_DEEPTH == msc->scsi_sense_tail) {
|
||||
msc->scsi_sense_tail = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process SCSI Test Unit Ready command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_test_unit_ready(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
/* case 9 : Hi > D0 */
|
||||
if(0U != msc->bbb_cbw.dCBWDataTransferLength) {
|
||||
scsi_sense_code(udev, msc->bbb_cbw.bCBWLUN, ILLEGAL_REQUEST, INVALID_CDB);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(0 != usbd_mem_fops->mem_ready(lun)) {
|
||||
scsi_sense_code(udev, lun, NOT_READY, MEDIUM_NOT_PRESENT);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
msc->bbb_datalen = 0U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process mode select 6 command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_mode_select6(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_datalen = 0U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process mode select 10 command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_mode_select10(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_datalen = 0U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process inquiry command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_inquiry(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
uint8_t *page = NULL;
|
||||
uint16_t len = 0U;
|
||||
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
if(params[1] & 0x01U) {
|
||||
page = (uint8_t *)msc_page00_inquiry_data;
|
||||
|
||||
len = INQUIRY_PAGE00_LENGTH;
|
||||
} else {
|
||||
page = (uint8_t *)usbd_mem_fops->mem_inquiry_data[lun];
|
||||
|
||||
len = (uint16_t)(page[4] + 5U);
|
||||
|
||||
if(params[4] <= len) {
|
||||
len = params[4];
|
||||
}
|
||||
}
|
||||
|
||||
msc->bbb_datalen = len;
|
||||
|
||||
while(len) {
|
||||
len--;
|
||||
msc->bbb_data[len] = page[len];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process read capacity 10 command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_read_capacity10(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
uint32_t blk_num = usbd_mem_fops->mem_block_len[lun] - 1U;
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->scsi_blk_nbr[lun] = usbd_mem_fops->mem_block_len[lun];
|
||||
msc->scsi_blk_size[lun] = usbd_mem_fops->mem_block_size[lun];
|
||||
|
||||
msc->bbb_data[0] = (uint8_t)(blk_num >> 24);
|
||||
msc->bbb_data[1] = (uint8_t)(blk_num >> 16);
|
||||
msc->bbb_data[2] = (uint8_t)(blk_num >> 8);
|
||||
msc->bbb_data[3] = (uint8_t)(blk_num);
|
||||
|
||||
msc->bbb_data[4] = (uint8_t)(msc->scsi_blk_size[lun] >> 24);
|
||||
msc->bbb_data[5] = (uint8_t)(msc->scsi_blk_size[lun] >> 16);
|
||||
msc->bbb_data[6] = (uint8_t)(msc->scsi_blk_size[lun] >> 8);
|
||||
msc->bbb_data[7] = (uint8_t)(msc->scsi_blk_size[lun]);
|
||||
|
||||
msc->bbb_datalen = 8U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process read format capacity command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_read_format_capacity(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
uint16_t i = 0U;
|
||||
uint32_t blk_size = usbd_mem_fops->mem_block_size[lun];
|
||||
uint32_t blk_num = usbd_mem_fops->mem_block_len[lun];
|
||||
uint32_t blk_nbr = blk_num - 1U;
|
||||
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
for(i = 0U; i < 12U; i++) {
|
||||
msc->bbb_data[i] = 0U;
|
||||
}
|
||||
|
||||
msc->bbb_data[3] = 0x08U;
|
||||
msc->bbb_data[4] = (uint8_t)(blk_nbr >> 24);
|
||||
msc->bbb_data[5] = (uint8_t)(blk_nbr >> 16);
|
||||
msc->bbb_data[6] = (uint8_t)(blk_nbr >> 8);
|
||||
msc->bbb_data[7] = (uint8_t)(blk_nbr);
|
||||
|
||||
msc->bbb_data[8] = 0x02U;
|
||||
msc->bbb_data[9] = (uint8_t)(blk_size >> 16);
|
||||
msc->bbb_data[10] = (uint8_t)(blk_size >> 8);
|
||||
msc->bbb_data[11] = (uint8_t)(blk_size);
|
||||
|
||||
msc->bbb_datalen = 12U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process mode sense6 command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_mode_sense6(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
uint16_t len = 8U;
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_datalen = len;
|
||||
|
||||
while(len) {
|
||||
len--;
|
||||
msc->bbb_data[len] = msc_mode_sense6_data[len];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process mode sense10 command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_mode_sense10(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
uint16_t len = 8U;
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_datalen = len;
|
||||
|
||||
while(len) {
|
||||
len--;
|
||||
msc->bbb_data[len] = msc_mode_sense10_data[len];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process request sense command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_request_sense(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
uint8_t i = 0U;
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
for(i = 0U; i < REQUEST_SENSE_DATA_LEN; i++) {
|
||||
msc->bbb_data[i] = 0U;
|
||||
}
|
||||
|
||||
msc->bbb_data[0] = 0x70U;
|
||||
msc->bbb_data[7] = REQUEST_SENSE_DATA_LEN - 6U;
|
||||
|
||||
if((msc->scsi_sense_head != msc->scsi_sense_tail)) {
|
||||
msc->bbb_data[2] = msc->scsi_sense[msc->scsi_sense_head].SenseKey;
|
||||
msc->bbb_data[12] = msc->scsi_sense[msc->scsi_sense_head].ASC;
|
||||
msc->bbb_data[13] = msc->scsi_sense[msc->scsi_sense_head].ASCQ;
|
||||
msc->scsi_sense_head++;
|
||||
|
||||
if(SENSE_LIST_DEEPTH == msc->scsi_sense_head) {
|
||||
msc->scsi_sense_head = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
msc->bbb_datalen = USB_MIN(REQUEST_SENSE_DATA_LEN, params[4]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process start stop unit command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static inline int8_t scsi_start_stop_unit(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_datalen = 0U;
|
||||
msc->scsi_disk_pop = 1U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process allow medium removal command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static inline int8_t scsi_allow_medium_removal(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
msc->bbb_datalen = 0U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process read10 command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_read10(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
if(BBB_IDLE == msc->bbb_state) {
|
||||
/* direction is from device to host */
|
||||
if(0x80U != (msc->bbb_cbw.bmCBWFlags & 0x80U)) {
|
||||
scsi_sense_code(udev, msc->bbb_cbw.bCBWLUN, ILLEGAL_REQUEST, INVALID_CDB);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(0 != usbd_mem_fops->mem_ready(lun)) {
|
||||
scsi_sense_code(udev, lun, NOT_READY, MEDIUM_NOT_PRESENT);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
msc->scsi_blk_addr = (params[2] << 24) | (params[3] << 16) | \
|
||||
(params[4] << 8) | params[5];
|
||||
|
||||
msc->scsi_blk_len = (params[7] << 8) | params[8];
|
||||
|
||||
if(scsi_check_address_range(udev, lun, msc->scsi_blk_addr, (uint16_t)msc->scsi_blk_len) < 0) {
|
||||
return -1; /* error */
|
||||
}
|
||||
|
||||
msc->bbb_state = BBB_DATA_IN;
|
||||
|
||||
msc->scsi_blk_addr *= msc->scsi_blk_size[lun];
|
||||
msc->scsi_blk_len *= msc->scsi_blk_size[lun];
|
||||
|
||||
/* cases 4,5 : Hi <> Dn */
|
||||
if(msc->bbb_cbw.dCBWDataTransferLength != msc->scsi_blk_len) {
|
||||
scsi_sense_code(udev, msc->bbb_cbw.bCBWLUN, ILLEGAL_REQUEST, INVALID_CDB);
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
msc->bbb_datalen = MSC_MEDIA_PACKET_SIZE;
|
||||
|
||||
return scsi_process_read(udev, lun);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process write10 command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_write10(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
if(BBB_IDLE == msc->bbb_state) {
|
||||
/* case 8 : Hi <> Do */
|
||||
if(0x80U == (msc->bbb_cbw.bmCBWFlags & 0x80U)) {
|
||||
scsi_sense_code(udev, msc->bbb_cbw.bCBWLUN, ILLEGAL_REQUEST, INVALID_CDB);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check whether media is ready */
|
||||
if(0 != usbd_mem_fops->mem_ready(lun)) {
|
||||
scsi_sense_code(udev, lun, NOT_READY, MEDIUM_NOT_PRESENT);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check if media is write-protected */
|
||||
if(0 != usbd_mem_fops->mem_protected(lun)) {
|
||||
scsi_sense_code(udev, lun, NOT_READY, WRITE_PROTECTED);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
msc->scsi_blk_addr = (params[2] << 24) | (params[3] << 16) | \
|
||||
(params[4] << 8) | params[5];
|
||||
|
||||
msc->scsi_blk_len = (params[7] << 8) | params[8];
|
||||
|
||||
/* check if LBA address is in the right range */
|
||||
if(scsi_check_address_range(udev, lun, msc->scsi_blk_addr, (uint16_t)msc->scsi_blk_len) < 0) {
|
||||
return -1; /* error */
|
||||
}
|
||||
|
||||
msc->scsi_blk_addr *= msc->scsi_blk_size[lun];
|
||||
msc->scsi_blk_len *= msc->scsi_blk_size[lun];
|
||||
|
||||
/* cases 3,11,13 : Hn,Ho <> D0 */
|
||||
if(msc->bbb_cbw.dCBWDataTransferLength != msc->scsi_blk_len) {
|
||||
scsi_sense_code(udev, msc->bbb_cbw.bCBWLUN, ILLEGAL_REQUEST, INVALID_CDB);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* prepare endpoint to receive first data packet */
|
||||
msc->bbb_state = BBB_DATA_OUT;
|
||||
|
||||
usbd_ep_recev(udev, \
|
||||
MSC_OUT_EP, \
|
||||
msc->bbb_data, \
|
||||
USB_MIN(msc->scsi_blk_len, MSC_MEDIA_PACKET_SIZE));
|
||||
} else { /* write process ongoing */
|
||||
return scsi_process_write(udev, lun);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process verify10 command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_verify10(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
if(0x02U == (params[1] & 0x02U)) {
|
||||
scsi_sense_code(udev, lun, ILLEGAL_REQUEST, INVALID_FIELD_IN_COMMAND);
|
||||
|
||||
return -1; /* error, verify mode not supported*/
|
||||
}
|
||||
|
||||
if(scsi_check_address_range(udev, lun, msc->scsi_blk_addr, (uint16_t)msc->scsi_blk_len) < 0) {
|
||||
return -1; /* error */
|
||||
}
|
||||
|
||||
msc->bbb_datalen = 0U;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief check address range
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] blk_offset: block offset
|
||||
\param[in] blk_nbr: number of block to be processed
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static inline int8_t scsi_check_address_range(usb_core_driver *udev, uint8_t lun, uint32_t blk_offset, uint16_t blk_nbr)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
if((blk_offset + blk_nbr) > msc->scsi_blk_nbr[lun]) {
|
||||
scsi_sense_code(udev, lun, ILLEGAL_REQUEST, ADDRESS_OUT_OF_RANGE);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief handle read process
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_process_read(usb_core_driver *udev, uint8_t lun)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
uint32_t len = USB_MIN(msc->scsi_blk_len, MSC_MEDIA_PACKET_SIZE);
|
||||
|
||||
if(usbd_mem_fops->mem_read(lun, \
|
||||
msc->bbb_data, \
|
||||
msc->scsi_blk_addr, \
|
||||
(uint16_t)(len / msc->scsi_blk_size[lun])) < 0) {
|
||||
scsi_sense_code(udev, lun, HARDWARE_ERROR, UNRECOVERED_READ_ERROR);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
usbd_ep_send(udev, MSC_IN_EP, msc->bbb_data, len);
|
||||
|
||||
msc->scsi_blk_addr += len;
|
||||
msc->scsi_blk_len -= len;
|
||||
|
||||
/* case 6 : Hi = Di */
|
||||
msc->bbb_csw.dCSWDataResidue -= len;
|
||||
|
||||
if(0U == msc->scsi_blk_len) {
|
||||
msc->bbb_state = BBB_LAST_DATA_IN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief handle write process
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_process_write(usb_core_driver *udev, uint8_t lun)
|
||||
{
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
uint32_t len = USB_MIN(msc->scsi_blk_len, MSC_MEDIA_PACKET_SIZE);
|
||||
|
||||
if(usbd_mem_fops->mem_write(lun, \
|
||||
msc->bbb_data, \
|
||||
msc->scsi_blk_addr, \
|
||||
(uint16_t)(len / msc->scsi_blk_size[lun])) < 0) {
|
||||
scsi_sense_code(udev, lun, HARDWARE_ERROR, WRITE_FAULT);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
msc->scsi_blk_addr += len;
|
||||
msc->scsi_blk_len -= len;
|
||||
|
||||
/* case 12 : Ho = Do */
|
||||
msc->bbb_csw.dCSWDataResidue -= len;
|
||||
|
||||
if(0U == msc->scsi_blk_len) {
|
||||
msc_bbb_csw_send(udev, CSW_CMD_PASSED);
|
||||
} else {
|
||||
/* prepare endpoint to receive next packet */
|
||||
usbd_ep_recev(udev, \
|
||||
MSC_OUT_EP, \
|
||||
msc->bbb_data, \
|
||||
USB_MIN(msc->scsi_blk_len, MSC_MEDIA_PACKET_SIZE));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process format unit command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static inline int8_t scsi_format_cmd(usb_core_driver *udev, uint8_t lun)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief process Read_Toc command
|
||||
\param[in] udev: pointer to USB device instance
|
||||
\param[in] lun: logical unit number
|
||||
\param[in] params: command parameters
|
||||
\param[out] none
|
||||
\retval status
|
||||
*/
|
||||
static int8_t scsi_toc_cmd_read(usb_core_driver *udev, uint8_t lun, uint8_t *params)
|
||||
{
|
||||
uint8_t* pPage;
|
||||
uint16_t len;
|
||||
|
||||
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
|
||||
|
||||
pPage = (uint8_t *)&usbd_mem_fops->mem_toc_data[lun * READ_TOC_CMD_LEN];
|
||||
len = (uint16_t)pPage[1] + 2U;
|
||||
|
||||
msc->bbb_datalen = len;
|
||||
|
||||
while(len) {
|
||||
len--;
|
||||
msc->bbb_data[len] = pPage[len];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user