Sync betaflight to Gitea

This commit is contained in:
2026-08-03 16:37:10 +08:00
commit ad3163cf91
4712 changed files with 3301380 additions and 0 deletions
@@ -0,0 +1,316 @@
/*!
\file audio_core.h
\brief the header file of USB audio device class core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 AUDIO_CORE_H
#define AUDIO_CORE_H
#include "usbd_enum.h"
#define FORMAT_24BIT(x) (uint8_t)(x);(uint8_t)((x) >> 8);(uint8_t)((x) >> 16)
/* number of sub-packets in the audio transfer buffer. you can modify this value but always make sure
that it is an even number and higher than 3 */
#define OUT_PACKET_NUM 200U
/* total size of the audio transfer buffer */
#define OUT_BUF_MARGIN 0U
#define TOTAL_OUT_BUF_SIZE ((uint32_t)((SPEAKER_OUT_PACKET + OUT_BUF_MARGIN) * OUT_PACKET_NUM))
/* audio configuration descriptor length and interface descriptor size */
#define AD_CONFIG_DESC_SET_LEN (sizeof(usb_desc_config_set))
#define AD_INTERFACE_DESC_SIZE 9U
/* audio standard endpoint and streaming endpoint descriptor size */
#define USB_AD_DESC_SIZ 0x09U /*!< audio descriptor size */
#define AD_STANDARD_EP_DESC_SIZE 0x09U /*!< audio standard endpoint descriptor size */
#define AD_STREAMING_EP_DESC_SIZE 0x07U /*!< audio streaming endpoint descriptor size */
/* audio interface class code */
#define USB_CLASS_AUDIO 0x01U
/* audio interface subclass codes */
#define AD_SUBCLASS_CONTROL 0x01U /*!< audio interface control */
#define AD_SUBCLASS_AUDIOSTREAMING 0x02U /*!< audio interface audiostreaming */
#define AD_SUBCLASS_MIDISTREAMING 0x03U /*!< audio interface midistreaming */
/* audio interface protocol codes */
#define AD_PROTOCOL_UNDEFINED 0x00U /*!< audio interface undefined */
#define AD_STREAMING_GENERAL 0x01U /*!< audio interface streaming general */
#define AD_STREAMING_FORMAT_TYPE 0x02U /*!< audio interface streaming format type */
/* audio class-specific descriptor types */
#define AD_DESCTYPE_UNDEFINED 0x20U /*!< audio class-specific descriptor undefined */
#define AD_DESCTYPE_DEVICE 0x21U /*!< audio device descriptor */
#define AD_DESCTYPE_CONFIGURATION 0x22U /*!< audio configuration descriptor */
#define AD_DESCTYPE_STRING 0x23U /*!< audio string descriptor */
#define AD_DESCTYPE_INTERFACE 0x24U /*!< audio interface descriptor */
#define AD_DESCTYPE_ENDPOINT 0x25U /*!< audio endpoint descriptor */
/* audio control interface descriptor subtypes */
#define AD_CONTROL_HEADER 0x01U /*!< audio control interface header descriptor */
#define AD_CONTROL_INPUT_TERMINAL 0x02U /*!< audio control interface input terminal descriptor */
#define AD_CONTROL_OUTPUT_TERMINAL 0x03U /*!< audio control interface output terminal descriptor */
#define AD_CONTROL_MIXER_UNIT 0x04U /*!< audio control interface maximum unit descriptor */
#define AD_CONTROL_SELECTOR_UNIT 0x05U /*!< audio control interface selector unit descriptor */
#define AD_CONTROL_FEATURE_UNIT 0x06U /*!< audio control interface feature unit descriptor */
#define AD_CONTROL_PROCESSING_UNIT 0x07U /*!< audio control interface processing unit descriptor */
#define AD_CONTROL_EXTENSION_UNIT 0x08U /*!< audio control interface extension unit descriptor */
/* audio input/output terminal and streaming interface descriptor size */
#define AD_INPUT_TERMINAL_DESC_SIZE 0x0CU /*!< audio input terminal interface descriptor size */
#define AD_OUTPUT_TERMINAL_DESC_SIZE 0x09U /*!< audio output terminal interface descriptor size */
#define AD_STREAMING_INTERFACE_DESC_SIZE 0x07U /*!< audio streaming interface descriptor size */
/* audio control types */
#define AD_CONTROL_MUTE 0x01U /*!< audio control mute type */
#define AD_CONTROL_VOLUME 0x02U /*!< audio control volume type */
/* audio format types */
#define AD_FORMAT_TYPE_I 0x01U /*!< audio format typeI */
#define AD_FORMAT_TYPE_III 0x03U /*!< audio format typeIII */
/* endpoint types */
#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01U /*!< audio isochronous endpoint type */
#define AD_ENDPOINT_GENERAL 0x01U /*!< audio general endpoint type */
/* audio request types */
#define AD_REQ_UNDEFINED 0x00U /*!< audio undefined request */
#define AD_REQ_SET_CUR 0x01U /*!< current setting attribute request */
#define AD_REQ_GET_CUR 0x81U /*!< current getting attribute request */
#define AD_REQ_SET_MIN 0x02U /*!< setting minimum range attribute request */
#define AD_REQ_GET_MIN 0x82U /*!< getting minimum range attribute request */
#define AD_REQ_SET_MAX 0x03U /*!< setting maximum range attribute request */
#define AD_REQ_GET_MAX 0x83U /*!< getting maximum range attribute request */
#define AD_REQ_SET_RES 0x04U /*!< setting range attribute request */
#define AD_REQ_GET_RES 0x84U /*!< getting range attribute request */
#define AD_REQ_SET_MEM 0x05U /*!< setting memory attribute request */
#define AD_REQ_GET_MEM 0x85U /*!< getting memory attribute request */
#define AD_REQ_GET_STAT 0xFFU /*!< getting state request */
/* streaming control types */
#define AD_OUT_STREAMING_CTRL 0x05U /*!< audio streaming control OUT */
#define AD_IN_STREAMING_CTRL 0x02U /*!< audio streaming control IN */
/* audio stream interface number */
enum {
#ifdef USE_USB_AD_MICPHONE
MIC_INTERFACE_COUNT, /*!< audio microphone interface count */
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
SPEAK_INTERFACE_COUNT, /*!< audio speaker interface count */
#endif /* USE_USB_AD_SPEAKER */
CONFIG_DESC_AS_ITF_COUNT /*!< audio system interface count */
};
/* audio interface descriptor total length */
#define AC_ITF_TOTAL_LEN (sizeof(usb_desc_AC_itf) + CONFIG_DESC_AS_ITF_COUNT*(sizeof(usb_desc_input_terminal) + \
sizeof(usb_desc_mono_feature_unit) + sizeof(usb_desc_output_terminal)))
#pragma pack(1)
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< header descriptor subtype */
uint16_t bcdADC; /*!< audio device class specification release number in binary-coded decimal */
uint16_t wTotalLength; /*!< total number of bytes */
uint8_t bInCollection; /*!< the number of the streaming interfaces */
#ifdef USE_USB_AD_MICPHONE
uint8_t baInterfaceNr0; /*!< interface number of the streaming interfaces */
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
uint8_t baInterfaceNr1; /*!< interface number of the streaming interfaces */
#endif /* USE_USB_AD_SPEAKER */
} usb_desc_AC_itf;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< general audio system descriptor subtype */
uint8_t bTerminalLink; /*!< the terminal ID */
uint8_t bDelay; /*!< delay introduced by the data path */
uint16_t wFormatTag; /*!< the audio data format */
} usb_desc_AS_itf;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< input terminal descriptor subtype. */
uint8_t bTerminalID; /*!< constant uniquely identifying the terminal within the audio function */
uint16_t wTerminalType; /*!< constant characterizing the type of terminal */
uint8_t bAssocTerminal; /*!< ID of the output terminal */
uint8_t bNrChannels; /*!< number of logical output channels */
uint16_t wChannelConfig; /*!< describes the spatial location of the logical channels */
uint8_t iChannelNames; /*!< index of a string descriptor */
uint8_t iTerminal; /*!< index of a string descriptor */
} usb_desc_input_terminal;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< output terminal descriptor subtype */
uint8_t bTerminalID; /*!< constant uniquely identifying the terminal within the audio function */
uint16_t wTerminalType; /*!< constant characterizing the type of terminal */
uint8_t bAssocTerminal; /*!< constant, identifying the input terminal to which this output terminal is associated */
uint8_t bSourceID; /*!< ID of the unit or terminal */
uint8_t iTerminal; /*!< index of a string descriptor */
} usb_desc_output_terminal;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< feature unit descriptor subtype */
uint8_t bUnitID; /*!< constant uniquely identifying the unit within the audio function */
uint8_t bSourceID; /*!< ID of the unit or terminal */
uint8_t bControlSize; /*!< size in bytes of an element of the bmaControls() array */
uint8_t bmaControls0; /*!< a bit set to 1 indicates that the mentioned control is supported for master channel 0 */
uint8_t bmaControls1; /*!< a bit set to 1 indicates that the mentioned control is supported for logical channel 1 */
uint8_t iFeature; /*!< index of a string descriptor */
} usb_desc_mono_feature_unit;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< feature unit descriptor subtype */
uint8_t bUnitID; /*!< constant uniquely identifying the unit within the audio function */
uint8_t bSourceID; /*!< ID of the unit or terminal */
uint8_t bControlSize; /*!< size in bytes of an element of the bmaControls() array */
uint16_t bmaControls0; /*!< a bit set to 1 indicates that the mentioned control is supported for master channel 0 */
uint16_t bmaControls1; /*!< a bit set to 1 indicates that the mentioned control is supported for logical channel 1 */
uint16_t bmaControls2; /*!< a bit set to 1 indicates that the mentioned control is supported for logical channel 2 */
uint8_t iFeature; /*!< index of a string descriptor */
} usb_desc_stereo_feature_unit;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< format type descriptor subtype */
uint8_t bFormatType; /*!< constant identifying the format type */
uint8_t bNrChannels; /*!< indicates the number of physical channels in the audio data stream */
uint8_t bSubFrameSize; /*!< the number of bytes occupied by one audio subframe */
uint8_t bBitResolution; /*!< the number of effectively used bits from the available bits in an audio subframe */
uint8_t bSamFreqType; /*!< indicates how the sampling frequency can be programmed */
uint8_t bSamFreq[3]; /*!< sampling frequency ns in Hz for this isochronous data endpoint */
} usb_desc_format_type;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bEndpointAddress; /*!< the address of the endpoint */
uint8_t bmAttributes; /*!< transfer type and synchronization type */
uint16_t wMaxPacketSize; /*!< maximum packet size this endpoint is capable of sending or receiving */
uint8_t bInterval; /*!< left to the designer's discretion */
uint8_t bRefresh; /*!< reset to 0 */
uint8_t bSynchAddress; /*!< reset to 0 */
} usb_desc_std_ep;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< general endpoint descriptor subtype */
uint8_t bmAttributes; /*!< transfer type and synchronization type */
uint8_t bLockDelayUnits; /*!< indicates the units used for the lock delay field */
uint16_t wLockDelay; /*!< indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry */
} usb_desc_AS_ep;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bEndpointAddress; /*!< general endpoint descriptor subtype */
uint8_t bmAttributes; /*!< transfer type and synchronization type */
uint16_t wMaxPacketSize; /*!< maximum packet size this endpoint is capable of sending or receiving */
uint8_t bInterval; /*!< polling interval in milliseconds for the endpoint if it is an interrupt or isochronous type */
uint8_t Refresh; /*!< refresh 1~9, power of 2 */
uint8_t bSynchAddress; /*!< synchronous address */
} usb_desc_FeedBack_ep;
#pragma pack()
/* USB configuration descriptor structure */
typedef struct {
usb_desc_config config; /*!< configuration descriptor */
usb_desc_itf std_itf; /*!< interface descriptor */
usb_desc_AC_itf ac_itf; /*!< audio controller interface descriptor */
#ifdef USE_USB_AD_MICPHONE
usb_desc_input_terminal mic_in_terminal; /*!< microphone input terminal descriptor */
usb_desc_mono_feature_unit mic_feature_unit; /*!< microphone feature unit descriptor */
usb_desc_output_terminal mic_out_terminal; /*!< microphone output terminal descriptor */
#endif
#ifdef USE_USB_AD_SPEAKER
usb_desc_input_terminal speak_in_terminal; /*!< speaker input terminal descriptor */
usb_desc_mono_feature_unit speak_feature_unit; /*!< speaker feature unit descriptor */
usb_desc_output_terminal speak_out_terminal; /*!< speaker output terminal descriptor */
#endif /* USE_USB_AD_SPEAKER */
#ifdef USE_USB_AD_MICPHONE
usb_desc_itf mic_std_as_itf_zeroband; /*!< microphone zeroband configuration standard audio streaming interface descriptor */
usb_desc_itf mic_std_as_itf_opera; /*!< microphone standard audio streaming interface descriptor */
usb_desc_AS_itf mic_as_itf; /*!< microphone audio correlation descriptor */
usb_desc_format_type mic_format_typeI; /*!< microphone typeI format type descriptor */
usb_desc_std_ep mic_std_endpoint; /*!< microphone standard endpoint descriptor */
usb_desc_AS_ep mic_as_endpoint; /*!< microphone audio dependent isochronous data endpoint descriptor */
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
usb_desc_itf speak_std_as_itf_zeroband; /*!< speaker zeroband configuration standard audio streaming interface descriptor */
usb_desc_itf speak_std_as_itf_opera; /*!< speaker standard audio streaming interface descriptor */
usb_desc_AS_itf speak_as_itf; /*!< speaker audio correlation descriptor */
usb_desc_format_type speak_format_typeI; /*!< speaker typeI format type descriptor */
usb_desc_std_ep speak_std_endpoint; /*!< speaker standard endpoint descriptor */
usb_desc_AS_ep speak_as_endpoint; /*!< speaker audio dependent isochronous data endpoint descriptor */
usb_desc_FeedBack_ep speak_feedback_endpoint; /*!< speaker feedback endpoint descriptor */
#endif /* USE_USB_AD_SPEAKER */
} usb_desc_config_set;
typedef struct {
/* main buffer for audio data OUT transfers and its relative pointers */
uint8_t isoc_out_buff[TOTAL_OUT_BUF_SIZE]; /*!< audio isochronous OUT data buff */
uint8_t* isoc_out_wrptr; /*!< audio isochronous OUT data write pointer */
uint8_t* isoc_out_rdptr; /*!< audio isochronous OUT data read pointer */
uint16_t buf_free_size; /*!< audio data buff free size */
uint16_t dam_tx_len; /*!< audio amplifier transmit length */
__IO uint32_t actual_freq; /*!< audio actual frequency */
__IO uint8_t play_flag; /*!< audio play flag */
uint8_t feedback_freq[3]; /*!< audio feedback frequency */
uint32_t cur_sam_freq; /*!< audio current sampling frequency */
/* USB receive buffer */
uint8_t usb_rx_buffer[SPEAKER_OUT_MAX_PACKET];
/* main buffer for audio control requests transfers and its relative variables */
uint8_t audioctl[64]; /*!< audio control requests transfers buff */
uint8_t audioctl_unit; /*!< audio control requests unit */
uint32_t audioctl_len; /*!< audio control requests length */
} usbd_audio_handler;
extern usb_desc audio_desc;
extern usb_class_core usbd_audio_cb;
extern usbd_audio_handler audio_handler;
#endif /* AUDIO_CORE_H */
@@ -0,0 +1,48 @@
/*!
\file audio_out_itf.h
\brief audio OUT (playback) interface header file
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 AUDIO_OUT_ITF_H
#define AUDIO_OUT_ITF_H
#include "usbd_conf.h"
typedef struct {
uint8_t (*audio_init)(uint32_t audio_freq, uint32_t volume);
uint8_t (*audio_deinit)(void);
uint8_t (*audio_cmd)(uint8_t* pbuf, uint32_t size, uint8_t cmd);
} audio_fops_struct;
extern audio_fops_struct audio_out_fops;
#endif /* AUDIO_OUT_ITF_H */
@@ -0,0 +1,951 @@
/*!
\file audio_core.c
\brief USB audio device class core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 "audio_out_itf.h"
#include "audio_core.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x9574U
#define VOL_MIN 0U /* volume minimum value */
#define VOL_MAX 100U /* volume maximum value */
#define VOL_RES 1U /* volume resolution */
#define VOL_0dB 70U /* 0dB is in the middle of VOL_MIN and VOL_MAX */
#ifdef USE_USB_AD_MICPHONE
extern volatile uint32_t count_data;
extern const char wavetestdata[];
#define LENGTH_DATA (1747U * 32U)
#endif /* USE_USB_AD_MICPHONE */
__ALIGN_BEGIN usbd_audio_handler audio_handler __ALIGN_END;
/* local function prototypes ('static') */
static uint8_t audio_init(usb_dev *udev, uint8_t config_index);
static uint8_t audio_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t audio_req_handler(usb_dev *udev, usb_req *req);
static uint8_t audio_set_intf(usb_dev *udev, usb_req *req);
static uint8_t audio_ctlx_out(usb_dev *udev);
static uint8_t audio_data_in(usb_dev *udev, uint8_t ep_num);
static uint8_t audio_data_out(usb_dev *udev, uint8_t ep_num);
static uint8_t audio_sof(usb_dev *udev);
static uint8_t audio_iso_in_incomplete(usb_dev *udev);
static uint8_t audio_iso_out_incomplete(usb_dev *udev);
static uint32_t usbd_audio_spk_get_feedback(usb_dev *udev);
static void get_feedback_fs_rate(uint32_t rate, uint8_t *buf);
usb_class_core usbd_audio_cb = {
.init = audio_init,
.deinit = audio_deinit,
.req_proc = audio_req_handler,
.set_intf = audio_set_intf,
.ctlx_out = audio_ctlx_out,
.data_in = audio_data_in,
.data_out = audio_data_out,
.SOF = audio_sof,
.incomplete_isoc_in = audio_iso_in_incomplete,
.incomplete_isoc_out = audio_iso_out_incomplete
};
/* note:it should use the c99 standard when compiling the below codes */
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev audio_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 audio_config_set __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = AD_CONFIG_DESC_SET_LEN,
.bNumInterfaces = 0x01U + CONFIG_DESC_AS_ITF_COUNT,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0xC0U,
.bMaxPower = 0x32U
},
.std_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_CONTROL,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.ac_itf =
{
.header =
{
.bLength = sizeof(usb_desc_AC_itf),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = 0x01U,
.bcdADC = 0x0100U,
.wTotalLength = AC_ITF_TOTAL_LEN,
.bInCollection = CONFIG_DESC_AS_ITF_COUNT,
#ifdef USE_USB_AD_MICPHONE
.baInterfaceNr0 = 0x01U,
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
.baInterfaceNr1 = 0x02U
#endif /* USE_USB_AD_SPEAKER */
},
#ifdef USE_USB_AD_MICPHONE
.mic_in_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_input_terminal),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = 0x02U,
.bTerminalID = 0x01U,
.wTerminalType = 0x0201U,
.bAssocTerminal = 0x00U,
.bNrChannels = 0x02U,
.wChannelConfig = 0x0003U,
.iChannelNames = 0x00U,
.iTerminal = 0x00U
},
.mic_feature_unit =
{
.header =
{
.bLength = sizeof(usb_desc_mono_feature_unit),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_FEATURE_UNIT,
.bUnitID = AD_IN_STREAMING_CTRL,
.bSourceID = 0x01U,
.bControlSize = 0x01U,
.bmaControls0 = AD_CONTROL_MUTE | AD_CONTROL_VOLUME,
.bmaControls1 = 0x00U,
.iFeature = 0x00U
},
.mic_out_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_output_terminal),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_OUTPUT_TERMINAL,
.bTerminalID = 0x03U,
.wTerminalType = 0x0101U,
.bAssocTerminal = 0x00U,
.bSourceID = 0x02U,
.iTerminal = 0x00U
},
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
.speak_in_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_input_terminal),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_INPUT_TERMINAL,
.bTerminalID = 0x04U,
.wTerminalType = 0x0101U,
.bAssocTerminal = 0x00U,
.bNrChannels = 0x02U,
.wChannelConfig = 0x0003U,
.iChannelNames = 0x00U,
.iTerminal = 0x00U
},
.speak_feature_unit =
{
.header =
{
.bLength = sizeof(usb_desc_mono_feature_unit),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_FEATURE_UNIT,
.bUnitID = AD_OUT_STREAMING_CTRL,
.bSourceID = 0x04U,
.bControlSize = 0x01U,
.bmaControls0 = AD_CONTROL_MUTE | AD_CONTROL_VOLUME,
.bmaControls1 = 0x00U,
.iFeature = 0x00U
},
.speak_out_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_output_terminal),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_OUTPUT_TERMINAL,
.bTerminalID = 0x06U,
.wTerminalType = 0x0301U,
.bAssocTerminal = 0x00U,
.bSourceID = 0x05U,
.iTerminal = 0x00U
},
#endif /* USE_USB_AD_SPEAKER */
#ifdef USE_USB_AD_MICPHONE
.mic_std_as_itf_zeroband =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x01U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.mic_std_as_itf_opera =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x01U,
.bAlternateSetting = 0x01U,
.bNumEndpoints = 0x01U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.mic_as_itf =
{
.header =
{
.bLength = sizeof(usb_desc_AS_itf),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_STREAMING_GENERAL,
.bTerminalLink = 0x03U,
.bDelay = 0x01U,
.wFormatTag = 0x0001U
},
.mic_format_typeI =
{
.header =
{
.bLength = sizeof(usb_desc_format_type),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_STREAMING_FORMAT_TYPE,
.bFormatType = AD_FORMAT_TYPE_I,
.bNrChannels = MIC_IN_CHANNEL_NBR,
.bSubFrameSize = 0x02U,
.bBitResolution = MIC_IN_BIT_RESOLUTION,
.bSamFreqType = 0x01U,
.bSamFreq[0] = (uint8_t)USBD_MIC_FREQ,
.bSamFreq[1] = USBD_MIC_FREQ >> 8,
.bSamFreq[2] = USBD_MIC_FREQ >> 16
},
.mic_std_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_std_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = AD_IN_EP,
.bmAttributes = USB_ENDPOINT_TYPE_ISOCHRONOUS,
.wMaxPacketSize = MIC_IN_PACKET,
.bInterval = 0x01U,
.bRefresh = 0x00U,
.bSynchAddress = 0x00U
},
.mic_as_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_AS_ep),
.bDescriptorType = AD_DESCTYPE_ENDPOINT
},
.bDescriptorSubtype = AD_ENDPOINT_GENERAL,
.bmAttributes = 0x00U,
.bLockDelayUnits = 0x00U,
.wLockDelay = 0x0000U
},
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
.speak_std_as_itf_zeroband =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x02U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.speak_std_as_itf_opera =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x02U,
.bAlternateSetting = 0x01U,
.bNumEndpoints = 0x02U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.speak_as_itf =
{
.header =
{
.bLength = sizeof(usb_desc_AS_itf),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_STREAMING_GENERAL,
.bTerminalLink = 0x04U,
.bDelay = 0x01U,
.wFormatTag = 0x0001U
},
.speak_format_typeI =
{
.header =
{
.bLength = sizeof(usb_desc_format_type),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_STREAMING_FORMAT_TYPE,
.bFormatType = AD_FORMAT_TYPE_I,
.bNrChannels = SPEAKER_OUT_CHANNEL_NBR,
.bSubFrameSize = 0x02U,
.bBitResolution = SPEAKER_OUT_BIT_RESOLUTION,
.bSamFreqType = 0x01U,
.bSamFreq[0] = (uint8_t)USBD_SPEAKER_FREQ,
.bSamFreq[1] = USBD_SPEAKER_FREQ >> 8,
.bSamFreq[2] = USBD_SPEAKER_FREQ >> 16
},
.speak_std_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_std_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = AD_OUT_EP,
.bmAttributes = USB_EP_ATTR_ISO | USB_EP_ATTR_ASYNC,
.wMaxPacketSize = SPEAKER_OUT_PACKET,
.bInterval = 0x01U,
.bRefresh = 0x00U,
.bSynchAddress = AD_FEEDBACK_IN_EP
},
.speak_as_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_AS_ep),
.bDescriptorType = AD_DESCTYPE_ENDPOINT
},
.bDescriptorSubtype = AD_ENDPOINT_GENERAL,
.bmAttributes = 0x00U,
.bLockDelayUnits = 0x00U,
.wLockDelay = 0x0000U,
},
.speak_feedback_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_FeedBack_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = AD_FEEDBACK_IN_EP,
.bmAttributes = USB_EP_ATTR_ISO | USB_EP_ATTR_ASYNC | USB_EP_ATTR_FEEDBACK,
.wMaxPacketSize = FEEDBACK_IN_PACKET,
.bInterval = 0x01U,
.Refresh = FEEDBACK_IN_INTERVAL, /* refresh every 32(2^5) ms */
.bSynchAddress = 0x00U
},
#endif /* USE_USB_AD_SPEAKER */
};
/* USB language ID descriptor */
static __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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN const usb_desc_str product_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(14U),
.bDescriptorType = USB_DESCTYPE_STR
},
.unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'A', 'u', 'd', 'i', 'o'}
};
/* USBD serial string */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
/* USB string descriptor */
void *const usbd_audio_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 descriptor configure */
usb_desc audio_desc = {
.dev_desc = (uint8_t *)&audio_dev_desc,
.config_desc = (uint8_t *)&audio_config_set,
.strings = usbd_audio_strings
};
/*!
\brief initialize the AUDIO 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 audio_init(usb_dev *udev, uint8_t config_index)
{
memset((void *)&audio_handler, 0U, sizeof(usbd_audio_handler));
#ifdef USE_USB_AD_MICPHONE
{
usb_desc_std_ep std_ep = audio_config_set.mic_std_endpoint;
usb_desc_ep ep = {
.header = std_ep.header,
.bEndpointAddress = std_ep.bEndpointAddress,
.bmAttributes = std_ep.bmAttributes,
.wMaxPacketSize = std_ep.wMaxPacketSize,
.bInterval = std_ep.bInterval
};
/* initialize TX endpoint */
usbd_ep_setup(udev, &ep);
}
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
{
audio_handler.isoc_out_rdptr = audio_handler.isoc_out_buff;
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
usb_desc_std_ep std_ep = audio_config_set.speak_std_endpoint;
usb_desc_ep ep1 = {
.header = std_ep.header,
.bEndpointAddress = std_ep.bEndpointAddress,
.bmAttributes = std_ep.bmAttributes,
.wMaxPacketSize = SPEAKER_OUT_MAX_PACKET,
.bInterval = std_ep.bInterval
};
/* initialize RX endpoint */
usbd_ep_setup(udev, &ep1);
/* prepare OUT endpoint to receive next audio packet */
usbd_ep_recev(udev, AD_OUT_EP, audio_handler.usb_rx_buffer, SPEAKER_OUT_MAX_PACKET);
/* initialize the audio output hardware layer */
if(USBD_OK != audio_out_fops.audio_init(USBD_SPEAKER_FREQ, DEFAULT_VOLUME)) {
return USBD_FAIL;
}
usb_desc_FeedBack_ep feedback_ep = audio_config_set.speak_feedback_endpoint;
usb_desc_ep ep2 = {
.header = feedback_ep.header,
.bEndpointAddress = feedback_ep.bEndpointAddress,
.bmAttributes = feedback_ep.bmAttributes,
.wMaxPacketSize = feedback_ep.wMaxPacketSize,
.bInterval = feedback_ep.bInterval
};
/* initialize TX endpoint */
usbd_ep_setup(udev, &ep2);
}
#endif /* USE_USB_AD_SPEAKER */
return USBD_OK;
}
/*!
\brief de-initialize the AUDIO 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 audio_deinit(usb_dev *udev, uint8_t config_index)
{
#ifdef USE_USB_AD_MICPHONE
/* deinitialize AUDIO endpoints */
usbd_ep_clear(udev, AD_IN_EP);
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
/* deinitialize AUDIO endpoints */
usbd_ep_clear(udev, AD_OUT_EP);
/* deinitialize the audio output hardware layer */
if(USBD_OK != audio_out_fops.audio_deinit()) {
return USBD_FAIL;
}
/* deinitialize AUDIO endpoints */
usbd_ep_clear(udev, AD_FEEDBACK_IN_EP);
#endif /* USE_USB_AD_SPEAKER */
return USBD_OK;
}
/*!
\brief handle the AUDIO class-specific 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 audio_req_handler(usb_dev *udev, usb_req *req)
{
uint8_t status = REQ_NOTSUPP;
usb_transc *transc_in = &udev->dev.transc_in[0];
usb_transc *transc_out = &udev->dev.transc_out[0];
switch(req->bRequest) {
case AD_REQ_GET_CUR:
transc_in->xfer_buf = audio_handler.audioctl;
transc_in->remain_len = req->wLength;
status = REQ_SUPP;
break;
case AD_REQ_SET_CUR:
if(req->wLength) {
transc_out->xfer_buf = audio_handler.audioctl;
transc_out->remain_len = req->wLength;
udev->dev.class_core->command = AD_REQ_SET_CUR;
audio_handler.audioctl_len = req->wLength;
audio_handler.audioctl_unit = BYTE_HIGH(req->wIndex);
status = REQ_SUPP;
}
break;
case AD_REQ_GET_MIN:
*((uint16_t *)audio_handler.audioctl) = VOL_MIN;
transc_in->xfer_buf = audio_handler.audioctl;
transc_in->remain_len = req->wLength;
status = REQ_SUPP;
break;
case AD_REQ_GET_MAX:
*((uint16_t *)audio_handler.audioctl) = VOL_MAX;
transc_in->xfer_buf = audio_handler.audioctl;
transc_in->remain_len = req->wLength;
status = REQ_SUPP;
break;
case AD_REQ_GET_RES:
*((uint16_t *)audio_handler.audioctl) = VOL_RES;
transc_in->xfer_buf = audio_handler.audioctl;
transc_in->remain_len = req->wLength;
status = REQ_SUPP;
break;
default:
break;
}
return status;
}
/*!
\brief handle the AUDIO set interface 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 audio_set_intf(usb_dev *udev, usb_req *req)
{
udev->dev.class_core->alter_set = req->wValue;
if(0xFFU != req->wValue) {
if(0U != req->wValue) {
/* deinit audio handler */
memset((void *)&audio_handler, 0U, sizeof(usbd_audio_handler));
audio_handler.play_flag = 0U;
audio_handler.isoc_out_rdptr = audio_handler.isoc_out_buff;
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
/* feedback calculate sample frequency */
audio_handler.actual_freq = I2S_ACTUAL_SAM_FREQ(USBD_SPEAKER_FREQ);
get_feedback_fs_rate(audio_handler.actual_freq, audio_handler.feedback_freq);
/* send feedback data of estimated frequency */
usbd_ep_send(udev, AD_FEEDBACK_IN_EP, audio_handler.feedback_freq, FEEDBACK_IN_PACKET);
} else {
/* stop audio output */
audio_out_fops.audio_cmd(audio_handler.isoc_out_rdptr, SPEAKER_OUT_PACKET / 2U, AD_CMD_STOP);
audio_handler.play_flag = 0U;
audio_handler.isoc_out_rdptr = audio_handler.isoc_out_buff;
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
usbd_fifo_flush(udev, AD_IN_EP);
usbd_fifo_flush(udev, AD_FEEDBACK_IN_EP);
usbd_fifo_flush(udev, AD_OUT_EP);
}
}
return 0U;
}
/*!
\brief handles the control transfer OUT callback
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_ctlx_out(usb_dev *udev)
{
#ifdef USE_USB_AD_SPEAKER
/* handles audio control requests data */
/* check if an audio_control request has been issued */
if(AD_REQ_SET_CUR == udev->dev.class_core->command) {
/* in this driver, to simplify code, only SET_CUR request is managed */
/* check for which addressed unit the audio_control request has been issued */
if(AD_OUT_STREAMING_CTRL == audio_handler.audioctl_unit) {
/* in this driver, to simplify code, only one unit is manage */
/* reset the audioctl_cmd variable to prevent re-entering this function */
udev->dev.class_core->command = 0U;
audio_handler.audioctl_len = 0U;
}
}
#endif /* USE_USB_AD_SPEAKER */
return USBD_OK;
}
/*!
\brief handles the audio IN data stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_data_in(usb_dev *udev, uint8_t ep_num)
{
#ifdef USE_USB_AD_MICPHONE
if(EP_ID(AD_IN_EP) == ep_num) {
if(count_data < LENGTH_DATA) {
/* Prepare next buffer to be sent: dummy data */
usbd_ep_send(udev, AD_IN_EP, (uint8_t *)&wavetestdata[count_data], MIC_IN_PACKET);
count_data += MIC_IN_PACKET;
} else {
usbd_ep_send(udev, AD_IN_EP, (uint8_t *)wavetestdata, MIC_IN_PACKET);
count_data = MIC_IN_PACKET;
}
}
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
if(EP_ID(AD_FEEDBACK_IN_EP) == ep_num) {
/* calculate feedback actual freq */
audio_handler.actual_freq = usbd_audio_spk_get_feedback(udev);
get_feedback_fs_rate(audio_handler.actual_freq, audio_handler.feedback_freq);
usbd_ep_send(udev, AD_FEEDBACK_IN_EP, audio_handler.feedback_freq, FEEDBACK_IN_PACKET);
}
#endif /* USE_USB_AD_SPEAKER */
return USBD_OK;
}
/*!
\brief handles the audio OUT data stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_data_out(usb_dev *udev, uint8_t ep_num)
{
uint16_t usb_rx_length, tail_len;
/* get receive length */
usb_rx_length = ((usb_core_driver *)udev)->dev.transc_out[ep_num].xfer_count;
if(audio_handler.isoc_out_wrptr >= audio_handler.isoc_out_rdptr) {
audio_handler.buf_free_size = TOTAL_OUT_BUF_SIZE + audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
} else {
audio_handler.buf_free_size = audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
}
/* free buffer enough to save RX data */
if(audio_handler.buf_free_size > usb_rx_length) {
if(audio_handler.isoc_out_wrptr >= audio_handler.isoc_out_rdptr) {
tail_len = audio_handler.isoc_out_buff + TOTAL_OUT_BUF_SIZE - audio_handler.isoc_out_wrptr;
if(tail_len >= usb_rx_length) {
memcpy(audio_handler.isoc_out_wrptr, audio_handler.usb_rx_buffer, usb_rx_length);
/* increment the buffer pointer */
audio_handler.isoc_out_wrptr += usb_rx_length;
/* increment the Buffer pointer or roll it back when all buffers are full */
if(audio_handler.isoc_out_wrptr >= (audio_handler.isoc_out_buff + TOTAL_OUT_BUF_SIZE)) {
/* all buffers are full: roll back */
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
}
} else {
memcpy(audio_handler.isoc_out_wrptr, audio_handler.usb_rx_buffer, tail_len);
/* adjust write pointer */
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
memcpy(audio_handler.isoc_out_wrptr, &audio_handler.usb_rx_buffer[tail_len], usb_rx_length - tail_len);
/* adjust write pointer */
audio_handler.isoc_out_wrptr += usb_rx_length - tail_len;
}
} else {
memcpy(audio_handler.isoc_out_wrptr, audio_handler.usb_rx_buffer, usb_rx_length);
/* increment the buffer pointer */
audio_handler.isoc_out_wrptr += usb_rx_length;
}
}
/* toggle the frame index */
udev->dev.transc_out[ep_num].frame_num = (udev->dev.transc_out[ep_num].frame_num) ? 0U : 1U;
/* prepare OUT endpoint to receive next audio packet */
usbd_ep_recev(udev, AD_OUT_EP, audio_handler.usb_rx_buffer, SPEAKER_OUT_MAX_PACKET);
if(audio_handler.isoc_out_wrptr >= audio_handler.isoc_out_rdptr) {
audio_handler.buf_free_size = TOTAL_OUT_BUF_SIZE + audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
} else {
audio_handler.buf_free_size = audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
}
if((0U == audio_handler.play_flag) && (audio_handler.buf_free_size < TOTAL_OUT_BUF_SIZE / 2U)) {
/* enable start of streaming */
audio_handler.play_flag = 1U;
/* initialize the audio output hardware layer */
if(USBD_OK != audio_out_fops.audio_cmd(audio_handler.isoc_out_rdptr, SPEAKER_OUT_MAX_PACKET / 2U, AD_CMD_PLAY)) {
return USBD_FAIL;
}
audio_handler.dam_tx_len = SPEAKER_OUT_MAX_PACKET;
}
return USBD_OK;
}
/*!
\brief handles the SOF event (data buffer update and synchronization)
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_sof(usb_dev *udev)
{
return USBD_OK;
}
/*!
\brief handles the audio ISO IN Incomplete event
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_iso_in_incomplete(usb_dev *udev)
{
(void)usb_txfifo_flush(&udev->regs, EP_ID(AD_FEEDBACK_IN_EP));
audio_handler.actual_freq = usbd_audio_spk_get_feedback(udev);
get_feedback_fs_rate(audio_handler.actual_freq, audio_handler.feedback_freq);
/* send feedback data of estimated frequency */
usbd_ep_send(udev, AD_FEEDBACK_IN_EP, audio_handler.feedback_freq, FEEDBACK_IN_PACKET);
return USBD_OK;
}
/*!
\brief handles the audio ISO OUT Incomplete event
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_iso_out_incomplete(usb_dev *udev)
{
return USBD_OK;
}
/*!
\brief calculate feedback sample frequency
\param[in] udev: pointer to USB device instance
\param[out] none
\retval feedback frequency value
*/
static uint32_t usbd_audio_spk_get_feedback(usb_dev *udev)
{
static uint32_t fb_freq;
/* calculate buffer free size */
if(audio_handler.isoc_out_wrptr >= audio_handler.isoc_out_rdptr) {
audio_handler.buf_free_size = TOTAL_OUT_BUF_SIZE + audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
} else {
audio_handler.buf_free_size = audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
}
/* calculate feedback frequency */
if(audio_handler.buf_free_size <= (TOTAL_OUT_BUF_SIZE / 4U)) {
fb_freq = I2S_ACTUAL_SAM_FREQ(USBD_SPEAKER_FREQ) - FEEDBACK_FREQ_OFFSET;
} else if(audio_handler.buf_free_size >= (TOTAL_OUT_BUF_SIZE * 3U / 4U)) {
fb_freq = I2S_ACTUAL_SAM_FREQ(USBD_SPEAKER_FREQ) + FEEDBACK_FREQ_OFFSET;
} else {
fb_freq = I2S_ACTUAL_SAM_FREQ(USBD_SPEAKER_FREQ);
}
return fb_freq;
}
/*!
\brief get feedback value from rate in USB full speed
\param[in] rate: sample frequency
\param[in] buf: pointer to result buffer
\param[out] none
\retval none
*/
static void get_feedback_fs_rate(uint32_t rate, uint8_t *buf)
{
rate = ((rate / 1000U) << 14) | ((rate % 1000U) << 4);
buf[0] = rate;
buf[1] = rate >> 8;
buf[2] = rate >> 16;
}
@@ -0,0 +1,167 @@
/*!
\file audio_out_itf.c
\brief audio OUT (playback) interface functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 "audio_core.h"
#include "audio_out_itf.h"
/* local function prototypes ('static') */
static uint8_t init(uint32_t audio_freq, uint32_t volume);
static uint8_t deinit(void);
static uint8_t audio_cmd(uint8_t *pbuf, uint32_t size, uint8_t cmd);
/* local variable defines */
static uint8_t audio_state = AD_STATE_INACTIVE;
audio_fops_struct audio_out_fops = {
.audio_init = init,
.audio_deinit = deinit,
.audio_cmd = audio_cmd
};
/*!
\brief initialize and configures all required resources
\param[in] audio_freq: statrt_up audio frequency
\param[in] volume: start_up volume to be set
\param[out] none
\retval AD_OK if all operations succeed, otherwise, AD_FAIL.
*/
static uint8_t init(uint32_t audio_freq, uint32_t volume)
{
static uint32_t initialized = 0U;
/* check if the low layer has already been initialized */
if(0U == initialized) {
/* initialize GPIO */
codec_gpio_init();
/* initialize I2S */
codec_audio_interface_init(audio_freq);
/* initialize DMA */
codec_i2s_dma_init();
/* prevent reinitializing the interface again */
initialized = 1U;
}
/* update the audio state machine */
audio_state = AD_STATE_ACTIVE;
return AD_OK;
}
/*!
\brief free all resources used by low layer and stops audio-play function
\param[in] none
\param[out] none
\retval AD_OK if all operations succeed, otherwise, AD_FAIL.
*/
static uint8_t deinit(void)
{
/* update the audio state machine */
audio_state = AD_STATE_INACTIVE;
return AD_OK;
}
/*!
\brief play, stop, pause or resume current file
\param[in] pbuf: address from which file should be played
\param[in] size: size of the current buffer/file
\param[in] cmd: command to be executed, can be:
\arg AD_CMD_PLAY
\arg AD_CMD_PAUSE
\arg AD_CMD_RESUME
\arg AD_CMD_STOP
\param[out] none
\retval AD_OK if all operations succeed, otherwise, AD_FAIL.
*/
static uint8_t audio_cmd(uint8_t *pbuf, uint32_t size, uint8_t cmd)
{
/* check the current state */
if((AD_STATE_INACTIVE == audio_state) || (AD_STATE_ERROR == audio_state)) {
audio_state = AD_STATE_ERROR;
return AD_FAIL;
}
switch(cmd) {
/* process the play command */
case AD_CMD_PLAY:
/* if current state is active or stopped */
if((AD_STATE_ACTIVE == audio_state) || \
(AD_STATE_STOPPED == audio_state) || \
(AD_STATE_PLAYING == audio_state)) {
audio_play((uint32_t)pbuf, size);
audio_state = AD_STATE_PLAYING;
return AD_OK;
} else if(AD_STATE_PAUSED == audio_state) {
audio_pause_resume(AD_RESUME, (uint32_t)pbuf, (size / 2U));
audio_state = AD_STATE_PLAYING;
return AD_OK;
} else {
return AD_FAIL;
}
/* process the stop command */
case AD_CMD_STOP:
if(AD_STATE_PLAYING != audio_state) {
/* unsupported command */
return AD_FAIL;
} else {
audio_stop();
audio_state = AD_STATE_STOPPED;
return AD_OK;
}
/* process the pause command */
case AD_CMD_PAUSE:
if(AD_STATE_PLAYING != audio_state) {
/* unsupported command */
return AD_FAIL;
} else {
audio_pause_resume(AD_PAUSE, (uint32_t)pbuf, (size / 2U));
audio_state = AD_STATE_PAUSED;
return AD_OK;
}
/* unsupported command */
default:
return AD_FAIL;
}
}
@@ -0,0 +1,65 @@
/*!
\file cdc_acm_core.h
\brief the header file of cdc acm driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 CDC_ACM_CORE_H
#define CDC_ACM_CORE_H
#include "usbd_enum.h"
#include "usb_cdc.h"
#define USB_CDC_RX_LEN USB_CDC_DATA_PACKET_SIZE /*< CDC data packet size */
typedef struct {
uint8_t data[USB_CDC_RX_LEN]; /*< CDC data transfer buff */
uint8_t cmd[USB_CDC_CMD_PACKET_SIZE]; /*< CDC cmd packet buff */
uint8_t packet_sent; /*< CDC data packet start send flag */
uint8_t packet_receive; /*< CDC data packet start receive flag */
uint32_t receive_length; /*< CDC data receive length */
acm_line line_coding; /*< CDC line coding structure */
} usb_cdc_handler;
extern usb_desc cdc_desc;
extern usb_class_core cdc_class;
/* function declarations */
/* check CDC ACM is ready for data transfer */
uint8_t cdc_acm_check_ready(usb_dev *udev);
/* send CDC ACM data */
void cdc_acm_data_send(usb_dev *udev);
/* receive CDC ACM data */
void cdc_acm_data_receive(usb_dev *udev);
#endif /* CDC_ACM_CORE_H */
@@ -0,0 +1,522 @@
/*!
\file cdc_acm_core.c
\brief CDC ACM driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 "cdc_acm_core.h"
#define USBD_VID 0x28E9U
#define USBD_PID 0x018AU
/* note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev cdc_dev_desc __ALIGN_END = {
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
.bcdUSB = 0x0200U,
.bDeviceClass = USB_CLASS_CDC,
.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_cdc_desc_config_set cdc_config_desc __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = USB_CDC_ACM_CONFIG_DESC_SIZE,
.bNumInterfaces = 0x02U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0x80U,
.bMaxPower = 0x32U
},
.cmd_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x01U,
.bInterfaceClass = USB_CLASS_CDC,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_PROTOCOL_AT,
.iInterface = 0x00U
},
.cdc_header =
{
.header =
{
.bLength = sizeof(usb_desc_header_func),
.bDescriptorType = USB_DESCTYPE_CS_INTERFACE
},
.bDescriptorSubtype = 0x00U,
.bcdCDC = 0x0110U
},
.cdc_call_managment =
{
.header =
{
.bLength = sizeof(usb_desc_call_managment_func),
.bDescriptorType = USB_DESCTYPE_CS_INTERFACE
},
.bDescriptorSubtype = 0x01U,
.bmCapabilities = 0x00U,
.bDataInterface = 0x01U
},
.cdc_acm =
{
.header =
{
.bLength = sizeof(usb_desc_acm_func),
.bDescriptorType = USB_DESCTYPE_CS_INTERFACE
},
.bDescriptorSubtype = 0x02U,
.bmCapabilities = 0x02U
},
.cdc_union =
{
.header =
{
.bLength = sizeof(usb_desc_union_func),
.bDescriptorType = USB_DESCTYPE_CS_INTERFACE
},
.bDescriptorSubtype = 0x06U,
.bMasterInterface = 0x00U,
.bSlaveInterface0 = 0x01U
},
.cdc_cmd_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = CDC_CMD_EP,
.bmAttributes = USB_EP_ATTR_INT,
.wMaxPacketSize = USB_CDC_CMD_PACKET_SIZE,
.bInterval = 0x0AU
},
.cdc_data_interface =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x01U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x02U,
.bInterfaceClass = USB_CLASS_DATA,
.bInterfaceSubClass = 0x00U,
.bInterfaceProtocol = USB_CDC_PROTOCOL_NONE,
.iInterface = 0x00U
},
.cdc_out_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = CDC_DATA_OUT_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = USB_CDC_DATA_PACKET_SIZE,
.bInterval = 0x00U
},
.cdc_in_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = CDC_DATA_IN_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = USB_CDC_DATA_PACKET_SIZE,
.bInterval = 0x00U
}
};
/* USB language ID Descriptor */
static __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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN const usb_desc_str product_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR
},
.unicode_string = {'G', 'D', '3', '2', '-', 'C', 'D', 'C', '_', 'A', 'C', 'M'}
};
/* USBD serial string */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
/* USB string descriptor set */
void *const usbd_cdc_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 cdc_desc = {
.dev_desc = (uint8_t *)&cdc_dev_desc,
.config_desc = (uint8_t *)&cdc_config_desc,
.strings = usbd_cdc_strings
};
/* local function prototypes ('static') */
static uint8_t cdc_acm_init(usb_dev *udev, uint8_t config_index);
static uint8_t cdc_acm_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t cdc_acm_req(usb_dev *udev, usb_req *req);
static uint8_t cdc_acm_ctlx_out(usb_dev *udev);
static uint8_t cdc_acm_in(usb_dev *udev, uint8_t ep_num);
static uint8_t cdc_acm_out(usb_dev *udev, uint8_t ep_num);
/* USB CDC device class callbacks structure */
usb_class_core cdc_class = {
.command = NO_CMD,
.alter_set = 0U,
.init = cdc_acm_init,
.deinit = cdc_acm_deinit,
.req_proc = cdc_acm_req,
.ctlx_out = cdc_acm_ctlx_out,
.data_in = cdc_acm_in,
.data_out = cdc_acm_out
};
/*!
\brief check CDC ACM is ready for data transfer
\param[in] udev: pointer to USB device instance
\param[out] none
\retval 0 if CDC is ready, else 1
*/
uint8_t cdc_acm_check_ready(usb_dev *udev)
{
if(NULL != udev->dev.class_data[CDC_COM_INTERFACE]) {
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
if((1U == cdc->packet_receive) && (1U == cdc->packet_sent)) {
return 0U;
}
}
return 1U;
}
/*!
\brief send CDC ACM data
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void cdc_acm_data_send(usb_dev *udev)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
if(0U != cdc->receive_length) {
cdc->packet_sent = 0U;
usbd_ep_send(udev, CDC_DATA_IN_EP, (uint8_t *)(cdc->data), cdc->receive_length);
cdc->receive_length = 0U;
}
}
/*!
\brief receive CDC ACM data
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void cdc_acm_data_receive(usb_dev *udev)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
cdc->packet_receive = 0U;
cdc->packet_sent = 0U;
usbd_ep_recev(udev, CDC_DATA_OUT_EP, (uint8_t *)(cdc->data), USB_CDC_DATA_PACKET_SIZE);
}
/*!
\brief initialize the CDC ACM 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 cdc_acm_init(usb_dev *udev, uint8_t config_index)
{
(void) config_index;
static __ALIGN_BEGIN usb_cdc_handler cdc_handler __ALIGN_END;
/* initialize the data TX endpoint */
usbd_ep_setup(udev, &(cdc_config_desc.cdc_in_endpoint));
/* initialize the data RX endpoint */
usbd_ep_setup(udev, &(cdc_config_desc.cdc_out_endpoint));
/* initialize the command TX endpoint */
usbd_ep_setup(udev, &(cdc_config_desc.cdc_cmd_endpoint));
/* initialize CDC handler structure */
cdc_handler.packet_receive = 1U;
cdc_handler.packet_sent = 1U;
cdc_handler.receive_length = 0U;
cdc_handler.line_coding = (acm_line) {
.dwDTERate = 115200U,
.bCharFormat = 0U,
.bParityType = 0U,
.bDataBits = 0x08U
};
udev->dev.class_data[CDC_COM_INTERFACE] = (void *)&cdc_handler;
return USBD_OK;
}
/*!
\brief deinitialize the CDC ACM 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 cdc_acm_deinit(usb_dev *udev, uint8_t config_index)
{
(void) config_index;
/* deinitialize the data TX/RX endpoint */
usbd_ep_clear(udev, CDC_DATA_IN_EP);
usbd_ep_clear(udev, CDC_DATA_OUT_EP);
/* deinitialize the command TX endpoint */
usbd_ep_clear(udev, CDC_CMD_EP);
return USBD_OK;
}
/*!
\brief handle the CDC ACM class-specific 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 cdc_acm_req(usb_dev *udev, usb_req *req)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
usb_transc *transc = NULL;
switch(req->bRequest) {
case SEND_ENCAPSULATED_COMMAND:
/* no operation for this driver */
break;
case GET_ENCAPSULATED_RESPONSE:
/* no operation for this driver */
break;
case SET_COMM_FEATURE:
/* no operation for this driver */
break;
case GET_COMM_FEATURE:
/* no operation for this driver */
break;
case CLEAR_COMM_FEATURE:
/* no operation for this driver */
break;
case SET_LINE_CODING:
transc = &udev->dev.transc_out[0];
/* set the value of the current command to be processed */
udev->dev.class_core->alter_set = req->bRequest;
/* enable EP0 prepare to receive command data packet */
transc->remain_len = req->wLength;
transc->xfer_buf = cdc->cmd;
break;
case GET_LINE_CODING:
transc = &udev->dev.transc_in[0];
cdc->cmd[0] = (uint8_t)(cdc->line_coding.dwDTERate);
cdc->cmd[1] = (uint8_t)(cdc->line_coding.dwDTERate >> 8);
cdc->cmd[2] = (uint8_t)(cdc->line_coding.dwDTERate >> 16);
cdc->cmd[3] = (uint8_t)(cdc->line_coding.dwDTERate >> 24);
cdc->cmd[4] = cdc->line_coding.bCharFormat;
cdc->cmd[5] = cdc->line_coding.bParityType;
cdc->cmd[6] = cdc->line_coding.bDataBits;
transc->xfer_buf = cdc->cmd;
transc->remain_len = 7U;
break;
case SET_CONTROL_LINE_STATE:
/* no operation for this driver */
break;
case SEND_BREAK:
/* no operation for this driver */
break;
default:
break;
}
return USBD_OK;
}
/*!
\brief command data received on control endpoint
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_acm_ctlx_out(usb_dev *udev)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
if(NO_CMD != udev->dev.class_core->alter_set) {
/* process the command data */
cdc->line_coding.dwDTERate = (uint32_t)((uint32_t)cdc->cmd[0] | \
((uint32_t)cdc->cmd[1] << 8) | \
((uint32_t)cdc->cmd[2] << 16) | \
((uint32_t)cdc->cmd[3] << 24));
cdc->line_coding.bCharFormat = cdc->cmd[4];
cdc->line_coding.bParityType = cdc->cmd[5];
cdc->line_coding.bDataBits = cdc->cmd[6];
udev->dev.class_core->alter_set = NO_CMD;
}
return USBD_OK;
}
/*!
\brief handle CDC ACM data IN stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_acm_in(usb_dev *udev, uint8_t ep_num)
{
usb_transc *transc = &udev->dev.transc_in[EP_ID(ep_num)];
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
if((0U == transc->xfer_len % transc->max_len) && (0U != transc->xfer_len)) {
usbd_ep_send(udev, ep_num, NULL, 0U);
} else {
cdc->packet_sent = 1U;
}
return USBD_OK;
}
/*!
\brief handle CDC ACM data OUT stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval USB device operation status
*/
static uint8_t cdc_acm_out(usb_dev *udev, uint8_t ep_num)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->dev.class_data[CDC_COM_INTERFACE];
cdc->packet_receive = 1U;
cdc->receive_length = ((usb_core_driver *)udev)->dev.transc_out[ep_num].xfer_count;
return USBD_OK;
}
@@ -0,0 +1,173 @@
/*!
\file dfu_core.h
\brief the header file of USB DFU device class core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 DFU_CORE_H
#define DFU_CORE_H
#include "usbd_enum.h"
/* DFU class code */
#define USB_DFU_CLASS 0xFEU
/* DFU subclass code */
#define USB_DFU_SUBCLASS_UPGRADE 0x01U
/* DFU protocol code */
#define USB_DFU_PROTOCL_RUNTIME 0x01U /*!< runtime interface protocol */
#define USB_DFU_PROTOCL_DFU 0x02U /*!< DFU interface protocol */
/* manifestation state */
#define MANIFEST_COMPLETE 0x00U /*!< manifest complete state */
#define MANIFEST_IN_PROGRESS 0x01U /*!< manifest in progress state */
/* DFU attributes code */
#define USB_DFU_CAN_DOWNLOAD 0x01U /*!< download attribute */
#define USB_DFU_CAN_UPLOAD 0x02U /*!< upload attribute */
#define USB_DFU_MANIFEST_TOLERANT 0x04U /*!< manifest tolerant attribute */
#define USB_DFU_WILL_DETACH 0x08U /*!< detach attribute */
/* special commands with download request */
#define GET_COMMANDS 0x00U /*!< get command request */
#define SET_ADDRESS_POINTER 0x21U /*!< set address pointer request */
#define ERASE 0x41U /*!< erase request */
/* memory operation command */
#define CMD_ERASE 0U /*!< erase command */
#define CMD_WRITE 1U /*!< write command */
#define _BYTE1(x) (uint8_t)((x) & 0xFFU) /*!< addressing cycle 1st byte */
#define _BYTE2(x) (uint8_t)(((x) & 0xFF00U) >> 8) /*!< addressing cycle 2nd byte */
#define _BYTE3(x) (uint8_t)(((x) & 0xFF0000U) >> 16) /*!< addressing cycle 3rd byte */
#define FLASH_ERASE_TIMEOUT 60U /*!< erase flash timeout */
#define FLASH_WRITE_TIMEOUT 80U /*!< write flash timeout */
/* bit detach capable = bit 3 in bmAttributes field */
#define DFU_DETACH_MASK (uint8_t)(0x10U)
/* DFU descriptor type code */
#define DFU_DESC_TYPE 0x21U
/* DFU device state defines */
typedef enum {
STATE_APP_IDLE = 0x00U, /*!< DFU APP idle state */
STATE_APP_DETACH, /*!< DFU APP detach state */
STATE_DFU_IDLE, /*!< idle state */
STATE_DFU_DNLOAD_SYNC, /*!< download synchronous state */
STATE_DFU_DNBUSY, /*!< download busy state */
STATE_DFU_DNLOAD_IDLE, /*!< download idle state */
STATE_DFU_MANIFEST_SYNC, /*!< manifest synchronous state */
STATE_DFU_MANIFEST, /*!< manifest state */
STATE_DFU_MANIFEST_WAIT_RESET, /*!< manifest wait reset state */
STATE_DFU_UPLOAD_IDLE, /*!< upload idle state */
STATE_DFU_ERROR /*!< DFU error state */
} dfu_state;
/* DFU device status defines */
typedef enum {
STATUS_OK = 0x00U, /*!< no error status */
STATUS_ERR_TARGET, /*!< trigger error status */
STATUS_ERR_FILE, /*!< file error status */
STATUS_ERR_WRITE, /*!< write error status */
STATUS_ERR_ERASE, /*!< erase error status */
STATUS_ERR_CHECK_ERASED, /*!< check erased error status */
STATUS_ERR_PROG, /*!< program error status */
STATUS_ERR_VERIFY, /*!< verify error status */
STATUS_ERR_ADDRESS, /*!< address error status */
STATUS_ERR_NOTDONE, /*!< not done error status */
STATUS_ERR_FIRMWARE, /*!< firmware error status */
STATUS_ERR_VENDOR, /*!< vendor error status */
STATUS_ERR_USBR, /*!< USB reset error status */
STATUS_ERR_POR, /*!< power error status */
STATUS_ERR_UNKNOWN, /*!< unknown error status */
STATUS_ERR_STALLEDPKT /*!< stalled pocket error status */
} dfu_status;
/* DFU class-specific requests */
typedef enum {
DFU_DETACH = 0U, /*!< detach request */
DFU_DNLOAD, /*!< download request */
DFU_UPLOAD, /*!< upload request */
DFU_GETSTATUS, /*!< get status request */
DFU_CLRSTATUS, /*!< clear status request */
DFU_GETSTATE, /*!< get state request */
DFU_ABORT, /*!< abort request */
DFU_REQ_MAX /*!< maximum request */
} dfu_requests;
#pragma pack(1)
/* USB DFU function descriptor structure */
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bmAttributes; /*!< DFU attributes */
uint16_t wDetachTimeOut; /*!< time, in milliseconds, that the device will wait after receipt of the DFU_DETACH request. */
uint16_t wTransferSize; /*!< maximum number of bytes that the device can accept per control-write transaction */
uint16_t bcdDFUVersion; /*!< numeric expression identifying the version of the DFU Specification release. */
} usb_desc_dfu_func;
#pragma pack()
/* USB configuration descriptor structure */
typedef struct {
usb_desc_config config; /*!< configuration descriptor */
usb_desc_itf dfu_itf0; /*!< DFU interface 0 descriptor */
usb_desc_itf dfu_itf1; /*!< DFU interface 1 descriptor */
usb_desc_itf dfu_itf2; /*!< DFU interface 2 descriptor */
usb_desc_dfu_func dfu_func; /*!< DFU function descriptor */
} usb_dfu_desc_config_set;
/* USB DFU handler structure */
typedef struct {
uint8_t bStatus; /*!< DFU device current status */
uint8_t bwPollTimeout0; /*!< DFU device polling timeout 0 */
uint8_t bwPollTimeout1; /*!< DFU device polling timeout 1 */
uint8_t bwPollTimeout2; /*!< DFU device polling timeout 2 */
uint8_t bState; /*!< DFU device current state */
uint8_t iString; /*!< DFU device string */
uint8_t manifest_state; /*!< DFU device current manifest state */
uint32_t data_len; /*!< DFU device data transfer length */
uint16_t block_num; /*!< memory block number */
uint32_t base_addr; /*!< memory base address */
uint8_t buf[TRANSFER_SIZE]; /*!< data transfer buff */
} usbd_dfu_handler;
typedef void (*app_func)(void);
extern usb_desc dfu_desc;
extern usb_class_core dfu_class;
#endif /* DFU_CORE_H */
@@ -0,0 +1,82 @@
/*!
\file dfu_mem.h
\brief USB DFU device media access layer header file
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 DFU_MEM_H
#define DFU_MEM_H
#include "usb_conf.h"
#define _1ST_BYTE(x) (uint8_t)((x) & 0xFFU) /*!< addressing cycle 1st byte */
#define _2ND_BYTE(x) (uint8_t)(((x) & 0xFF00U) >> 8) /*!< addressing cycle 2nd byte */
#define _3RD_BYTE(x) (uint8_t)(((x) & 0xFF0000U) >> 16) /*!< addressing cycle 3rd byte */
#define POLLING_TIMEOUT_SET(x) buffer[0] = _1ST_BYTE(x); \
buffer[1] = _2ND_BYTE(x); \
buffer[2] = _3RD_BYTE(x);
/* USB DFU property structure */
typedef struct _dfu_mem_prop {
const uint8_t *pstr_desc; /*!< pointer to the string descriptor */
uint8_t (*mem_init)(void);
uint8_t (*mem_deinit)(void);
uint8_t (*mem_erase)(uint32_t addr);
uint8_t (*mem_write)(uint8_t *buf, uint32_t addr, uint32_t len);
uint8_t *(*mem_read)(uint8_t *buf, uint32_t addr, uint32_t len);
uint8_t (*mem_checkaddr)(uint32_t addr);
const uint32_t erase_timeout; /*!< erase memory timeout */
const uint32_t write_timeout; /*!< write memory timeout */
} dfu_mem_prop;
typedef enum {
MEM_OK = 0U, /*!< memory operation succeed */
MEM_FAIL /*!< memory operation fail */
} mem_status;
/* function declarations */
/* initialize the memory media */
uint8_t dfu_mem_init(void);
/* deinitialize the memory media */
uint8_t dfu_mem_deinit(void);
/* erase a memory sector */
uint8_t dfu_mem_erase(uint32_t addr);
/* write data to sectors of memory */
uint8_t dfu_mem_write(uint8_t *buf, uint32_t addr, uint32_t len);
/* read data from sectors of memory */
uint8_t *dfu_mem_read(uint8_t *buf, uint32_t addr, uint32_t len);
/* get the status of a given memory and store in buffer */
uint8_t dfu_mem_getstatus(uint32_t addr, uint8_t cmd, uint8_t *buffer);
#endif /* DFU_MEM_H */
@@ -0,0 +1,721 @@
/*!
\file dfu_core.c
\brief USB DFU device class core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 "dfu_core.h"
#include "dfu_mem.h"
#include "drv_usb_hw.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x0189U
/* local function prototypes ('static') */
static uint8_t dfu_init(usb_dev *udev, uint8_t config_index);
static uint8_t dfu_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t dfu_req_handler(usb_dev *udev, usb_req *req);
static uint8_t dfu_ctlx_in(usb_dev *udev);
static void dfu_mode_leave(usb_dev *udev);
static uint8_t dfu_getstatus_complete(usb_dev *udev);
/* DFU requests management functions */
static void dfu_detach(usb_dev *udev, usb_req *req);
static void dfu_dnload(usb_dev *udev, usb_req *req);
static void dfu_upload(usb_dev *udev, usb_req *req);
static void dfu_getstatus(usb_dev *udev, usb_req *req);
static void dfu_clrstatus(usb_dev *udev, usb_req *req);
static void dfu_getstate(usb_dev *udev, usb_req *req);
static void dfu_abort(usb_dev *udev, usb_req *req);
static void string_to_unicode(uint8_t *str, uint16_t *pbuf);
static void (*dfu_request_process[])(usb_dev *udev, usb_req *req) = {
[DFU_DETACH] = dfu_detach,
[DFU_DNLOAD] = dfu_dnload,
[DFU_UPLOAD] = dfu_upload,
[DFU_GETSTATUS] = dfu_getstatus,
[DFU_CLRSTATUS] = dfu_clrstatus,
[DFU_GETSTATE] = dfu_getstate,
[DFU_ABORT] = dfu_abort
};
/* note:it should use the c99 standard when compiling the below codes */
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev dfu_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_dfu_desc_config_set dfu_config_desc __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = sizeof(usb_dfu_desc_config_set),
.bNumInterfaces = 0x01U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0x80U,
.bMaxPower = 0x32U
},
.dfu_itf0 =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_DFU_CLASS,
.bInterfaceSubClass = USB_DFU_SUBCLASS_UPGRADE,
.bInterfaceProtocol = USB_DFU_PROTOCL_DFU,
.iInterface = STR_IDX_ALT_ITF0
},
.dfu_itf1 =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x01U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_DFU_CLASS,
.bInterfaceSubClass = USB_DFU_SUBCLASS_UPGRADE,
.bInterfaceProtocol = USB_DFU_PROTOCL_DFU,
.iInterface = STR_IDX_ALT_ITF1
},
.dfu_itf2 =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x02U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_DFU_CLASS,
.bInterfaceSubClass = USB_DFU_SUBCLASS_UPGRADE,
.bInterfaceProtocol = USB_DFU_PROTOCL_DFU,
.iInterface = STR_IDX_ALT_ITF2
},
.dfu_func =
{
.header =
{
.bLength = sizeof(usb_desc_dfu_func),
.bDescriptorType = DFU_DESC_TYPE
},
.bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_CAN_UPLOAD | USB_DFU_WILL_DETACH,
.wDetachTimeOut = 0x00FFU,
.wTransferSize = TRANSFER_SIZE,
.bcdDFUVersion = 0x0110U
}
};
/* USB language ID descriptor */
static __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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN 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', '_', 'D', 'F', 'U'}
};
/* USB serial string */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(2U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
/* USB configure string */
static __ALIGN_BEGIN const usb_desc_str config_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(15U),
.bDescriptorType = USB_DESCTYPE_STR
},
.unicode_string = {'G', 'D', '3', '2', ' ', 'U', 'S', 'B', ' ', 'C', 'O', 'N', 'F', 'I', 'G'}
};
/* alternate interface 0 string */
static __ALIGN_BEGIN usb_desc_str interface_string0 __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(2U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
/* alternate interface 1 string */
static __ALIGN_BEGIN usb_desc_str interface_string1 __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(2U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
/* alternate interface 2 string */
static __ALIGN_BEGIN usb_desc_str interface_string2 __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(2U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
void *const usbd_dfu_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,
[STR_IDX_CONFIG] = (uint8_t *)&config_string,
[STR_IDX_ALT_ITF0] = (uint8_t *)&interface_string0,
[STR_IDX_ALT_ITF1] = (uint8_t *)&interface_string1,
[STR_IDX_ALT_ITF2] = (uint8_t *)&interface_string2
};
usb_desc dfu_desc = {
.dev_desc = (uint8_t *)&dfu_dev_desc,
.config_desc = (uint8_t *)&dfu_config_desc,
.strings = usbd_dfu_strings
};
usb_class_core dfu_class = {
.init = dfu_init,
.deinit = dfu_deinit,
.req_proc = dfu_req_handler,
.ctlx_in = dfu_ctlx_in
};
/*!
\brief initialize the DFU 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 dfu_init(usb_dev *udev, uint8_t config_index)
{
static __ALIGN_BEGIN usbd_dfu_handler dfu_handler __ALIGN_END;
/* unlock the internal flash */
dfu_mem_init();
memset((void *)&dfu_handler, 0U, sizeof(usbd_dfu_handler));
dfu_handler.manifest_state = MANIFEST_COMPLETE;
dfu_handler.bState = STATE_DFU_IDLE;
dfu_handler.bStatus = STATUS_OK;
udev->dev.class_data[USBD_DFU_INTERFACE] = (void *)&dfu_handler;
/* create interface string */
string_to_unicode((uint8_t *)dfu_inter_flash_cb.pstr_desc, udev->dev.desc->strings[STR_IDX_ALT_ITF0]);
string_to_unicode((uint8_t *)dfu_nor_flash_cb.pstr_desc, udev->dev.desc->strings[STR_IDX_ALT_ITF1]);
string_to_unicode((uint8_t *)dfu_nand_flash_cb.pstr_desc, udev->dev.desc->strings[STR_IDX_ALT_ITF2]);
return USBD_OK;
}
/*!
\brief deinitialize the DFU 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 dfu_deinit(usb_dev *udev, uint8_t config_index)
{
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
/* restore device default state */
memset(udev->dev.class_data[USBD_DFU_INTERFACE], 0U, sizeof(usbd_dfu_handler));
dfu->bState = STATE_DFU_IDLE;
dfu->bStatus = STATUS_OK;
/* deinit the memory */
dfu_mem_deinit();
return USBD_OK;
}
/*!
\brief handle the DFU class-specific 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 dfu_req_handler(usb_dev *udev, usb_req *req)
{
if(req->bRequest < DFU_REQ_MAX) {
dfu_request_process[req->bRequest](udev, req);
} else {
return USBD_FAIL;
}
return USBD_OK;
}
/*!
\brief handle data stage
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t dfu_ctlx_in(usb_dev *udev)
{
dfu_getstatus_complete(udev);
return USBD_OK;
}
/*!
\brief leave DFU mode and reset device to jump to user loaded code
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void dfu_mode_leave(usb_dev *udev)
{
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
dfu->manifest_state = MANIFEST_COMPLETE;
if(dfu_config_desc.dfu_func.bmAttributes & 0x04U) {
dfu->bState = STATE_DFU_MANIFEST_SYNC;
} else {
dfu->bState = STATE_DFU_MANIFEST_WAIT_RESET;
/* deinit the memory */
dfu_mem_deinit();
/* generate system reset to allow jumping to the user code */
NVIC_SystemReset();
}
}
/*!
\brief handle data IN stage in control endpoint 0
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t dfu_getstatus_complete(usb_dev *udev)
{
uint32_t addr;
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
if(STATE_DFU_DNBUSY == dfu->bState) {
/* decode the special command */
if(0U == dfu->block_num) {
if(1U == dfu->data_len) {
if(GET_COMMANDS == dfu->buf[0]) {
/* no operation */
}
} else if(5U == dfu->data_len) {
if(SET_ADDRESS_POINTER == dfu->buf[0]) {
/* set flash operation address */
dfu->base_addr = *(uint32_t *)(dfu->buf + 1U);
} else if(ERASE == dfu->buf[0]) {
dfu->base_addr = *(uint32_t *)(dfu->buf + 1U);
dfu_mem_erase(dfu->base_addr);
} else {
/* no operation */
}
} else {
/* no operation */
}
} else if(dfu->block_num > 1U) { /* regular download command */
/* decode the required address */
addr = (dfu->block_num - 2U) * TRANSFER_SIZE + dfu->base_addr;
dfu_mem_write(dfu->buf, addr, dfu->data_len);
dfu->block_num = 0U;
} else {
/* no operation */
}
dfu->data_len = 0U;
/* update the device state and poll timeout */
dfu->bState = STATE_DFU_DNLOAD_SYNC;
return USBD_OK;
} else if(STATE_DFU_MANIFEST == dfu->bState) { /* manifestation in progress */
/* start leaving DFU mode */
dfu_mode_leave(udev);
} else {
/* no operation */
}
return USBD_OK;
}
/*!
\brief handle the DFU_DETACH request
\param[in] udev: pointer to USB device instance
\param[in] req: DFU class request
\param[out] none
\retval none.
*/
static void dfu_detach(usb_dev *udev, usb_req *req)
{
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
switch(dfu->bState) {
case STATE_DFU_IDLE:
case STATE_DFU_DNLOAD_SYNC:
case STATE_DFU_DNLOAD_IDLE:
case STATE_DFU_MANIFEST_SYNC:
case STATE_DFU_UPLOAD_IDLE:
dfu->bStatus = STATUS_OK;
dfu->bState = STATE_DFU_IDLE;
dfu->iString = 0U; /* iString */
dfu->block_num = 0U;
dfu->data_len = 0U;
break;
default:
break;
}
/* check the detach capability in the DFU functional descriptor */
if(dfu_config_desc.dfu_func.wDetachTimeOut & DFU_DETACH_MASK) {
usbd_disconnect(udev);
usbd_connect(udev);
} else {
/* wait for the period of time specified in detach request */
usb_mdelay(4U);
}
}
/*!
\brief handle the DFU_DNLOAD request
\param[in] udev: pointer to USB device instance
\param[in] req: DFU class request
\param[out] none
\retval none
*/
static void dfu_dnload(usb_dev *udev, usb_req *req)
{
usb_transc *transc = &udev->dev.transc_out[0];
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
switch(dfu->bState) {
case STATE_DFU_IDLE:
case STATE_DFU_DNLOAD_IDLE:
if(req->wLength > 0U) {
/* update the global length and block number */
dfu->block_num = req->wValue;
dfu->data_len = req->wLength;
dfu->bState = STATE_DFU_DNLOAD_SYNC;
transc->remain_len = dfu->data_len;
transc->xfer_buf = dfu->buf;
} else {
dfu->manifest_state = MANIFEST_IN_PROGRESS;
dfu->bState = STATE_DFU_MANIFEST_SYNC;
}
break;
default:
break;
}
}
/*!
\brief handles the DFU UPLOAD request.
\param[in] udev: pointer to USB device instance
\param[in] req: DFU class request
\param[out] none
\retval none
*/
static void dfu_upload(usb_dev *udev, usb_req *req)
{
uint8_t *phy_addr = NULL;
uint32_t addr = 0U;
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
usb_transc *transc = &udev->dev.transc_in[0];
if(req->wLength <= 0U) {
dfu->bState = STATE_DFU_IDLE;
return;
}
switch(dfu->bState) {
case STATE_DFU_IDLE:
case STATE_DFU_UPLOAD_IDLE:
/* update the global length and block number */
dfu->block_num = req->wValue;
dfu->data_len = req->wLength;
/* DFU get command */
if(0U == dfu->block_num) {
/* update the state machine */
dfu->bState = (dfu->data_len > 3U) ? STATE_DFU_IDLE : STATE_DFU_UPLOAD_IDLE;
/* store the values of all supported commands */
dfu->buf[0] = GET_COMMANDS;
dfu->buf[1] = SET_ADDRESS_POINTER;
dfu->buf[2] = ERASE;
/* send the status data over EP0 */
transc->xfer_buf = &(dfu->buf[0]);
transc->remain_len = 3U;
} else if(dfu->block_num > 1U) {
dfu->bState = STATE_DFU_UPLOAD_IDLE;
/* change is accelerated */
addr = (dfu->block_num - 2U) * TRANSFER_SIZE + dfu->base_addr;
/* return the physical address where data are stored */
phy_addr = dfu_mem_read(dfu->buf, addr, dfu->data_len);
/* send the status data over EP0 */
transc->xfer_buf = phy_addr;
transc->remain_len = dfu->data_len;
} else {
dfu->bState = STATUS_ERR_STALLEDPKT;
}
break;
default:
dfu->data_len = 0U;
dfu->block_num = 0U;
break;
}
}
/*!
\brief handle the DFU_GETSTATUS request
\param[in] udev: pointer to USB device instance
\param[in] req: DFU class request
\param[out] none
\retval none
*/
static void dfu_getstatus(usb_dev *udev, usb_req *req)
{
usb_transc *transc = &udev->dev.transc_in[0];
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
switch(dfu->bState) {
case STATE_DFU_DNLOAD_SYNC:
if(0U != dfu->data_len) {
dfu->bState = STATE_DFU_DNBUSY;
if(0U == dfu->block_num) {
if(ERASE == dfu->buf[0]) {
dfu_mem_getstatus(dfu->base_addr, CMD_ERASE, (uint8_t *)&dfu->bwPollTimeout0);
} else {
dfu_mem_getstatus(dfu->base_addr, CMD_WRITE, (uint8_t *)&dfu->bwPollTimeout0);
}
}
} else {
dfu->bState = STATE_DFU_DNLOAD_IDLE;
}
break;
case STATE_DFU_MANIFEST_SYNC:
if(MANIFEST_IN_PROGRESS == dfu->manifest_state) {
dfu->bState = STATE_DFU_MANIFEST;
dfu->bwPollTimeout0 = 1U;
} else if((MANIFEST_COMPLETE == dfu->manifest_state) && \
(dfu_config_desc.dfu_func.bmAttributes & 0x04U)) {
dfu->bState = STATE_DFU_IDLE;
dfu->bwPollTimeout0 = 0U;
} else {
/* no operation */
}
break;
default:
break;
}
/* send the status data of DFU interface to host over EP0 */
transc->xfer_buf = (uint8_t *)&(dfu->bStatus);
transc->remain_len = 6U;
}
/*!
\brief handle the DFU_CLRSTATUS request
\param[in] udev: pointer to USB device instance
\param[in] req: DFU class request
\param[out] none
\retval none
*/
static void dfu_clrstatus(usb_dev *udev, usb_req *req)
{
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
if(STATE_DFU_ERROR == dfu->bState) {
dfu->bStatus = STATUS_OK;
dfu->bState = STATE_DFU_IDLE;
} else {
/* state error */
dfu->bStatus = STATUS_ERR_UNKNOWN;
dfu->bState = STATE_DFU_ERROR;
}
dfu->iString = 0U; /* iString: index = 0 */
}
/*!
\brief handle the DFU_GETSTATE request
\param[in] udev: pointer to USB device instance
\param[in] req: DFU class request
\param[out] none
\retval none
*/
static void dfu_getstate(usb_dev *udev, usb_req *req)
{
usb_transc *transc = &udev->dev.transc_in[0];
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
/* send the current state of the DFU interface to host */
transc->xfer_buf = &(dfu->bState);
transc->remain_len = 1U;
}
/*!
\brief handle the DFU_ABORT request
\param[in] udev: pointer to USB device instance
\param[in] req: DFU class request
\param[out] none
\retval none
*/
static void dfu_abort(usb_dev *udev, usb_req *req)
{
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->dev.class_data[USBD_DFU_INTERFACE];
switch(dfu->bState) {
case STATE_DFU_IDLE:
case STATE_DFU_DNLOAD_SYNC:
case STATE_DFU_DNLOAD_IDLE:
case STATE_DFU_MANIFEST_SYNC:
case STATE_DFU_UPLOAD_IDLE:
dfu->bStatus = STATUS_OK;
dfu->bState = STATE_DFU_IDLE;
dfu->iString = 0U; /* iString: index = 0 */
dfu->block_num = 0U;
dfu->data_len = 0U;
break;
default:
break;
}
}
/*!
\brief convert string value into unicode char
\param[in] str: pointer to plain string
\param[in] pbuf: buffer pointer to store unicode char
\param[out] none
\retval none
*/
static void string_to_unicode(uint8_t *str, uint16_t *pbuf)
{
uint8_t index = 0U;
if(NULL != str) {
pbuf[index++] = ((strlen((const char *)str) * 2U + 2U) & 0x00FFU) | ((USB_DESCTYPE_STR << 8) & 0xFF00U);
while('\0' != *str) {
pbuf[index++] = *str++;
}
}
}
@@ -0,0 +1,241 @@
/*!
\file dfu_mem.c
\brief USB DFU device media access layer functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 "dfu_mem.h"
#include "usbd_transc.h"
extern usb_core_driver usb_dfu_dev;
extern struct {
uint8_t buf[TRANSFER_SIZE];
uint16_t data_len;
uint16_t block_num;
uint32_t base_addr;
} prog;
dfu_mem_prop *mem_tab[MAX_USED_MEMORY_MEDIA] = {
&dfu_inter_flash_cb,
&dfu_nor_flash_cb,
&dfu_nand_flash_cb,
};
/* The list of memory interface string descriptor pointers. This list
can be updated whenever a memory has to be added or removed */
const uint8_t *USBD_DFU_StringDesc[MAX_USED_MEMORY_MEDIA] = {
(const uint8_t *)INTER_FLASH_IF_STR,
(const uint8_t *)NOR_FLASH_IF_STR,
(const uint8_t *)NAND_FLASH_IF_STR
};
static uint8_t dfu_mem_checkaddr(uint32_t addr);
/*!
\brief initialize the memory media
\param[in] none
\param[out] none
\retval MEM_OK
*/
uint8_t dfu_mem_init(void)
{
uint32_t mem_index = 0U;
/* initialize all supported memory medias */
for(mem_index = 0U; mem_index < MAX_USED_MEMORY_MEDIA; mem_index++) {
/* check if the memory media exists */
if(NULL != mem_tab[mem_index]->mem_init) {
mem_tab[mem_index]->mem_init();
}
}
return MEM_OK;
}
/*!
\brief deinitialize the memory media
\param[in] none
\param[out] none
\retval MEM_OK
*/
uint8_t dfu_mem_deinit(void)
{
uint32_t mem_index = 0U;
/* deinitialize all supported memory medias */
for(mem_index = 0U; mem_index < MAX_USED_MEMORY_MEDIA; mem_index++) {
/* check if the memory media exists */
if(NULL != mem_tab[mem_index]->mem_deinit) {
mem_tab[mem_index]->mem_deinit();
}
}
return MEM_OK;
}
/*!
\brief erase a memory sector
\param[in] addr: memory sector address/code
\param[out] none
\retval MEM_OK
*/
uint8_t dfu_mem_erase(uint32_t addr)
{
uint32_t mem_index = dfu_mem_checkaddr(addr);
/* check if the address is in protected area */
if(IS_PROTECTED_AREA(addr)) {
return MEM_FAIL;
}
if(mem_index < MAX_USED_MEMORY_MEDIA) {
/* check if the operation is supported */
if(NULL != mem_tab[mem_index]->mem_erase) {
return mem_tab[mem_index]->mem_erase(addr);
} else {
return MEM_FAIL;
}
} else {
return MEM_FAIL;
}
}
/*!
\brief write data to sectors of memory
\param[in] buf: the data buffer to be write
\param[in] addr: memory sector address/code
\param[in] len: data length
\param[out] none
\retval MEM_OK
*/
uint8_t dfu_mem_write(uint8_t *buf, uint32_t addr, uint32_t len)
{
uint32_t mem_index = dfu_mem_checkaddr(addr);
/* check if the address is in protected area */
if(IS_PROTECTED_AREA(addr)) {
return MEM_FAIL;
}
if(OB_RDPT0 == (addr & MAL_MASK_OB)) {
option_byte_write(addr, buf);
NVIC_SystemReset();
return MEM_OK;
}
if(mem_index < MAX_USED_MEMORY_MEDIA) {
/* check if the operation is supported */
if(NULL != mem_tab[mem_index]->mem_write) {
return mem_tab[mem_index]->mem_write(buf, addr, len);
} else {
return MEM_FAIL;
}
} else {
return MEM_FAIL;
}
}
/*!
\brief read data from sectors of memory
\param[in] buf: the data buffer to be write
\param[in] addr: memory sector address/code
\param[in] len: data length
\param[out] none
\retval pointer to buffer
*/
uint8_t *dfu_mem_read(uint8_t *buf, uint32_t addr, uint32_t len)
{
uint32_t mem_index = 0U;
if((OB_RDPT0 != addr) && (OB_RDPT1 != addr)) {
mem_index = dfu_mem_checkaddr(addr);
}
if(mem_index < MAX_USED_MEMORY_MEDIA) {
/* check if the operation is supported */
if(NULL != mem_tab[mem_index]->mem_read) {
return mem_tab[mem_index]->mem_read(buf, addr, len);
} else {
return buf;
}
} else {
return buf;
}
}
/*!
\brief get the status of a given memory and store in buffer
\param[in] addr: memory sector address/code
\param[in] cmd: 0 for erase and 1 for write
\param[in] buffer: pointer to the buffer where the status data will be stored
\param[out] none
\retval MEM_OK if all operations are OK, MEM_FAIL else
*/
uint8_t dfu_mem_getstatus(uint32_t addr, uint8_t cmd, uint8_t *buffer)
{
uint32_t mem_index = dfu_mem_checkaddr(addr);
if(mem_index < MAX_USED_MEMORY_MEDIA) {
if(cmd & 0x01U) {
POLLING_TIMEOUT_SET(mem_tab[mem_index]->write_timeout);
} else {
POLLING_TIMEOUT_SET(mem_tab[mem_index]->erase_timeout);
}
return MEM_OK;
} else {
return MEM_FAIL;
}
}
/*!
\brief check the address is supported
\param[in] addr: memory sector address/code
\param[out] none
\retval index of the addressed memory
*/
static uint8_t dfu_mem_checkaddr(uint32_t addr)
{
uint8_t mem_index = 0U;
/* check with all supported memories */
for(mem_index = 0U; mem_index < MAX_USED_MEMORY_MEDIA; mem_index++) {
/* if the check address is supported, return the memory index */
if(MEM_OK == mem_tab[mem_index]->mem_checkaddr(addr)) {
return mem_index;
}
}
/* if there is no memory found, return MAX_USED_MEMORY_MEDIA */
return (MAX_USED_MEMORY_MEDIA);
}
@@ -0,0 +1,66 @@
/*!
\file custom_hid_core.h
\brief definitions for HID core
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 CUSTOM_HID_CORE_H
#define CUSTOM_HID_CORE_H
#include "usbd_enum.h"
#include "usb_hid.h"
#define DESC_LEN_REPORT 96U /*!< report descriptor length */
#define DESC_LEN_CONFIG 41U /*!< configuration descriptor length */
#define NO_CMD 0xFFU /*!< no command */
#define MAX_PERIPH_NUM 4U /*!< maximum peripheral number */
typedef struct {
uint8_t data[2]; /*!< custom HID data packet buff */
uint8_t reportID; /*!< custom HID report id */
uint8_t idlestate; /*!< idle state */
uint8_t protocol; /*!< HID protocol */
} custom_hid_handler;
typedef struct {
void (*periph_config[MAX_PERIPH_NUM])(void);
} hid_fop_handler;
extern usb_desc custom_hid_desc;
extern usb_class_core usbd_custom_hid_cb;
/* function declarations */
/* register HID interface operation functions */
uint8_t custom_hid_itfop_register(usb_dev *udev, hid_fop_handler *hid_fop);
/* send custom HID report */
uint8_t custom_hid_report_send(usb_dev *udev, uint8_t *report, uint32_t len);
#endif /* CUSTOM_HID_CORE_H */
@@ -0,0 +1,67 @@
/*!
\file standard_hid_core.h
\brief definitions for HID core
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 STANDARD_HID_CORE_H
#define STANDARD_HID_CORE_H
#include "usbd_enum.h"
#include "usb_hid.h"
#define USB_HID_CONFIG_DESC_LEN 0x22U /*!< HID device configuration descriptor length */
#define USB_HID_REPORT_DESC_LEN 0x2EU /*!< HID device report descriptor length */
#define NO_CMD 0xFFU /*!< no command */
typedef struct {
uint32_t protocol; /*!< control request protocol */
uint32_t idle_state; /*!< HID device idle state */
uint8_t data[HID_IN_PACKET]; /*!< data transfer buff */
__IO uint8_t prev_transfer_complete; /*!< previous transfer complete flag */
} standard_hid_handler;
typedef struct {
void (*hid_itf_config)(void);
void (*hid_itf_data_process)(usb_dev *udev);
} hid_fop_handler;
extern usb_desc hid_desc;
extern usb_class_core usbd_hid_cb;
/* function declarations */
/* register HID interface operation functions */
uint8_t hid_itfop_register(usb_dev *udev, hid_fop_handler *hid_fop);
/* send keyboard report */
uint8_t hid_report_send(usb_dev *udev, uint8_t *report, uint32_t len);
#endif /* STANDARD_HID_CORE_H */
@@ -0,0 +1,473 @@
/*!
\file custom_hid_core.c
\brief custom HID class driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 "custom_hid_core.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x028AU
/* Note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev custom_hid_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_hid_desc_config_set custom_hid_config_desc __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = DESC_LEN_CONFIG,
.bNumInterfaces = 0x01U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0x80U,
.bMaxPower = 0x32U
},
.hid_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x02U,
.bInterfaceClass = USB_HID_CLASS,
.bInterfaceSubClass = 0x00U,
.bInterfaceProtocol = 0x00U,
.iInterface = 0x00U
},
.hid_vendor =
{
.header =
{
.bLength = sizeof(usb_desc_hid),
.bDescriptorType = USB_DESCTYPE_HID
},
.bcdHID = 0x0111U,
.bCountryCode = 0x00U,
.bNumDescriptors = 0x01U,
.bDescriptorType = USB_DESCTYPE_REPORT,
.wDescriptorLength = DESC_LEN_REPORT
},
.hid_epin =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = CUSTOMHID_IN_EP,
.bmAttributes = USB_EP_ATTR_INT,
.wMaxPacketSize = CUSTOMHID_IN_PACKET,
.bInterval = 0x20U
},
.hid_epout =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = CUSTOMHID_OUT_EP,
.bmAttributes = USB_EP_ATTR_INT,
.wMaxPacketSize = CUSTOMHID_OUT_PACKET,
.bInterval = 0x20U
}
};
/* USB language ID descriptor */
static __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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN const usb_desc_str product_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(14U),
.bDescriptorType = USB_DESCTYPE_STR
},
.unicode_string = {'G', 'D', '3', '2', '-', 'C', 'u', 's', 't', 'o', 'm', 'H', 'I', 'D'}
};
/* USBD serial string */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
/* USB string descriptor set */
void *const usbd_hid_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 custom_hid_desc = {
.dev_desc = (uint8_t *)&custom_hid_dev_desc,
.config_desc = (uint8_t *)&custom_hid_config_desc,
.strings = usbd_hid_strings
};
__ALIGN_BEGIN const uint8_t customhid_report_descriptor[DESC_LEN_REPORT] __ALIGN_END = {
0x06U, 0x00U, 0xFFU, /* USAGE_PAGE (Vendor Defined: 0xFF00) */
0x09U, 0x00U, /* USAGE (Custom Device) */
0xa1U, 0x01U, /* COLLECTION (Application) */
/* led 1 */
0x85U, 0x11U, /* REPORT_ID (0x11) */
0x09U, 0x01U, /* USAGE (LED 1) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0x01U, /* LOGICAL_MAXIMUM (1) */
0x75U, 0x08U, /* REPORT_SIZE (8) */
0x95U, 0x01U, /* REPORT_COUNT (1) */
0x91U, 0x82U, /* OUTPUT (Data,Var,Abs,Vol) */
/* led 2 */
0x85U, 0x12U, /* REPORT_ID (0x12) */
0x09U, 0x02U, /* USAGE (LED 2) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0x01U, /* LOGICAL_MAXIMUM (1) */
0x75U, 0x08U, /* REPORT_SIZE (8) */
0x95U, 0x01U, /* REPORT_COUNT (1) */
0x91U, 0x82U, /* OUTPUT (Data,Var,Abs,Vol) */
/* led 3 */
0x85U, 0x13U, /* REPORT_ID (0x13) */
0x09U, 0x03U, /* USAGE (LED 3) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0x01U, /* LOGICAL_MAXIMUM (1) */
0x75U, 0x08U, /* REPORT_SIZE (8) */
0x95U, 0x01U, /* REPORT_COUNT (1) */
0x91U, 0x82U, /* OUTPUT (Data,Var,Abs,Vol) */
/* led 4 */
0x85U, 0x14U, /* REPORT_ID (0x14) */
0x09U, 0x04U, /* USAGE (LED 4) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0x01U, /* LOGICAL_MAXIMUM (1) */
0x75U, 0x08U, /* REPORT_SIZE (8) */
0x95U, 0x01U, /* REPORT_COUNT (1) */
0x91U, 0x82U, /* OUTPUT (Data,Var,Abs,Vol) */
/* wakeup key */
0x85U, 0x15U, /* REPORT_ID (0x15) */
0x09U, 0x05U, /* USAGE (Push Button) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0x01U, /* LOGICAL_MAXIMUM (1) */
0x75U, 0x01U, /* REPORT_SIZE (1) */
0x81U, 0x02U, /* INPUT (Data,Var,Abs,Vol) */
0x75U, 0x07U, /* REPORT_SIZE (7) */
0x81U, 0x03U, /* INPUT (Cnst,Var,Abs,Vol) */
/* tamper key */
0x85U, 0x16U, /* REPORT_ID (0x16) */
0x09U, 0x06U, /* USAGE (Push Button) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0x01U, /* LOGICAL_MAXIMUM (1) */
0x75U, 0x01U, /* REPORT_SIZE (1) */
0x81U, 0x02U, /* INPUT (Data,Var,Abs,Vol) */
0x75U, 0x07U, /* REPORT_SIZE (7) */
0x81U, 0x03U, /* INPUT (Cnst,Var,Abs,Vol) */
0xc0U /* END_COLLECTION */
};
/* local function prototypes ('static') */
static uint8_t custom_hid_init(usb_dev *udev, uint8_t config_index);
static uint8_t custom_hid_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t custom_hid_req_handler(usb_dev *udev, usb_req *req);
static uint8_t custom_hid_data_in(usb_dev *udev, uint8_t ep_num);
static uint8_t custom_hid_data_out(usb_dev *udev, uint8_t ep_num);
usb_class_core usbd_custom_hid_cb = {
.command = NO_CMD,
.alter_set = 0U,
.init = custom_hid_init,
.deinit = custom_hid_deinit,
.req_proc = custom_hid_req_handler,
.data_in = custom_hid_data_in,
.data_out = custom_hid_data_out
};
/*!
\brief register HID interface operation functions
\param[in] udev: pointer to USB device instance
\param[in] hid_fop: HID operation functions structure
\param[out] none
\retval USB device operation status
*/
uint8_t custom_hid_itfop_register(usb_dev *udev, hid_fop_handler *hid_fop)
{
if(NULL != hid_fop) {
udev->dev.user_data = hid_fop;
return USBD_OK;
}
return USBD_FAIL;
}
/*!
\brief send custom HID report
\param[in] udev: pointer to USB device instance
\param[in] report: pointer to HID report
\param[in] len: data length
\param[out] none
\retval USB device operation status
*/
uint8_t custom_hid_report_send(usb_dev *udev, uint8_t *report, uint32_t len)
{
usbd_ep_send(udev, CUSTOMHID_IN_EP, report, len);
return USBD_OK;
}
/*!
\brief initialize the HID 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 custom_hid_init(usb_dev *udev, uint8_t config_index)
{
static __ALIGN_BEGIN custom_hid_handler hid_handler __ALIGN_END;
memset((void *)&hid_handler, 0U, sizeof(custom_hid_handler));
/* initialize the data TX endpoint */
usbd_ep_setup(udev, &(custom_hid_config_desc.hid_epin));
/* initialize the data RX endpoint */
usbd_ep_setup(udev, &(custom_hid_config_desc.hid_epout));
/* prepare receive data */
usbd_ep_recev(udev, CUSTOMHID_OUT_EP, hid_handler.data, 2U);
udev->dev.class_data[CUSTOM_HID_INTERFACE] = (void *)&hid_handler;
if(NULL != udev->dev.user_data) {
for(uint8_t i = 0U; i < MAX_PERIPH_NUM; i++) {
if(NULL != ((hid_fop_handler *)udev->dev.user_data)->periph_config[i]) {
((hid_fop_handler *)udev->dev.user_data)->periph_config[i]();
}
}
}
return USBD_OK;
}
/*!
\brief deinitialize the HID 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 custom_hid_deinit(usb_dev *udev, uint8_t config_index)
{
/* deinitialize HID endpoints */
usbd_ep_clear(udev, CUSTOMHID_IN_EP);
usbd_ep_clear(udev, CUSTOMHID_OUT_EP);
return USBD_OK;
}
/*!
\brief handle the HID class-specific 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 custom_hid_req_handler(usb_dev *udev, usb_req *req)
{
usb_transc *transc = &udev->dev.transc_in[0];
custom_hid_handler *hid = (custom_hid_handler *)udev->dev.class_data[CUSTOM_HID_INTERFACE];
switch(req->bRequest) {
case GET_REPORT:
break;
case GET_IDLE:
transc->xfer_buf = (uint8_t *)&hid->idlestate;
transc->remain_len = 1U;
break;
case GET_PROTOCOL:
transc->xfer_buf = (uint8_t *)&hid->protocol;
transc->remain_len = 1U;
break;
case SET_REPORT:
hid->reportID = (uint8_t)(req->wValue);
break;
case SET_IDLE:
hid->idlestate = (uint8_t)(req->wValue >> 8);
break;
case SET_PROTOCOL:
hid->protocol = (uint8_t)(req->wValue);
break;
case USB_GET_DESCRIPTOR:
if(USB_DESCTYPE_REPORT == (req->wValue >> 8)) {
transc->remain_len = USB_MIN(DESC_LEN_REPORT, req->wLength);
transc->xfer_buf = (uint8_t *)customhid_report_descriptor;
}
break;
default:
return USBD_FAIL;
}
return USBD_OK;
}
/*!
\brief handle custom HID data
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval USB device operation status
*/
static uint8_t custom_hid_data_in(usb_dev *udev, uint8_t ep_num)
{
return USBD_OK;
}
/*!
\brief handle custom HID data
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval USB device operation status
*/
static uint8_t custom_hid_data_out(usb_dev *udev, uint8_t ep_num)
{
custom_hid_handler *hid = (custom_hid_handler *)udev->dev.class_data[CUSTOM_HID_INTERFACE];
/* light the LED */
switch(hid->data[0]) {
case 0x11U:
if(RESET != hid->data[1]) {
gd_eval_led_on(LED1);
} else {
gd_eval_led_off(LED1);
}
break;
case 0x12U:
if(RESET != hid->data[1]) {
gd_eval_led_on(LED2);
} else {
gd_eval_led_off(LED2);
}
break;
case 0x13U:
if(RESET != hid->data[1]) {
gd_eval_led_on(LED3);
} else {
gd_eval_led_off(LED3);
}
break;
case 0x14U:
break;
default:
break;
}
usbd_ep_recev(udev, CUSTOMHID_OUT_EP, hid->data, 2U);
return USBD_OK;
}
@@ -0,0 +1,463 @@
/*!
\file standard_hid_core.c
\brief HID class driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 "standard_hid_core.h"
#include <string.h>
#define USBD_VID 0x28e9U
#define USBD_PID 0x0380U
/* Note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev hid_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
};
__ALIGN_BEGIN const usb_hid_desc_config_set hid_config_desc __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = USB_HID_CONFIG_DESC_LEN,
.bNumInterfaces = 0x01U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0xA0U,
.bMaxPower = 0x32U
},
.hid_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x01U,
.bInterfaceClass = USB_HID_CLASS,
.bInterfaceSubClass = USB_HID_SUBCLASS_BOOT_ITF,
.bInterfaceProtocol = USB_HID_PROTOCOL_KEYBOARD,
.iInterface = 0x00U
},
.hid_vendor =
{
.header =
{
.bLength = sizeof(usb_desc_hid),
.bDescriptorType = USB_DESCTYPE_HID
},
.bcdHID = 0x0111U,
.bCountryCode = 0x00U,
.bNumDescriptors = 0x01U,
.bDescriptorType = USB_DESCTYPE_REPORT,
.wDescriptorLength = USB_HID_REPORT_DESC_LEN
},
.hid_epin =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = HID_IN_EP,
.bmAttributes = USB_EP_ATTR_INT,
.wMaxPacketSize = HID_IN_PACKET,
.bInterval = 0x10U
}
};
__ALIGN_BEGIN const usb_hid_desc_config_set other_speed_hid_config_desc __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_OTHER_SPD_CONFIG
},
.wTotalLength = USB_HID_CONFIG_DESC_LEN,
.bNumInterfaces = 0x01U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0xA0U,
.bMaxPower = 0x32U
},
.hid_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x01U,
.bInterfaceClass = USB_HID_CLASS,
.bInterfaceSubClass = USB_HID_SUBCLASS_BOOT_ITF,
.bInterfaceProtocol = USB_HID_PROTOCOL_KEYBOARD,
.iInterface = 0x00U
},
.hid_vendor =
{
.header =
{
.bLength = sizeof(usb_desc_hid),
.bDescriptorType = USB_DESCTYPE_HID
},
.bcdHID = 0x0111U,
.bCountryCode = 0x00U,
.bNumDescriptors = 0x01U,
.bDescriptorType = USB_DESCTYPE_REPORT,
.wDescriptorLength = USB_HID_REPORT_DESC_LEN
},
.hid_epin =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = HID_IN_EP,
.bmAttributes = USB_EP_ATTR_INT,
.wMaxPacketSize = HID_IN_PACKET,
.bInterval = 0x40U
}
};
// __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 */
static __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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN const usb_desc_str product_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(17U),
.bDescriptorType = USB_DESCTYPE_STR
},
.unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'K', 'e', 'y', 'b', 'o', 'a', 'r', 'd'}
};
/* USBD serial string */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
void *const usbd_hid_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 hid_desc = {
.dev_desc = (uint8_t *)&hid_dev_desc,
.config_desc = (uint8_t *)&hid_config_desc,
#if defined(USE_USB_HS) && defined(USE_ULPI_PHY)
.other_speed_config_desc = (uint8_t *)&other_speed_hid_config_desc,
.qualifier_desc = (uint8_t *)&usbd_qualifier_desc,
#endif
.strings = usbd_hid_strings
};
__ALIGN_BEGIN const uint8_t hid_report_desc[USB_HID_REPORT_DESC_LEN] __ALIGN_END = {
0x05U, 0x01U, /* USAGE_PAGE (Generic Desktop) */
0x09U, 0x06U, /* USAGE (Keyboard) */
0xa1U, 0x01U, /* COLLECTION (Application) */
0x05U, 0x07U, /* USAGE_PAGE (Keyboard/Keypad) */
0x19U, 0xe0U, /* USAGE_MINIMUM (Keyboard LeftControl) */
0x29U, 0xe7U, /* USAGE_MAXIMUM (Keyboard Right GUI) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0x01U, /* LOGICAL_MAXIMUM (1) */
0x95U, 0x08U, /* REPORT_COUNT (8) */
0x75U, 0x01U, /* REPORT_SIZE (1) */
0x81U, 0x02U, /* INPUT (Data,Var,Abs) */
0x95U, 0x01U, /* REPORT_COUNT (1) */
0x75U, 0x08U, /* REPORT_SIZE (8) */
0x81U, 0x03U, /* INPUT (Cnst,Var,Abs) */
0x95U, 0x06U, /* REPORT_COUNT (6) */
0x75U, 0x08U, /* REPORT_SIZE (8) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x26U, 0xFFU, 0x00U, /* LOGICAL_MAXIMUM (255) */
0x05U, 0x07U, /* USAGE_PAGE (Keyboard/Keypad) */
0x19U, 0x00U, /* USAGE_MINIMUM (Reserved (no event indicated)) */
0x29U, 0x65U, /* USAGE_MAXIMUM (Keyboard Application) */
0x81U, 0x00U, /* INPUT (Data,Ary,Abs) */
0xc0U /* END_COLLECTION */
};
/* local function prototypes ('static') */
static uint8_t hid_init(usb_dev *udev, uint8_t config_index);
static uint8_t hid_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t hid_req(usb_dev *udev, usb_req *req);
static uint8_t hid_data_in(usb_dev *udev, uint8_t ep_num);
usb_class_core usbd_hid_cb = {
.command = NO_CMD,
.alter_set = 0U,
.init = hid_init,
.deinit = hid_deinit,
.req_proc = hid_req,
.data_in = hid_data_in
};
/*!
\brief register HID interface operation functions
\param[in] udev: pointer to USB device instance
\param[in] hid_fop: HID operation function structure
\param[out] none
\retval USB device operation status
*/
uint8_t hid_itfop_register(usb_dev *udev, hid_fop_handler *hid_fop)
{
if(NULL != hid_fop) {
udev->dev.user_data = (void *)hid_fop;
return USBD_OK;
}
return USBD_FAIL;
}
/*!
\brief send keyboard report
\param[in] udev: pointer to USB device instance
\param[in] report: pointer to HID report
\param[in] len: data length
\param[out] none
\retval USB device operation status
*/
uint8_t hid_report_send(usb_dev *udev, uint8_t *report, uint32_t len)
{
standard_hid_handler *hid = (standard_hid_handler *)udev->dev.class_data[USBD_HID_INTERFACE];
hid->prev_transfer_complete = 0U;
usbd_ep_send(udev, HID_IN_EP, report, len);
return USBD_OK;
}
/*!
\brief initialize the HID 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 hid_init(usb_dev *udev, uint8_t config_index)
{
(void) config_index;
static __ALIGN_BEGIN standard_hid_handler hid_handler __ALIGN_END;
memset((void *)&hid_handler, 0U, sizeof(standard_hid_handler));
/* initialize the data TX endpoint */
usbd_ep_setup(udev, &(hid_config_desc.hid_epin));
hid_handler.prev_transfer_complete = 1U;
udev->dev.class_data[USBD_HID_INTERFACE] = (void *)&hid_handler;
if(NULL != udev->dev.user_data) {
((hid_fop_handler *)udev->dev.user_data)->hid_itf_config();
}
return USBD_OK;
}
/*!
\brief deinitialize the HID 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 hid_deinit(usb_dev *udev, uint8_t config_index)
{
(void) config_index;
/* deinitialize HID endpoints */
usbd_ep_clear(udev, HID_IN_EP);
return USBD_OK;
}
/*!
\brief handle the HID class-specific 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 hid_req(usb_dev *udev, usb_req *req)
{
usb_transc *transc = &udev->dev.transc_in[0];
standard_hid_handler *hid = (standard_hid_handler *)udev->dev.class_data[USBD_HID_INTERFACE];
switch(req->bRequest) {
case GET_REPORT:
/* no use for this driver */
break;
case GET_IDLE:
transc->xfer_buf = (uint8_t *)&hid->idle_state;
transc->remain_len = 1U;
break;
case GET_PROTOCOL:
transc->xfer_buf = (uint8_t *)&hid->protocol;
transc->remain_len = 1U;
break;
case SET_REPORT:
/* no use for this driver */
break;
case SET_IDLE:
hid->idle_state = (uint8_t)(req->wValue >> 8);
break;
case SET_PROTOCOL:
hid->protocol = (uint8_t)(req->wValue);
break;
case USB_GET_DESCRIPTOR:
if(USB_DESCTYPE_REPORT == (req->wValue >> 8)) {
transc->remain_len = USB_MIN(USB_HID_REPORT_DESC_LEN, req->wLength);
transc->xfer_buf = (uint8_t *)hid_report_desc;
return REQ_SUPP;
} else if(USB_DESCTYPE_HID == (req->wValue >> 8)) {
transc->remain_len = USB_MIN(9U, req->wLength);
transc->xfer_buf = (uint8_t *)(&(hid_config_desc.hid_vendor));
} else {
/* no operation */
}
break;
default:
break;
}
return USBD_OK;
}
/*!
\brief handle data stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval USB device operation status
*/
static uint8_t hid_data_in(usb_dev *udev, uint8_t ep_num)
{
(void) ep_num;
standard_hid_handler *hid = (standard_hid_handler *)udev->dev.class_data[USBD_HID_INTERFACE];
if(0U != hid->data[2]) {
hid->data[2] = 0x00U;
usbd_ep_send(udev, HID_IN_EP, hid->data, HID_IN_PACKET);
} else {
hid->prev_transfer_complete = 1U;
}
return USBD_OK;
}
@@ -0,0 +1,103 @@
/*!
\file usb_iap_core.h
\brief the header file of IAP driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 USB_IAP_CORE_H
#define USB_IAP_CORE_H
#include "usbd_enum.h"
#include "usb_hid.h"
#define USB_SERIAL_STRING_SIZE 0x06U /*!< serial string size */
#ifdef USE_USB_FS
#define USB_DESC_LEN_IAP_REPORT 35U /*!< report descriptor length */
#elif defined(USE_USB_HS)
#ifdef USE_ULPI_PHY
#define USB_DESC_LEN_IAP_REPORT 37U /*!< report descriptor length */
#else
#define USB_DESC_LEN_IAP_REPORT 35U /*!< report descriptor length */
#endif /* USE_ULPI_PHY */
#else
#error "please select 'USE_USB_FS' or 'USE_USB_HS'"
#endif /* USE_USB_FS */
#define USB_DESC_LEN_IAP_CONFIG_SET 41U /*!< configuration descriptor length */
/* special commands with download request */
#define IAP_READ_OPTION_BYTE 0x01U /*!< read option byte request */
#define IAP_ERASE 0x02U /*!< erase request */
#define IAP_DOWNLOAD 0x03U /*!< download request */
#define IAP_LEAVE 0x04U /*!< leave request */
#define IAP_GETBIN_ADDRESS 0x05U /*!< get bin address request */
#define IAP_WRITE_OPTION_BYTE 0x06U /*!< write option byte request */
#define IAP_UPLOAD 0x07U /*!< upload request */
#define IAP_CHECK_RDP 0x08U /*!< check rdp state request */
#define OPERATION_SUCCESS 0x02U /*!< operation success status */
#define OPERATION_FAIL 0x5FU /*!< operation fail status */
#define LEAVE_FINISH 0x04U /*!< leave finish status */
#define OB_WRITE_SUCCESS 0x03U /*!< OB write success status */
#define IS_RDP_MODE 0xBBU /*!< MCU RDP status */
#define IS_NORMAL_MODE 0xA5U /*!< MCU normal status */
#define IAP_HOST_ID 0x01U /*!< IAP host ID */
#define IAP_DEVICE_ID 0x02U /*!< IAP device ID */
typedef struct {
__ALIGN_BEGIN uint8_t report_buf[IAP_OUT_PACKET + 1U] __ALIGN_END; /*!< report buff */
__ALIGN_BEGIN uint8_t option_byte[IAP_IN_PACKET] __ALIGN_END; /*!< option byte buff */
/* state machine variables */
__ALIGN_BEGIN uint8_t dev_status[IAP_IN_PACKET] __ALIGN_END; /*!< device status */
__ALIGN_BEGIN uint8_t bin_addr[IAP_IN_PACKET] __ALIGN_END; /*!< load bin address */
uint8_t reportID; /*!< report id */
uint8_t flag; /*!< flag */
uint32_t protocol; /*!< control request protocol */
uint32_t idlestate; /*!< control request idle state */
uint16_t transfer_times; /*!< data transfer times */
uint16_t page_count; /*!< memory page count */
uint32_t file_length; /*!< file length*/
uint32_t base_address; /*!< loaded base address */
} usbd_iap_handler;
typedef void (*app_func)(void);
extern usb_desc iap_desc;
extern usb_class_core iap_class;
/* function declarations */
/* send IAP report */
uint8_t iap_report_send(usb_dev *udev, uint8_t *report, uint32_t len);
#endif /* USB_IAP_CORE_H */
@@ -0,0 +1,663 @@
/*!
\file usb_iap_core.c
\brief IAP driver
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 <string.h>
#include "usb_iap_core.h"
#include "flash_operation.h"
#define USBD_VID 0x28E9U
#define USBD_PID 0x0228U
/* Note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev iap_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
};
__ALIGN_BEGIN const usb_hid_desc_config_set iap_config_desc __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = USB_DESC_LEN_IAP_CONFIG_SET,
.bNumInterfaces = 0x01U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0x80U,
.bMaxPower = 0x32U
},
.hid_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x02U,
.bInterfaceClass = USB_HID_CLASS,
.bInterfaceSubClass = 0x00U,
.bInterfaceProtocol = 0x00U,
.iInterface = 0x00U
},
.hid_vendor =
{
.header =
{
.bLength = sizeof(usb_desc_hid),
.bDescriptorType = USB_DESCTYPE_HID
},
.bcdHID = 0x0111U,
.bCountryCode = 0x00U,
.bNumDescriptors = 0x01U,
.bDescriptorType = USB_DESCTYPE_REPORT,
.wDescriptorLength = USB_DESC_LEN_IAP_REPORT
},
.hid_epin =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = IAP_IN_EP,
.bmAttributes = USB_EP_ATTR_INT,
.wMaxPacketSize = IAP_IN_PACKET,
.bInterval = 0x01U
},
.hid_epout =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = IAP_OUT_EP,
.bmAttributes = USB_EP_ATTR_INT,
.wMaxPacketSize = IAP_OUT_PACKET,
.bInterval = 0x01U
}
};
/* USB language ID Descriptor */
static __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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN 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', '_', 'I', 'A', 'P'}
};
/* USB serial string */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(2U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
void *const usbd_iap_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 iap_desc = {
.dev_desc = (uint8_t *)&iap_dev_desc,
.config_desc = (uint8_t *)&iap_config_desc,
.strings = usbd_iap_strings
};
/* local function prototypes ('static') */
static uint8_t iap_init(usb_dev *udev, uint8_t config_index);
static uint8_t iap_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t iap_req_handler(usb_dev *udev, usb_req *req);
static uint8_t iap_data_out(usb_dev *udev, uint8_t ep_num);
/* IAP requests management functions */
static void iap_req_erase(usb_dev *udev);
static void iap_req_download(usb_dev *udev);
static void iap_req_read_optionbyte(usb_dev *udev);
static void iap_req_write_optionbyte(usb_dev *udev);
static void iap_req_leave(usb_dev *udev);
static void iap_address_send(usb_dev *udev);
static void iap_req_upload(usb_dev *udev);
static void iap_check_rdp(usb_dev *udev);
usb_class_core iap_class = {
.init = iap_init,
.deinit = iap_deinit,
.req_proc = iap_req_handler,
.data_out = iap_data_out
};
/* USB custom HID device report descriptor */
__ALIGN_BEGIN const uint8_t iap_report_desc[USB_DESC_LEN_IAP_REPORT] __ALIGN_END = {
0x05U, 0x01U, /* USAGE_PAGE (Generic Desktop) */
0x09U, 0x00U, /* USAGE (Custom Device) */
0xa1U, 0x01U, /* COLLECTION (Application) */
/* IAP command and data */
0x85U, 0x01U, /* REPORT_ID (0x01) */
0x09U, 0x01U, /* USAGE (IAP command) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0xffU, /* LOGICAL_MAXIMUM (255) */
0x75U, 0x08U, /* REPORT_SIZE (8) */
#ifdef USE_USB_FS
0x95U, REPORT_OUT_COUNT,
#else
#ifdef USE_ULPI_PHY
0x96U, BYTE_LOW(REPORT_OUT_COUNT), BYTE_HIGH(REPORT_OUT_COUNT),
#elif defined(USE_EMBEDDED_PHY)
0x95U, REPORT_OUT_COUNT,
#endif
#endif
0x91U, 0x82U, /* OUTPUT (Data,Var,Abs,Vol) */
/* device status and option byte */
0x85U, 0x02U, /* REPORT_ID (0x02) */
0x09U, 0x02U, /* USAGE (Status and option byte) */
0x15U, 0x00U, /* LOGICAL_MINIMUM (0) */
0x25U, 0xffU, /* LOGICAL_MAXIMUM (255) */
0x75U, 0x08U, /* REPORT_SIZE (8) */
#ifdef USE_USB_FS
0x95U, REPORT_IN_COUNT,
#else
#ifdef USE_ULPI_PHY
0x96U, BYTE_LOW(REPORT_IN_COUNT), BYTE_HIGH(REPORT_IN_COUNT),
#elif defined(USE_EMBEDDED_PHY)
0x95U, REPORT_IN_COUNT,
#endif
#endif
0x81U, 0x82U, /* INPUT (Data,Var,Abs,Vol) */
0xc0U /* END_COLLECTION */
};
/*!
\brief send IAP report
\param[in] udev: pointer to USB device instance
\param[in] report: pointer to HID report
\param[in] len: data length
\param[out] none
\retval USB device operation status
*/
uint8_t iap_report_send(usb_dev *udev, uint8_t *report, uint32_t len)
{
usbd_ep_send(udev, IAP_IN_EP, report, len);
return USBD_OK;
}
/*!
\brief initialize the IAP 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 iap_init(usb_dev *udev, uint8_t config_index)
{
static __ALIGN_BEGIN usbd_iap_handler iap_handler __ALIGN_END;
/* initialize TX endpoint */
usbd_ep_setup(udev, &(iap_config_desc.hid_epin));
/* initialize RX endpoint */
usbd_ep_setup(udev, &(iap_config_desc.hid_epout));
memset((void *)&iap_handler, 0U, sizeof(usbd_iap_handler));
/* prepare receive data */
usbd_ep_recev(udev, IAP_OUT_EP, iap_handler.report_buf, IAP_OUT_PACKET);
iap_handler.base_address = APP_LOADED_ADDR;
udev->dev.class_data[USBD_IAP_INTERFACE] = (void *)&iap_handler;
return USBD_OK;
}
/*!
\brief deinitialize the IAP 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 iap_deinit(usb_dev *udev, uint8_t config_index)
{
/* deinitialize IAP endpoints */
usbd_ep_clear(udev, IAP_IN_EP);
usbd_ep_clear(udev, IAP_OUT_EP);
return USBD_OK;
}
/*!
\brief handle the IAP class-specific 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 iap_req_handler(usb_dev *udev, usb_req *req)
{
usb_transc *transc = &udev->dev.transc_in[0];
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
switch(req->bRequest) {
case GET_REPORT:
/* no use for this driver */
break;
case GET_IDLE:
transc->xfer_buf = (uint8_t *)&iap->idlestate;
transc->remain_len = 1U;
break;
case GET_PROTOCOL:
transc->xfer_buf = (uint8_t *)&iap->protocol;
transc->remain_len = 1U;
break;
case SET_REPORT:
iap->reportID = (uint8_t)(req->wValue);
break;
case SET_IDLE:
iap->idlestate = (uint8_t)(req->wValue >> 8);
break;
case SET_PROTOCOL:
iap->protocol = (uint8_t)(req->wValue);
break;
case USB_GET_DESCRIPTOR:
if(USB_DESCTYPE_REPORT == (req->wValue >> 8)) {
transc->remain_len = USB_MIN(USB_DESC_LEN_IAP_REPORT, req->wLength);
transc->xfer_buf = (uint8_t *)iap_report_desc;
}
break;
default:
return USBD_FAIL;
}
return USBD_OK;
}
/*!
\brief handle data OUT stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static uint8_t iap_data_out(usb_dev *udev, uint8_t ep_num)
{
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
if(IAP_HOST_ID == iap->report_buf[0]) {
switch(iap->report_buf[1]) {
case IAP_DOWNLOAD:
iap_req_download(udev);
break;
case IAP_ERASE:
iap_req_erase(udev);
break;
case IAP_READ_OPTION_BYTE:
iap_req_read_optionbyte(udev);
break;
case IAP_LEAVE:
iap_req_leave(udev);
break;
case IAP_GETBIN_ADDRESS:
iap_address_send(udev);
break;
case IAP_WRITE_OPTION_BYTE:
iap_req_write_optionbyte(udev);
break;
case IAP_UPLOAD:
iap_req_upload(udev);
break;
case IAP_CHECK_RDP:
iap_check_rdp(udev);
break;
default:
break;
}
}
usbd_ep_recev(udev, IAP_OUT_EP, iap->report_buf, IAP_OUT_PACKET);
return USBD_OK;
}
/*!
\brief handle the IAP_DOWNLOAD request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_req_download(usb_dev *udev)
{
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
iap->dev_status[0] = IAP_DEVICE_ID;
/* get the target address to download */
iap->base_address = iap->report_buf[2];
iap->base_address |= (uint32_t)iap->report_buf[3] << 8;
iap->base_address |= (uint32_t)iap->report_buf[4] << 16;
iap->base_address |= (uint32_t)iap->report_buf[5] << 24;
/* program the target address */
if(FMC_READY == iap_data_write(&iap->report_buf[6], iap->base_address, TRANSFER_SIZE)) {
iap->dev_status[1] = OPERATION_SUCCESS;
} else {
iap->dev_status[1] = OPERATION_FAIL;
}
iap_report_send(udev, iap->dev_status, IAP_IN_PACKET);
}
/*!
\brief handle the IAP_WRITE_OPTION_BYTE request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_req_write_optionbyte(usb_dev *udev)
{
uint32_t option_byte_addr = 0U;
uint16_t option_byte_size = 0U;
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
/* get option byte address address */
option_byte_addr = iap->report_buf[2];
option_byte_addr |= (uint32_t)iap->report_buf[3] << 8;
option_byte_addr |= (uint32_t)iap->report_buf[4] << 16;
option_byte_addr |= (uint32_t)iap->report_buf[5] << 24;
/* get option byte address size */
if(OPT_BYTE_ADDR1 == option_byte_addr) {
option_byte_size = OPT_BYTE_SIZE2;
} else if(OPT_BYTE_ADDR2 == option_byte_addr) {
option_byte_size = OPT_BYTE_SIZE2;
}
iap->dev_status[0] = IAP_DEVICE_ID;
/* write option byte address data */
if(FMC_READY == option_byte_write(option_byte_addr, &iap->report_buf[6], option_byte_size)) {
iap->dev_status[1] = OB_WRITE_SUCCESS;
} else {
iap->dev_status[1] = OPERATION_FAIL;
}
iap_report_send(udev, iap->dev_status, IAP_IN_PACKET);
}
/*!
\brief handle the IAP_ERASE request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_req_erase(usb_dev *udev)
{
uint32_t addr = 0U;
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
/* get base address to erase */
iap->base_address = iap->report_buf[2];
iap->base_address |= (uint32_t)iap->report_buf[3] << 8;
iap->base_address |= (uint32_t)iap->report_buf[4] << 16;
iap->base_address |= (uint32_t)iap->report_buf[5] << 24;
/* get file length */
iap->file_length = iap->report_buf[6];
iap->file_length |= (uint32_t)iap->report_buf[7] << 8;
iap->file_length |= (uint32_t)iap->report_buf[8] << 16;
iap->file_length |= (uint32_t)iap->report_buf[9] << 24;
/* check if the address is in protected area */
if(IS_PROTECTED_AREA(iap->base_address)) {
return;
}
addr = iap->base_address;
iap->dev_status[0] = IAP_DEVICE_ID;
if(FMC_READY == flash_erase(addr, iap->file_length)) {
iap->dev_status[1] = OPERATION_SUCCESS;
} else {
iap->dev_status[1] = OPERATION_FAIL;
}
usbd_ep_send(udev, IAP_IN_EP, iap->dev_status, IAP_IN_PACKET);
}
/*!
\brief handle the IAP_READ_OPTION_BYTE request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_req_read_optionbyte(usb_dev *udev)
{
uint8_t i = 0U;
uint32_t option_size = 0U, temp = 0U, option_address = 0U;
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
/* read option address address */
option_address = iap->report_buf[2] + (iap->report_buf[3] << 8) + (iap->report_buf[4] << 16) + (iap->report_buf[5] << 24);
iap->option_byte[0] = IAP_DEVICE_ID;
if(OPT_BYTE_ADDR1 == option_address) {
option_size = OPT_BYTE_SIZE1;
} else if(OPT_BYTE_ADDR2 == option_address) {
option_size = OPT_BYTE_SIZE2;
}
/* read option address content */
for(i = 0U; i < (option_size / 4U); i++) {
temp = *(uint32_t *)option_address;
iap->option_byte[4 * i + 5] = temp >> 24;
iap->option_byte[4 * i + 4] = temp >> 16;
iap->option_byte[4 * i + 3] = temp >> 8;
iap->option_byte[4 * i + 2] = temp;
option_address = option_address + 4U;
}
iap->option_byte[1] = OPERATION_SUCCESS;
iap_report_send(udev, iap->option_byte, IAP_IN_PACKET);
}
/*!
\brief handle the IAP_LEAVE request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_req_leave(usb_dev *udev)
{
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
/* get base address to jump */
iap->base_address = iap->report_buf[2];
iap->base_address |= (uint32_t)iap->report_buf[3] << 8;
iap->base_address |= (uint32_t)iap->report_buf[4] << 16;
iap->base_address |= (uint32_t)iap->report_buf[5] << 24;
iap->dev_status[0] = IAP_DEVICE_ID;
iap->dev_status[1] = LEAVE_FINISH;
usbd_ep_send(udev, IAP_IN_EP, iap->dev_status, IAP_IN_PACKET);
usbd_disconnect(udev);
/* reset register */
register_reset();
/* jump to target */
jump_to_execute(iap->base_address);
}
/*!
\brief handle the IAP_GETBIN_ADDRESS request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_address_send(usb_dev *udev)
{
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
iap->bin_addr[0] = IAP_DEVICE_ID;
/* get app boundary address */
iap->bin_addr[1] = (uint8_t)(APP_LOADED_ADDR);
iap->bin_addr[2] = (uint8_t)(APP_LOADED_ADDR >> 8);
iap->bin_addr[3] = (uint8_t)(APP_LOADED_ADDR >> 16);
iap->bin_addr[4] = (uint8_t)(APP_LOADED_ADDR >> 24);
iap_report_send(udev, iap->bin_addr, IAP_IN_PACKET);
}
/*!
\brief handle the IAP_UPLOAD request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_req_upload(usb_dev *udev)
{
uint16_t packet_valid_length = 0U, i = 0U;
uint32_t bin_flash_addr = APP_LOADED_ADDR;
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
iap->bin_addr[0] = IAP_DEVICE_ID;
/* get target flash address */
bin_flash_addr = iap->report_buf[2];
bin_flash_addr |= (uint32_t)iap->report_buf[3] << 8;
bin_flash_addr |= (uint32_t)iap->report_buf[4] << 16;
bin_flash_addr |= (uint32_t)iap->report_buf[5] << 24;
/* get current packet valid length */
packet_valid_length = iap->report_buf[6];
packet_valid_length |= iap->report_buf[7] << 8;
/* get target flash address content */
for(i = 0U; i < packet_valid_length; i++) {
iap->bin_addr[i + 1] = REG8(bin_flash_addr + i);
}
iap_report_send(udev, iap->bin_addr, IAP_IN_PACKET);
}
/*!
\brief handle the IAP_CHECK_RDP request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_check_rdp(usb_dev *udev)
{
uint8_t mode = 0U;
usbd_iap_handler *iap = (usbd_iap_handler *)udev->dev.class_data[USBD_IAP_INTERFACE];
/* check whether the SPC bit of FMC module is normal state */
if(0xAAU != (uint8_t)(REG16(OPT_BYTE_ADDR1) >> 8U)) {
mode = IS_RDP_MODE;
} else {
mode = IS_NORMAL_MODE;
}
iap->bin_addr[0] = IAP_DEVICE_ID;
iap->bin_addr[1] = mode;
iap_report_send(udev, iap->bin_addr, IAP_IN_PACKET);
}
@@ -0,0 +1,100 @@
/*!
\file usbd_msc_bbb.h
\brief the header file of the usbd_msc_bbb.c file
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 {
uint8_t bbb_data[MSC_MEDIA_PACKET_SIZE]; /*!< 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 */
msc_bbb_cbw bbb_cbw; /*!< MSC BBB CBW structural */
msc_bbb_csw bbb_csw; /*!< 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 */
msc_scsi_sense scsi_sense[SENSE_LIST_DEEPTH]; /*!< MSC SCSI sense structural buff */
} 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,57 @@
/*!
\file usbd_msc_core.h
\brief the header file of USB MSC device class core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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,72 @@
/*!
\file usbd_msc_mem.h
\brief header file for storage memory
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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_getcapacity) (uint8_t lun, uint32_t *block_num, uint32_t *block_size);
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 */ /*!< memory block length buff */
}usbd_mem_cb;
extern usbd_mem_cb *usbd_mem_fops;
typedef struct _USBD_STORAGE
{
int8_t (* Init) (uint8_t lun);
int8_t (* GetCapacity) (uint8_t lun, uint32_t *block_num, uint32_t *block_size);
int8_t (* IsReady) (uint8_t lun);
int8_t (* IsWriteProtected) (uint8_t lun);
int8_t (* Read) (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
int8_t (* Write)(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
int8_t (* GetMaxLun)(void);
int8_t *pInquiry;
}USBD_STORAGE_cb_TypeDef;
#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 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 7U /*!< 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,293 @@
/*!
\file usbd_msc_bbb.c
\brief USB BBB(Bulk/Bulk/Bulk) protocol core functions
\note BBB means Bulk-only transport protocol for USB MSC
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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);
usbd_mem_fops->mem_getcapacity(lun_num,usbd_mem_fops->mem_block_len,usbd_mem_fops->mem_block_size);
}
/* 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 deinitialize 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)
{
(void)(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)
{
(void)(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,399 @@
/*!
\file usbd_msc_core.c
\brief USB MSC device class core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 <string.h>
#include "usbd_enum.h"
#include "usbd_msc_bbb.h"
#include "usbd_msc_core.h"
#include "usbd_msc_mem.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 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 */
static __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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
/* USB string descriptor */
static 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,
#if defined(USE_USB_HS) && defined(USE_ULPI_PHY)
.other_speed_config_desc = (uint8_t *)&other_speed_msc_config_desc,
.qualifier_desc = (uint8_t *)&usbd_qualifier_desc,
#endif /* USE_USB_HS && USE_ULPI_PHY */
.strings = usbd_msc_strings
};
static __ALIGN_BEGIN uint8_t usbd_msc_maxlun __ALIGN_END = 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)
{
(void)config_index;
static __ALIGN_BEGIN usbd_msc_handler msc_handler __ALIGN_END;
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 deinitialize 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)
{
(void)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,815 @@
/*!
\file usbd_msc_scsi.c
\brief USB SCSI layer functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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"
/* USB mass storage page 0 inquiry data */
const uint8_t msc_page00_inquiry_data[] = {
0x00U,
0x00U,
0x00U,
(INQUIRY_PAGE00_LENGTH - 4U),
0x00U,
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_start_stop_unit(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)
{
(void)(lun);
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)
{
(void)(*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)
//{
//(void)(lun);
//(void)(*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)
//{
//(void)(lun);
//(void)(*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 * USBD_STD_INQUIRY_LENGTH];
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)
{
(void)(*params);
usbd_msc_handler *msc = (usbd_msc_handler *)udev->dev.class_data[USBD_MSC_INTERFACE];
if(usbd_mem_fops->mem_getcapacity(lun,usbd_mem_fops->mem_block_len,usbd_mem_fops->mem_block_size) != 0){
scsi_sense_code(udev, lun, NOT_READY, MEDIUM_NOT_PRESENT);
return -1;
}
uint32_t blk_num = usbd_mem_fops->mem_block_len[lun] - 1U;
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)
{
(void)(*params);
uint16_t i = 0U;
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;
}
if(usbd_mem_fops->mem_getcapacity(lun,usbd_mem_fops->mem_block_len,usbd_mem_fops->mem_block_size) != 0){
scsi_sense_code(udev, lun, NOT_READY, MEDIUM_NOT_PRESENT);
return -1;
}
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;
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)
{
(void)(*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];
}
if(usbd_mem_fops->mem_protected(lun)!=0){
msc->bbb_data[2] = msc->bbb_data[2]|(1<<7);
}
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)
{
(void)(lun);
(void)(*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];
}
if(usbd_mem_fops->mem_protected(lun)!=0){
msc->bbb_data[3] = msc->bbb_data[3]|(1<<7);
}
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)
{
(void)(lun);
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)
{
(void)(lun);
(void)(*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)
//{
//(void)(lun);
//(void)(*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*/
}
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_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/msc->scsi_blk_size[lun], \
(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/msc->scsi_blk_size[lun], \
(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)
//{
//(void)(*udev);
//(void)(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)
//{
//(void)(*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;
//}
@@ -0,0 +1,78 @@
/*!
\file printer_core.h
\brief the header file of USB printer device class core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 PRINTER_CORE_H
#define PRINTER_CORE_H
#include "usbd_enum.h"
/* USB printing device class code */
#define USB_CLASS_PRINTER 0x07U
/* printing device subclass code */
#define USB_SUBCLASS_PRINTER 0x01U
/* printing device protocol code */
#define PROTOCOL_UNIDIRECTIONAL_ITF 0x01U /*!< unidirectional interface */
#define PROTOCOL_BI_DIRECTIONAL_ITF 0x02U /*!< BI directional interface */
#define PROTOCOL_1284_4_ITF 0x03U /*!< 1284.4 interface */
#define PROTOCOL_VENDOR 0xFFU /*!< vendor */
/* device id length */
#define DEVICE_ID_LEN 103U
/* printer configuration descriptor length */
#define USB_PRINTER_CONFIG_DESC_LEN 32U
/* printing device specific-class request */
#define GET_DEVICE_ID 0x00U /*!< get device id request */
#define GET_PORT_STATUS 0x01U /*!< get port status request */
#define SOFT_RESET 0x02U /*!< soft reset request */
#pragma pack(1)
/* USB configuration descriptor structure */
typedef struct {
usb_desc_config config; /*!< printer configuration descriptor */
usb_desc_itf printer_itf; /*!< printer interface descriptor */
usb_desc_ep printer_epin; /*!< endpoint IN descriptor */
usb_desc_ep printer_epout; /*!< endpoint OUT descriptor */
} usb_printer_desc_config_set;
#pragma pack()
extern usb_desc printer_desc;
extern usb_class_core usbd_printer_cb;
#endif /* PRINTER_CORE_H */
@@ -0,0 +1,302 @@
/*!
\file printer_core.c
\brief USB printer device class core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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 "printer_core.h"
#define USBD_VID 0x28E9U
#define USBD_PID 0x028DU
/* printer port status: paper not empty/selected/no error */
static uint8_t g_port_status = 0x18U;
uint8_t g_printer_data_buf[PRINTER_OUT_PACKET];
__ALIGN_BEGIN uint8_t PRINTER_DEVICE_ID[DEVICE_ID_LEN] __ALIGN_END = {
0x00U, 0x67U,
'M', 'A', 'N', 'U', 'F', 'A', 'C', 'T', 'U', 'R', 'E', 'R', ':',
'G', 'I', 'G', 'A', ' ', 'D', 'E', 'V', 'I', 'C', 'E', '-', ';',
'C', 'O', 'M', 'M', 'A', 'N', 'D', ' ', 'S', 'E', 'T', ':',
'P', 'C', 'L', ',', 'M', 'P', 'L', ';',
'M', 'O', 'D', 'E', 'L', ':',
'L', 'a', 's', 'e', 'r', 'B', 'e', 'a', 'm', '?', ';',
'C', 'O', 'M', 'M', 'E', 'N', 'T', ':',
'G', 'o', 'o', 'd', ' ', '!', ';',
'A', 'C', 'T', 'I', 'V', 'E', ' ', 'C', 'O', 'M', 'M', 'A', 'N', 'D', ' ', 'S', 'E', 'T', ':',
'P', 'C', 'L', ';'
};
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev printer_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_printer_desc_config_set printer_config_desc __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = USB_PRINTER_CONFIG_DESC_LEN,
.bNumInterfaces = 0x01U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0xA0U,
.bMaxPower = 0x32U
},
.printer_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x02U,
.bInterfaceClass = USB_CLASS_PRINTER,
.bInterfaceSubClass = USB_SUBCLASS_PRINTER,
.bInterfaceProtocol = PROTOCOL_BI_DIRECTIONAL_ITF,
.iInterface = 0x00U
},
.printer_epin =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = PRINTER_IN_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = PRINTER_IN_PACKET,
.bInterval = 0x00U
},
.printer_epout =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = PRINTER_OUT_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = PRINTER_OUT_PACKET,
.bInterval = 0x00U
},
};
/* USB language ID Descriptor */
static __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 */
static __ALIGN_BEGIN 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 */
static __ALIGN_BEGIN const usb_desc_str product_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(16U),
.bDescriptorType = USB_DESCTYPE_STR
},
.unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'P', 'r', 'i', 'n', 't', 'e', 'r'}
};
/* USBD serial string */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR
}
};
/* USB string descriptor */
static void *const usbd_printer_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 printer_desc = {
.dev_desc = (uint8_t *)&printer_dev_desc,
.config_desc = (uint8_t *)&printer_config_desc,
.strings = usbd_printer_strings
};
/* local function prototypes ('static') */
static uint8_t printer_init(usb_dev *udev, uint8_t config_index);
static uint8_t printer_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t printer_req(usb_dev *udev, usb_req *req);
static uint8_t printer_in(usb_dev *udev, uint8_t ep_num);
static uint8_t printer_out(usb_dev *udev, uint8_t ep_num);
usb_class_core usbd_printer_cb = {
.init = printer_init,
.deinit = printer_deinit,
.req_proc = printer_req,
.data_in = printer_in,
.data_out = printer_out
};
/*!
\brief initialize the printer 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 printer_init(usb_dev *udev, uint8_t config_index)
{
/* initialize the data TX endpoint */
usbd_ep_setup(udev, &(printer_config_desc.printer_epin));
/* initialize the data RX endpoint */
usbd_ep_setup(udev, &(printer_config_desc.printer_epout));
/* prepare to receive data */
usbd_ep_recev(udev, PRINTER_OUT_EP, g_printer_data_buf, PRINTER_OUT_PACKET);
return USBD_OK;
}
/*!
\brief deinitialize the printer 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 printer_deinit(usb_dev *udev, uint8_t config_index)
{
/* deinitialize the data TX/RX endpoint */
usbd_ep_clear(udev, PRINTER_IN_EP);
usbd_ep_clear(udev, PRINTER_OUT_EP);
return USBD_OK;
}
/*!
\brief handle the printer class-specific 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 printer_req(usb_dev *udev, usb_req *req)
{
usb_transc *transc = &udev->dev.transc_in[0];
switch(req->bRequest) {
case GET_DEVICE_ID:
transc->xfer_buf = (uint8_t *)PRINTER_DEVICE_ID;
transc->remain_len = DEVICE_ID_LEN;
break;
case GET_PORT_STATUS:
transc->xfer_buf = (uint8_t *)&g_port_status;
transc->remain_len = 1U;
break;
case SOFT_RESET:
usbd_ep_recev(udev, PRINTER_OUT_EP, g_printer_data_buf, PRINTER_OUT_PACKET);
break;
default:
return USBD_FAIL;
}
return USBD_OK;
}
/*!
\brief handle printer data
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static uint8_t printer_in(usb_dev *udev, uint8_t ep_num)
{
return USBD_OK;
}
/*!
\brief handle printer data
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static uint8_t printer_out(usb_dev *udev, uint8_t ep_num)
{
return USBD_OK;
}
@@ -0,0 +1,102 @@
/*!
\file usbd_core.h
\brief USB device mode core functions prototype
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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_CORE_H
#define USBD_CORE_H
#include "drv_usb_core.h"
#include "drv_usb_dev.h"
typedef enum {
USBD_OK = 0U, /*!< status OK */
USBD_BUSY, /*!< status busy */
USBD_FAIL /*!< status fail */
} usbd_status;
enum _usbd_status {
USBD_DEFAULT = 1U, /*!< default status */
USBD_ADDRESSED = 2U, /*!< address send status */
USBD_CONFIGURED = 3U, /*!< configured status */
USBD_SUSPENDED = 4U /*!< suspended status */
};
/* static inline function definitions */
/*!
\brief set USB device address
\param[in] udev: pointer to USB core instance
\param[in] addr: device address to set
\param[out] none
\retval none
*/
__STATIC_INLINE void usbd_addr_set(usb_core_driver *udev, uint8_t addr)
{
usb_devaddr_set(udev, addr);
}
/*!
\brief get the received data length
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval received data length
*/
__STATIC_INLINE uint16_t usbd_rxcount_get(usb_core_driver *udev, uint8_t ep_num)
{
return (uint16_t)udev->dev.transc_out[ep_num].xfer_count;
}
/* function declarations */
/* initializes the USB device-mode stack and load the class driver */
void usbd_init(usb_core_driver *udev, usb_core_enum core, usb_desc *desc, usb_class_core *class_core);
/* endpoint initialization */
uint32_t usbd_ep_setup(usb_core_driver *udev, const usb_desc_ep *ep_desc);
/* configure the endpoint when it is disabled */
uint32_t usbd_ep_clear(usb_core_driver *udev, uint8_t ep_addr);
/* endpoint prepare to receive data */
uint32_t usbd_ep_recev(usb_core_driver *udev, uint8_t ep_addr, uint8_t *pbuf, uint32_t len);
/* endpoint prepare to transmit data */
uint32_t usbd_ep_send(usb_core_driver *udev, uint8_t ep_addr, uint8_t *pbuf, uint32_t len);
/* set an endpoint to STALL status */
uint32_t usbd_ep_stall(usb_core_driver *udev, uint8_t ep_addr);
/* clear endpoint STALLed status */
uint32_t usbd_ep_stall_clear(usb_core_driver *udev, uint8_t ep_addr);
/* flush the endpoint FIFOs */
uint32_t usbd_fifo_flush(usb_core_driver *udev, uint8_t ep_addr);
/* device connect */
void usbd_connect(usb_core_driver *udev);
/* device disconnect */
void usbd_disconnect(usb_core_driver *udev);
#endif /* USBD_CORE_H */
@@ -0,0 +1,98 @@
/*!
\file usbd_enum.h
\brief USB enumeration definitions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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_ENUM_H
#define USBD_ENUM_H
#include "usbd_core.h"
#include <wchar.h>
typedef enum _usb_reqsta {
REQ_SUPP = 0x0U, /*!< request support */
REQ_NOTSUPP = 0x1U /*!< request not support */
} usb_reqsta;
/* string descriptor index */
enum _str_index {
STR_IDX_LANGID = 0x00U, /*!< language ID string index */
STR_IDX_MFC = 0x01U, /*!< manufacturer string index */
STR_IDX_PRODUCT = 0x02U, /*!< product string index */
STR_IDX_SERIAL = 0x03U, /*!< serial string index */
STR_IDX_CONFIG = 0x04U, /*!< configuration string index */
STR_IDX_ITF = 0x05U, /*!< interface string index */
#ifndef WINUSB_EXEMPT_DRIVER
STR_IDX_MAX = 0x0AU, /*!< string maximum index */
#else
STR_IDX_MAX = 0xEFU /*!< string maximum index */
#endif /* WINUSB_EXEMPT_DRIVER */
};
typedef enum _usb_pwrsta {
USB_PWRSTA_SELF_POWERED = 0x1U, /*!< USB is in self powered status */
USB_PWRSTA_REMOTE_WAKEUP = 0x2U /*!< USB is in remote wakeup status */
} usb_pwrsta;
typedef enum _usb_feature {
USB_FEATURE_EP_HALT = 0x0U, /*!< USB has endpoint halt feature */
USB_FEATURE_REMOTE_WAKEUP = 0x1U, /*!< USB has endpoint remote wakeup feature */
USB_FEATURE_TEST_MODE = 0x2U /*!< USB has endpoint test mode feature */
} usb_feature;
#define ENG_LANGID 0x0409U /*!< english language ID */
#define CHN_LANGID 0x0804U /*!< chinese language ID */
/* USB device exported macros */
#define CTL_EP(ep) ((0x00U == (ep)) || (0x80U == (ep)))
#define DEVICE_ID1 (0x1FFF7A10U) /*!< device ID1 */
#define DEVICE_ID2 (0x1FFF7A14U) /*!< device ID2 */
#define DEVICE_ID3 (0x1FFF7A18U) /*!< device ID3 */
#define DEVICE_ID (0x40023D00U) /*!< product reserved ID */
/* function declarations */
/* handle USB standard device request */
usb_reqsta usbd_standard_request(usb_core_driver *udev, usb_req *req);
/* handle USB device class request */
usb_reqsta usbd_class_request(usb_core_driver *udev, usb_req *req);
/* handle USB vendor request */
usb_reqsta usbd_vendor_request(usb_core_driver *udev, usb_req *req);
/* handle USB enumeration error */
void usbd_enum_error(usb_core_driver *udev, usb_req *req);
/* convert hex 32bits value into Unicode char */
void int_to_unicode(uint32_t value, uint8_t *pbuf, uint8_t len);
/* get serial string */
void serial_string_get(uint16_t *unicode_str);
#endif /* USBD_ENUM_H */
@@ -0,0 +1,56 @@
/*!
\file usbd_transc.h
\brief USB transaction core functions prototype
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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_TRANSC_H
#define USBD_TRANSC_H
#include "usbd_core.h"
/* function declarations */
/* USB send data in the control transaction */
usbd_status usbd_ctl_send(usb_core_driver *udev);
/* USB receive data in the control transaction */
usbd_status usbd_ctl_recev(usb_core_driver *udev);
/* USB send control transaction status */
usbd_status usbd_ctl_status_send(usb_core_driver *udev);
/* USB control receive status */
usbd_status usbd_ctl_status_recev(usb_core_driver *udev);
/* USB SETUP stage processing */
uint8_t usbd_setup_transc(usb_core_driver *udev);
/* data OUT stage processing */
uint8_t usbd_out_transc(usb_core_driver *udev, uint8_t ep_num);
/* data IN stage processing */
uint8_t usbd_in_transc(usb_core_driver *udev, uint8_t ep_num);
#endif /* USBD_TRANSC_H */
@@ -0,0 +1,320 @@
/*!
\file usbd_core.c
\brief USB device mode core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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_core.h"
#include "usbd_enum.h"
#include "drv_usb_hw.h"
/* endpoint type */
const uint32_t ep_type[] = {
[USB_EP_ATTR_CTL] = (uint32_t)USB_EPTYPE_CTRL,
[USB_EP_ATTR_BULK] = (uint32_t)USB_EPTYPE_BULK,
[USB_EP_ATTR_INT] = (uint32_t)USB_EPTYPE_INTR,
[USB_EP_ATTR_ISO] = (uint32_t)USB_EPTYPE_ISOC
};
/*!
\brief initializes the USB device-mode stack and load the class driver
\param[in] udev: pointer to USB core instance
\param[in] core: USB core type
\param[in] desc: pointer to USB descriptor
\param[in] class_core: class driver
\param[out] none
\retval none
*/
void usbd_init(usb_core_driver *udev, usb_core_enum core, usb_desc *desc, usb_class_core *class_core)
{
udev->dev.desc = desc;
/* class callbacks */
udev->dev.class_core = class_core;
/* create serial string */
serial_string_get(udev->dev.desc->strings[STR_IDX_SERIAL]);
/* configure USB capabilities */
(void)usb_basic_init(&udev->bp, &udev->regs, core);
usb_globalint_disable(&udev->regs);
/* initializes the USB core*/
(void)usb_core_init(udev->bp, &udev->regs);
/* set device disconnect */
usbd_disconnect(udev);
#ifndef USE_OTG_MODE
usb_curmode_set(&udev->regs, DEVICE_MODE);
#endif /* USE_OTG_MODE */
/* initializes device mode */
(void)usb_devcore_init(udev);
usb_globalint_enable(&udev->regs);
/* set device connect */
usbd_connect(udev);
udev->dev.cur_status = (uint8_t)USBD_DEFAULT;
}
/*!
\brief endpoint initialization
\param[in] udev: pointer to USB core instance
\param[in] ep_desc: pointer to endpoint descriptor
\param[out] none
\retval none
*/
uint32_t usbd_ep_setup(usb_core_driver *udev, const usb_desc_ep *ep_desc)
{
usb_transc *transc;
uint8_t ep_addr = ep_desc->bEndpointAddress;
uint16_t max_len = ep_desc->wMaxPacketSize & EP_MAX_PACKET_SIZE_MASK;
/* set endpoint direction */
if(EP_DIR(ep_addr)) {
transc = &udev->dev.transc_in[EP_ID(ep_addr)];
transc->ep_addr.dir = 1U;
} else {
transc = &udev->dev.transc_out[ep_addr];
transc->ep_addr.dir = 0U;
}
transc->ep_addr.num = EP_ID(ep_addr);
transc->max_len = max_len;
transc->ep_type = (uint8_t)ep_type[ep_desc->bmAttributes & (uint8_t)USB_EPTYPE_MASK];
/* active USB endpoint function */
(void)usb_transc_active(udev, transc);
return 0U;
}
/*!
\brief configure the endpoint when it is disabled
\param[in] udev: pointer to USB core instance
\param[in] ep_addr: endpoint address
in this parameter:
bit0..bit6: endpoint number (0..7)
bit7: endpoint direction which can be IN(1) or OUT(0)
\param[out] none
\retval none
*/
uint32_t usbd_ep_clear(usb_core_driver *udev, uint8_t ep_addr)
{
usb_transc *transc;
if(EP_DIR(ep_addr)) {
transc = &udev->dev.transc_in[EP_ID(ep_addr)];
} else {
transc = &udev->dev.transc_out[ep_addr];
}
/* deactivate USB endpoint function */
(void)usb_transc_deactivate(udev, transc);
return 0U;
}
/*!
\brief endpoint prepare to receive data
\param[in] udev: pointer to USB core instance
\param[in] ep_addr: endpoint address
in this parameter:
bit0..bit6: endpoint number (0..7)
bit7: endpoint direction which can be IN(1) or OUT(0)
\param[in] pbuf: user buffer address pointer
\param[in] len: buffer length
\param[out] none
\retval none
*/
uint32_t usbd_ep_recev(usb_core_driver *udev, uint8_t ep_addr, uint8_t *pbuf, uint32_t len)
{
usb_transc *transc = &udev->dev.transc_out[EP_ID(ep_addr)];
/* setup the transfer */
transc->xfer_buf = pbuf;
transc->xfer_len = len;
transc->xfer_count = 0U;
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
transc->dma_addr = (uint32_t)pbuf;
}
/* start the transfer */
(void)usb_transc_outxfer(udev, transc);
return 0U;
}
/*!
\brief endpoint prepare to transmit data
\param[in] udev: pointer to USB core instance
\param[in] ep_addr: endpoint address
in this parameter:
bit0..bit6: endpoint number (0..7)
bit7: endpoint direction which can be IN(1) or OUT(0)
\param[in] pbuf: transmit buffer address pointer
\param[in] len: buffer length
\param[out] none
\retval none
*/
uint32_t usbd_ep_send(usb_core_driver *udev, uint8_t ep_addr, uint8_t *pbuf, uint32_t len)
{
usb_transc *transc = &udev->dev.transc_in[EP_ID(ep_addr)];
/* setup the transfer */
transc->xfer_buf = pbuf;
transc->xfer_len = len;
transc->xfer_count = 0U;
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
transc->dma_addr = (uint32_t)pbuf;
}
/* start the transfer */
(void)usb_transc_inxfer(udev, transc);
return 0U;
}
/*!
\brief set an endpoint to STALL status
\param[in] udev: pointer to USB core instance
\param[in] ep_addr: endpoint address
in this parameter:
bit0..bit6: endpoint number (0..7)
bit7: endpoint direction which can be IN(1) or OUT(0)
\param[out] none
\retval none
*/
uint32_t usbd_ep_stall(usb_core_driver *udev, uint8_t ep_addr)
{
usb_transc *transc = NULL;
if(EP_DIR(ep_addr)) {
transc = &udev->dev.transc_in[EP_ID(ep_addr)];
} else {
transc = &udev->dev.transc_out[ep_addr];
}
transc->ep_stall = 1U;
(void)usb_transc_stall(udev, transc);
return (0U);
}
/*!
\brief clear endpoint STALLed status
\param[in] udev: pointer to USB core instance
\param[in] ep_addr: endpoint address
in this parameter:
bit0..bit6: endpoint number (0..7)
bit7: endpoint direction which can be IN(1) or OUT(0)
\param[out] none
\retval none
*/
uint32_t usbd_ep_stall_clear(usb_core_driver *udev, uint8_t ep_addr)
{
usb_transc *transc = NULL;
if(EP_DIR(ep_addr)) {
transc = &udev->dev.transc_in[EP_ID(ep_addr)];
} else {
transc = &udev->dev.transc_out[ep_addr];
}
transc->ep_stall = 0U;
(void)usb_transc_clrstall(udev, transc);
return (0U);
}
/*!
\brief flush the endpoint FIFOs
\param[in] udev: pointer to USB core instance
\param[in] ep_addr: endpoint address
in this parameter:
bit0..bit6: endpoint number (0..7)
bit7: endpoint direction which can be IN(1) or OUT(0)
\param[out] none
\retval none
*/
uint32_t usbd_fifo_flush(usb_core_driver *udev, uint8_t ep_addr)
{
if(EP_DIR(ep_addr)) {
(void)usb_txfifo_flush(&udev->regs, EP_ID(ep_addr));
} else {
(void)usb_rxfifo_flush(&udev->regs);
}
return (0U);
}
/*!
\brief device connect
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void usbd_connect(usb_core_driver *udev)
{
#ifndef USE_OTG_MODE
/* connect device */
usb_dev_connect(udev);
usb_mdelay(3U);
#endif /* USE_OTG_MODE */
}
/*!
\brief device disconnect
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void usbd_disconnect(usb_core_driver *udev)
{
#ifndef USE_OTG_MODE
/* disconnect device for 3ms */
usb_dev_disconnect(udev);
usb_mdelay(3U);
#endif /* USE_OTG_MODE */
}
@@ -0,0 +1,816 @@
/*!
\file usbd_enum.c
\brief USB enumeration function
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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"
#ifdef WINUSB_EXEMPT_DRIVER
extern usbd_status usbd_OEM_req(usb_dev *udev, usb_req *req);
#endif /* WINUSB_EXEMPT_DRIVER */
/* local function prototypes ('static') */
static usb_reqsta _usb_std_reserved(usb_core_driver *udev, usb_req *req);
static uint8_t *_usb_dev_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len);
static uint8_t *_usb_config_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len);
#if defined(USE_USB_HS) && defined(USE_ULPI_PHY)
static uint8_t *_usb_other_speed_config_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len);
static uint8_t *_usb_qualifier_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len);
#endif
static uint8_t *_usb_bos_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len);
static uint8_t *_usb_str_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len);
static usb_reqsta _usb_std_getstatus(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_clearfeature(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_setfeature(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_setaddress(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_getdescriptor(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_setdescriptor(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_getconfiguration(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_setconfiguration(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_getinterface(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_setinterface(usb_core_driver *udev, usb_req *req);
static usb_reqsta _usb_std_synchframe(usb_core_driver *udev, usb_req *req);
static usb_reqsta(*_std_dev_req[])(usb_core_driver *udev, usb_req *req) = {
[USB_GET_STATUS] = _usb_std_getstatus,
[USB_CLEAR_FEATURE] = _usb_std_clearfeature,
[USB_RESERVED2] = _usb_std_reserved,
[USB_SET_FEATURE] = _usb_std_setfeature,
[USB_RESERVED4] = _usb_std_reserved,
[USB_SET_ADDRESS] = _usb_std_setaddress,
[USB_GET_DESCRIPTOR] = _usb_std_getdescriptor,
[USB_SET_DESCRIPTOR] = _usb_std_setdescriptor,
[USB_GET_CONFIGURATION] = _usb_std_getconfiguration,
[USB_SET_CONFIGURATION] = _usb_std_setconfiguration,
[USB_GET_INTERFACE] = _usb_std_getinterface,
[USB_SET_INTERFACE] = _usb_std_setinterface,
[USB_SYNCH_FRAME] = _usb_std_synchframe,
};
/* get standard descriptor handler */
static uint8_t *(*std_desc_get[])(usb_core_driver *udev, uint8_t index, uint16_t *len) = {
[(uint8_t)USB_DESCTYPE_DEV - 1U] = _usb_dev_desc_get,
[(uint8_t)USB_DESCTYPE_CONFIG - 1U] = _usb_config_desc_get,
[(uint8_t)USB_DESCTYPE_STR - 1U] = _usb_str_desc_get,
#if defined(USE_USB_HS) && defined(USE_ULPI_PHY)
[(uint8_t)USB_DESCTYPE_DEV_QUALIFIER - 3U] = _usb_qualifier_desc_get,
[(uint8_t)USB_DESCTYPE_OTHER_SPD_CONFIG - 3U] = _usb_other_speed_config_desc_get,
#endif
};
/*!
\brief handle USB standard device request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
usb_reqsta usbd_standard_request(usb_core_driver *udev, usb_req *req)
{
return (*_std_dev_req[req->bRequest])(udev, req);
}
/*!
\brief handle USB device class request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device class request
\param[out] none
\retval USB device request status
*/
usb_reqsta usbd_class_request(usb_core_driver *udev, usb_req *req)
{
if((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) {
if(BYTE_LOW(req->wIndex) <= USBD_ITF_MAX_NUM) {
/* call device class handle function */
return (usb_reqsta)udev->dev.class_core->req_proc(udev, req);
}
}
return REQ_NOTSUPP;
}
/*!
\brief handle USB vendor request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB vendor request
\param[out] none
\retval USB device request status
*/
usb_reqsta usbd_vendor_request(usb_core_driver *udev, usb_req *req)
{
(void)udev;
(void)req;
/* added by user... */
#ifdef WINUSB_EXEMPT_DRIVER
usbd_OEM_req(udev, req);
#endif
return REQ_SUPP;
}
/*!
\brief handle USB enumeration error
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval none
*/
void usbd_enum_error(usb_core_driver *udev, usb_req *req)
{
(void)req;
(void)usbd_ep_stall(udev, 0x80U);
(void)usbd_ep_stall(udev, 0x00U);
usb_ctlep_startout(udev);
}
/*!
\brief convert hex 32bits value into Unicode char
\param[in] value: hex 32bits value
\param[in] pbuf: buffer pointer to store Unicode char
\param[in] len: value length
\param[out] none
\retval none
*/
void int_to_unicode(uint32_t value, uint8_t *pbuf, uint8_t len)
{
uint8_t index;
for(index = 0U; index < len; index++) {
if((value >> 28) < 0x0AU) {
pbuf[2 * index] = (uint8_t)((value >> 28) + '0');
} else {
pbuf[2 * index] = (uint8_t)((value >> 28) + 'A' - 10U);
}
value = value << 4;
pbuf[2U * index + 1U] = 0U;
}
}
/*!
\brief convert hex 32bits value into Unicode char
\param[in] unicode_str: pointer to Unicode string
\param[out] none
\retval none
*/
void serial_string_get(uint16_t *unicode_str)
{
if(6U != (unicode_str[0] & 0x00FFU)) {
uint32_t DeviceSerial0, DeviceSerial1, DeviceSerial2;
DeviceSerial0 = *(uint32_t *)DEVICE_ID1;
DeviceSerial1 = *(uint32_t *)DEVICE_ID2;
DeviceSerial2 = *(uint32_t *)DEVICE_ID3;
DeviceSerial0 += DeviceSerial2;
if(0U != DeviceSerial0) {
int_to_unicode(DeviceSerial0, (uint8_t *)&(unicode_str[1]), 8U);
int_to_unicode(DeviceSerial1, (uint8_t *)&(unicode_str[9]), 4U);
}
} else {
uint32_t device_serial = *(uint32_t *)DEVICE_ID;
if(0U != device_serial) {
unicode_str[1] = (uint16_t)(device_serial & 0x0000FFFFU);
unicode_str[2] = (uint16_t)((device_serial & 0xFFFF0000U) >> 16);
}
}
}
/*!
\brief no operation, just for reserved
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB vendor request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_reserved(usb_core_driver *udev, usb_req *req)
{
(void)udev;
(void)req;
/* no operation... */
return REQ_NOTSUPP;
}
/*!
\brief get the device descriptor
\param[in] udev: pointer to USB device instance
\param[in] index: no use
\param[out] len: data length pointer
\retval descriptor buffer pointer
*/
static uint8_t *_usb_dev_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len)
{
(void)index;
*len = udev->dev.desc->dev_desc[0];
return udev->dev.desc->dev_desc;
}
/*!
\brief get the configuration descriptor
\brief[in] udev: pointer to USB device instance
\brief[in] index: no use
\param[out] len: data length pointer
\retval descriptor buffer pointer
*/
static uint8_t *_usb_config_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len)
{
(void)index;
*len = udev->dev.desc->config_desc[2] | (udev->dev.desc->config_desc[3] << 8);
return udev->dev.desc->config_desc;
}
#if defined(USE_USB_HS) && defined(USE_ULPI_PHY)
/*!
\brief get the other speed configuration descriptor
\brief[in] udev: pointer to USB device instance
\brief[in] index: no use
\param[out] len: data length pointer
\retval descriptor buffer pointer
*/
static uint8_t *_usb_other_speed_config_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len)
{
(void)index;
*len = udev->dev.desc->other_speed_config_desc[2];
return udev->dev.desc->other_speed_config_desc;
}
/*!
\brief get the other speed configuration descriptor
\brief[in] udev: pointer to USB device instance
\brief[in] index: no use
\param[out] len: data length pointer
\retval descriptor buffer pointer
*/
static uint8_t *_usb_qualifier_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len)
{
(void)index;
*len = udev->dev.desc->qualifier_desc[0];
return udev->dev.desc->qualifier_desc;
}
#endif /* USE_USB_HS && USE_ULPI_PHY */
/*!
\brief get the BOS descriptor
\brief[in] udev: pointer to USB device instance
\brief[in] index: no use
\param[out] len: data length pointer
\retval descriptor buffer pointer
*/
static uint8_t *_usb_bos_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len)
{
(void)index;
*len = udev->dev.desc->bos_desc[2];
return udev->dev.desc->bos_desc;
}
/*!
\brief get string descriptor
\param[in] udev: pointer to USB device instance
\param[in] index: string descriptor index
\param[out] len: pointer to string length
\retval descriptor buffer pointer
*/
static uint8_t *_usb_str_desc_get(usb_core_driver *udev, uint8_t index, uint16_t *len)
{
uint8_t *desc = udev->dev.desc->strings[index];
*len = desc[0];
return desc;
}
/*!
\brief handle Get_Status request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_getstatus(usb_core_driver *udev, usb_req *req)
{
uint8_t recp = BYTE_LOW(req->wIndex);
usb_reqsta req_status = REQ_NOTSUPP;
usb_transc *transc = &udev->dev.transc_in[0];
static uint8_t status[2] = {0};
switch(req->bmRequestType & (uint8_t)USB_RECPTYPE_MASK) {
case USB_RECPTYPE_DEV:
if(((uint8_t)USBD_ADDRESSED == udev->dev.cur_status) || \
((uint8_t)USBD_CONFIGURED == udev->dev.cur_status)) {
if(udev->dev.pm.power_mode) {
status[0] = USB_STATUS_SELF_POWERED;
} else {
status[0] = 0U;
}
if(udev->dev.pm.dev_remote_wakeup) {
status[0] |= USB_STATUS_REMOTE_WAKEUP;
} else {
status[0] = 0U;
}
req_status = REQ_SUPP;
}
break;
case USB_RECPTYPE_ITF:
if(((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) && (recp <= USBD_ITF_MAX_NUM)) {
req_status = REQ_SUPP;
}
break;
case USB_RECPTYPE_EP:
if((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) {
if(0x80U == (recp & 0x80U)) {
status[0] = udev->dev.transc_in[EP_ID(recp)].ep_stall;
} else {
status[0] = udev->dev.transc_out[recp].ep_stall;
}
req_status = REQ_SUPP;
}
break;
default:
break;
}
if(REQ_SUPP == req_status) {
transc->xfer_buf = status;
transc->remain_len = 2U;
}
return req_status;
}
/*!
\brief handle USB Clear_Feature request
\param[in] udev: pointer to USB device instance
\param[in] req: USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_clearfeature(usb_core_driver *udev, usb_req *req)
{
uint8_t ep = 0U;
switch(req->bmRequestType & (uint8_t)USB_RECPTYPE_MASK) {
case USB_RECPTYPE_DEV:
if(((uint8_t)USBD_ADDRESSED == udev->dev.cur_status) || \
((uint8_t)USBD_CONFIGURED == udev->dev.cur_status)) {
/* clear device remote wakeup feature */
if((uint16_t)USB_FEATURE_REMOTE_WAKEUP == req->wValue) {
udev->dev.pm.dev_remote_wakeup = 0U;
return REQ_SUPP;
}
}
break;
case USB_RECPTYPE_ITF:
break;
case USB_RECPTYPE_EP:
/* get endpoint address */
ep = BYTE_LOW(req->wIndex);
if((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) {
/* clear endpoint halt feature */
if(((uint16_t)USB_FEATURE_EP_HALT == req->wValue) && (!CTL_EP(ep))) {
(void)usbd_ep_stall_clear(udev, ep);
(void)udev->dev.class_core->req_proc(udev, req);
}
return REQ_SUPP;
}
break;
default:
break;
}
return REQ_NOTSUPP;
}
/*!
\brief handle USB Set_Feature request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_setfeature(usb_core_driver *udev, usb_req *req)
{
uint8_t ep = 0U;
switch(req->bmRequestType & (uint8_t)USB_RECPTYPE_MASK) {
case USB_RECPTYPE_DEV:
if(((uint8_t)USBD_ADDRESSED == udev->dev.cur_status) || \
((uint8_t)USBD_CONFIGURED == udev->dev.cur_status)) {
/* set device remote wakeup feature */
if((uint16_t)USB_FEATURE_REMOTE_WAKEUP == req->wValue) {
udev->dev.pm.dev_remote_wakeup = 1U;
}
return REQ_SUPP;
}
break;
case USB_RECPTYPE_ITF:
break;
case USB_RECPTYPE_EP:
/* get endpoint address */
ep = BYTE_LOW(req->wIndex);
if((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) {
/* set endpoint halt feature */
if(((uint16_t)USB_FEATURE_EP_HALT == req->wValue) && (!CTL_EP(ep))) {
(void)usbd_ep_stall(udev, ep);
}
return REQ_SUPP;
}
break;
default:
break;
}
return REQ_NOTSUPP;
}
/*!
\brief handle USB Set_Address request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_setaddress(usb_core_driver *udev, usb_req *req)
{
if((0U == req->wIndex) && (0U == req->wLength)) {
udev->dev.dev_addr = (uint8_t)(req->wValue) & 0x7FU;
if((uint8_t)USBD_CONFIGURED != udev->dev.cur_status) {
usbd_addr_set(udev, udev->dev.dev_addr);
if(udev->dev.dev_addr) {
udev->dev.cur_status = (uint8_t)USBD_ADDRESSED;
} else {
udev->dev.cur_status = (uint8_t)USBD_DEFAULT;
}
return REQ_SUPP;
}
}
return REQ_NOTSUPP;
}
/*!
\brief handle USB Get_Descriptor request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_getdescriptor(usb_core_driver *udev, usb_req *req)
{
uint8_t desc_type = 0U;
uint8_t desc_index = 0U;
usb_reqsta status = REQ_NOTSUPP;
usb_transc *transc = &udev->dev.transc_in[0];
/* get device standard descriptor */
switch(req->bmRequestType & USB_RECPTYPE_MASK) {
case USB_RECPTYPE_DEV:
desc_type = BYTE_HIGH(req->wValue);
desc_index = BYTE_LOW(req->wValue);
switch(desc_type) {
case USB_DESCTYPE_DEV:
transc->xfer_buf = std_desc_get[desc_type - 1U](udev, desc_index, (uint16_t *)&(transc->remain_len));
if(64U == req->wLength) {
transc->remain_len = 8U;
}
break;
case USB_DESCTYPE_CONFIG:
transc->xfer_buf = std_desc_get[desc_type - 1U](udev, desc_index, (uint16_t *)&(transc->remain_len));
break;
case USB_DESCTYPE_STR:
if(desc_index < (uint8_t)STR_IDX_MAX) {
transc->xfer_buf = std_desc_get[desc_type - 1U](udev, desc_index, (uint16_t *)&(transc->remain_len));
}
break;
case USB_DESCTYPE_ITF:
case USB_DESCTYPE_EP:
break;
#if defined(USE_USB_HS) && defined(USE_ULPI_PHY)
case USB_DESCTYPE_DEV_QUALIFIER:
transc->xfer_buf = std_desc_get[desc_type - 3U](udev, desc_index, (uint16_t *)&(transc->remain_len));
break;
case USB_DESCTYPE_OTHER_SPD_CONFIG:
transc->xfer_buf = std_desc_get[desc_type - 3U](udev, desc_index, (uint16_t *)&(transc->remain_len));
break;
#endif
case USB_DESCTYPE_ITF_POWER:
break;
case USB_DESCTYPE_BOS:
transc->xfer_buf = _usb_bos_desc_get(udev, desc_index, (uint16_t *)&(transc->remain_len));
break;
default:
break;
}
break;
case USB_RECPTYPE_ITF:
/* get device class special descriptor */
status = (usb_reqsta)(udev->dev.class_core->req_proc(udev, req));
break;
case USB_RECPTYPE_EP:
break;
default:
break;
}
if((0U != transc->remain_len) && (0U != req->wLength)) {
if(transc->remain_len < req->wLength) {
if((transc->remain_len >= transc->max_len) && (0U == (transc->remain_len % transc->max_len))) {
udev->dev.control.ctl_zlp = 1U;
}
} else {
transc->remain_len = req->wLength;
}
status = REQ_SUPP;
}
return status;
}
/*!
\brief handle USB Set_Descriptor request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_setdescriptor(usb_core_driver *udev, usb_req *req)
{
(void)udev;
(void)req;
/* no handle... */
return REQ_SUPP;
}
/*!
\brief handle USB Get_Configuration request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_getconfiguration(usb_core_driver *udev, usb_req *req)
{
(void)req;
usb_reqsta req_status = REQ_NOTSUPP;
usb_transc *transc = &udev->dev.transc_in[0];
switch(udev->dev.cur_status) {
case USBD_ADDRESSED:
if(USB_DEFAULT_CONFIG == udev->dev.config) {
req_status = REQ_SUPP;
}
break;
case USBD_CONFIGURED:
if(USB_DEFAULT_CONFIG != udev->dev.config) {
req_status = REQ_SUPP;
}
break;
default:
break;
}
if(REQ_SUPP == req_status) {
transc->xfer_buf = &(udev->dev.config);
transc->remain_len = 1U;
}
return req_status;
}
/*!
\brief handle USB Set_Configuration request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_setconfiguration(usb_core_driver *udev, usb_req *req)
{
static uint8_t config;
usb_reqsta status = REQ_NOTSUPP;
config = (uint8_t)(req->wValue);
if(config <= USBD_CFG_MAX_NUM) {
switch(udev->dev.cur_status) {
case USBD_ADDRESSED:
if(config) {
(void)udev->dev.class_core->init(udev, config);
udev->dev.config = config;
udev->dev.cur_status = (uint8_t)USBD_CONFIGURED;
}
status = REQ_SUPP;
break;
case USBD_CONFIGURED:
if(USB_DEFAULT_CONFIG == config) {
(void)udev->dev.class_core->deinit(udev, config);
udev->dev.config = config;
udev->dev.cur_status = (uint8_t)USBD_ADDRESSED;
} else if(config != udev->dev.config) {
/* clear old configuration */
(void)udev->dev.class_core->deinit(udev, config);
/* set new configuration */
udev->dev.config = config;
(void)udev->dev.class_core->init(udev, config);
} else {
/* no operation */
}
status = REQ_SUPP;
break;
case USBD_DEFAULT:
break;
default:
break;
}
}
return status;
}
/*!
\brief handle USB Get_Interface request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_getinterface(usb_core_driver *udev, usb_req *req)
{
switch(udev->dev.cur_status) {
case USBD_DEFAULT:
break;
case USBD_ADDRESSED:
break;
case USBD_CONFIGURED:
if(BYTE_LOW(req->wIndex) <= USBD_ITF_MAX_NUM) {
usb_transc *transc = &udev->dev.transc_in[0];
transc->xfer_buf = &(udev->dev.class_core->alter_set);
transc->remain_len = 1U;
return REQ_SUPP;
}
break;
default:
break;
}
return REQ_NOTSUPP;
}
/*!
\brief handle USB Set_Interface request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_setinterface(usb_core_driver *udev, usb_req *req)
{
switch(udev->dev.cur_status) {
case USBD_DEFAULT:
break;
case USBD_ADDRESSED:
break;
case USBD_CONFIGURED:
if(BYTE_LOW(req->wIndex) <= USBD_ITF_MAX_NUM) {
if(NULL != udev->dev.class_core->set_intf) {
(void)udev->dev.class_core->set_intf(udev, req);
}
return REQ_SUPP;
}
break;
default:
break;
}
return REQ_NOTSUPP;
}
/*!
\brief handle USB SynchFrame request
\param[in] udev: pointer to USB device instance
\param[in] req: pointer to USB device request
\param[out] none
\retval USB device request status
*/
static usb_reqsta _usb_std_synchframe(usb_core_driver *udev, usb_req *req)
{
(void)udev;
(void)req;
/* no handle */
return REQ_SUPP;
}
@@ -0,0 +1,264 @@
/*!
\file usbd_transc.c
\brief USB transaction core functions
\version 2024-12-20, V3.3.1, firmware for GD32F4xx
*/
/*
Copyright (c) 2024, 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_transc.h"
/*!
\brief USB send data in the control transaction
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation cur_status
*/
usbd_status usbd_ctl_send(usb_core_driver *udev)
{
usb_transc *transc = &udev->dev.transc_in[0];
(void)usbd_ep_send(udev, 0U, transc->xfer_buf, transc->remain_len);
if(transc->remain_len > transc->max_len) {
udev->dev.control.ctl_state = (uint8_t)USB_CTL_DATA_IN;
} else {
udev->dev.control.ctl_state = (uint8_t)USB_CTL_LAST_DATA_IN;
}
return USBD_OK;
}
/*!
\brief USB receive data in the control transaction
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation cur_status
*/
usbd_status usbd_ctl_recev(usb_core_driver *udev)
{
usb_transc *transc = &udev->dev.transc_out[0];
(void)usbd_ep_recev(udev, 0U, transc->xfer_buf, transc->remain_len);
if(transc->remain_len > transc->max_len) {
udev->dev.control.ctl_state = (uint8_t)USB_CTL_DATA_OUT;
} else {
udev->dev.control.ctl_state = (uint8_t)USB_CTL_LAST_DATA_OUT;
}
return USBD_OK;
}
/*!
\brief USB send control transaction status
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation cur_status
*/
usbd_status usbd_ctl_status_send(usb_core_driver *udev)
{
udev->dev.control.ctl_state = (uint8_t)USB_CTL_STATUS_IN;
(void)usbd_ep_send(udev, 0U, NULL, 0U);
usb_ctlep_startout(udev);
return USBD_OK;
}
/*!
\brief USB control receive status
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation cur_status
*/
usbd_status usbd_ctl_status_recev(usb_core_driver *udev)
{
udev->dev.control.ctl_state = (uint8_t)USB_CTL_STATUS_OUT;
(void)usbd_ep_recev(udev, 0U, NULL, 0U);
usb_ctlep_startout(udev);
return USBD_OK;
}
/*!
\brief USB SETUP stage processing
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation cur_status
*/
uint8_t usbd_setup_transc(usb_core_driver *udev)
{
usb_reqsta reqstat = REQ_NOTSUPP;
usb_req req = udev->dev.control.req;
switch(req.bmRequestType & USB_REQTYPE_MASK) {
/* standard device request */
case USB_REQTYPE_STRD:
reqstat = usbd_standard_request(udev, &req);
break;
/* device class request */
case USB_REQTYPE_CLASS:
reqstat = usbd_class_request(udev, &req);
break;
/* vendor defined request */
case USB_REQTYPE_VENDOR:
reqstat = usbd_vendor_request(udev, &req);
break;
default:
break;
}
if(REQ_SUPP == reqstat) {
if(0U == req.wLength) {
(void)usbd_ctl_status_send(udev);
} else {
if(req.bmRequestType & 0x80U) {
(void)usbd_ctl_send(udev);
} else {
(void)usbd_ctl_recev(udev);
}
}
} else {
usbd_enum_error(udev, &req);
}
return (uint8_t)USBD_OK;
}
/*!
\brief data OUT stage processing
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier(0..7)
\param[out] none
\retval USB device operation cur_status
*/
uint8_t usbd_out_transc(usb_core_driver *udev, uint8_t ep_num)
{
if(0U == ep_num) {
usb_transc *transc = &udev->dev.transc_out[0];
switch(udev->dev.control.ctl_state) {
case USB_CTL_DATA_OUT:
/* update transfer length */
transc->remain_len -= transc->max_len;
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
transc->xfer_buf += transc->max_len;
}
(void)usbd_ctl_recev(udev);
break;
case USB_CTL_LAST_DATA_OUT:
if((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) {
if(NULL != udev->dev.class_core->ctlx_out) {
(void)udev->dev.class_core->ctlx_out(udev);
}
}
transc->remain_len = 0U;
(void)usbd_ctl_status_send(udev);
break;
default:
break;
}
} else if((NULL != udev->dev.class_core->data_out) && ((uint8_t)USBD_CONFIGURED == udev->dev.cur_status)) {
(void)udev->dev.class_core->data_out(udev, ep_num);
} else {
/* no operation */
}
return (uint8_t)USBD_OK;
}
/*!
\brief data IN stage processing
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier(0..7)
\param[out] none
\retval USB device operation cur_status
*/
uint8_t usbd_in_transc(usb_core_driver *udev, uint8_t ep_num)
{
if(0U == ep_num) {
usb_transc *transc = &udev->dev.transc_in[0];
switch(udev->dev.control.ctl_state) {
case USB_CTL_DATA_IN:
/* update transfer length */
transc->remain_len -= transc->max_len;
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
transc->xfer_buf += transc->max_len;
}
(void)usbd_ctl_send(udev);
break;
case USB_CTL_LAST_DATA_IN:
/* last packet is MPS multiple, so send ZLP packet */
if(udev->dev.control.ctl_zlp) {
(void)usbd_ep_send(udev, 0U, NULL, 0U);
udev->dev.control.ctl_zlp = 0U;
} else {
if((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) {
if(NULL != udev->dev.class_core->ctlx_in) {
(void)udev->dev.class_core->ctlx_in(udev);
}
}
transc->remain_len = 0U;
(void)usbd_ctl_status_recev(udev);
}
break;
default:
break;
}
} else {
if(((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) && (NULL != udev->dev.class_core->data_in)) {
(void)udev->dev.class_core->data_in(udev, ep_num);
}
}
return (uint8_t)USBD_OK;
}