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;
}
@@ -0,0 +1,347 @@
/*!
\file drv_usb_core.h
\brief USB core low level driver 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 DRV_USB_CORE_H
#define DRV_USB_CORE_H
#include "drv_usb_regs.h"
#include "usb_ch9_std.h"
#define USB_FS_EP0_MAX_LEN 64U /*!< maximum packet size of endpoint 0 */
#define HC_MAX_PACKET_COUNT 140U /*!< maximum packet count */
#define EP_ID(x) ((uint8_t)((x) & 0x7FU)) /*!< endpoint number */
#define EP_DIR(x) ((uint8_t)((x) >> 7)) /*!< endpoint direction */
#define EP_MAX_PACKET_SIZE_MASK 0x07FFU /*!< endpoint maximum packet size mask */
enum _usb_mode {
DEVICE_MODE = 0U, /*!< device mode */
HOST_MODE, /*!< host mode */
OTG_MODE /*!< OTG mode */
};
enum _usb_eptype {
USB_EPTYPE_CTRL = 0U, /*!< control endpoint type */
USB_EPTYPE_ISOC = 1U, /*!< isochronous endpoint type */
USB_EPTYPE_BULK = 2U, /*!< bulk endpoint type */
USB_EPTYPE_INTR = 3U, /*!< interrupt endpoint type */
USB_EPTYPE_MASK = 3U /*!< endpoint type mask */
};
typedef enum {
USB_OTG_OK = 0U, /*!< USB OTG status succeed */
USB_OTG_FAIL /*!< USB OTG status fail */
} usb_otg_status;
typedef enum {
USB_OK = 0U, /*!< USB status succeed */
USB_FAIL /*!< USB status fail */
} usb_status;
typedef enum {
USB_USE_FIFO = 0U, /*!< USB use FIFO transfer mode */
USB_USE_DMA /*!< USB use DMA transfer mode */
} usb_transfer_mode;
typedef struct {
uint8_t core_enum; /*!< USB core type */
uint8_t core_speed; /*!< USB core speed */
uint8_t num_pipe; /*!< USB host channel numbers */
uint8_t num_ep; /*!< USB device endpoint numbers */
uint8_t transfer_mode; /*!< USB transfer mode */
uint8_t phy_itf; /*!< USB core PHY interface */
uint8_t sof_enable; /*!< USB SOF output */
uint8_t low_power; /*!< USB low power */
uint8_t lpm_enable; /*!< USB link power mode(LPM) */
uint8_t vbus_sensing_enable; /*!< USB VBUS sensing feature */
uint8_t use_dedicated_ep1; /*!< USB dedicated endpoint1 interrupt */
uint8_t use_external_vbus; /*!< enable or disable the use of the external VBUS */
uint32_t base_reg; /*!< base register address */
} usb_core_basic;
#ifdef USE_DEVICE_MODE
/* USB descriptor */
typedef struct _usb_desc {
uint8_t *dev_desc; /*!< device descriptor */
uint8_t *config_desc; /*!< configure descriptor */
uint8_t *bos_desc; /*!< BOS descriptor */
#if defined(USE_USB_HS) && defined(USE_ULPI_PHY)
uint8_t *other_speed_config_desc; /*!< other speed configuration descriptor */
uint8_t *qualifier_desc; /*!< qualifier descriptor */
#endif
void* const *strings; /*!< string descriptor */
} usb_desc;
/* USB power management */
typedef struct _usb_pm {
uint8_t power_mode; /*!< power mode */
uint8_t power_low; /*!< power low */
uint8_t dev_remote_wakeup; /*!< remote wakeup */
uint8_t remote_wakeup_on; /*!< remote wakeup on */
} usb_pm;
/* USB control information */
typedef struct _usb_control {
usb_req req; /*!< USB standard device request */
uint8_t ctl_state; /*!< USB control transfer state */
uint8_t ctl_zlp; /*!< zero length package */
} usb_control;
typedef struct {
struct {
uint8_t num: 4U; /*!< the endpoint number.it can be from 0 to 6 */
uint8_t pad: 3U; /*!< padding between number and direction */
uint8_t dir: 1U; /*!< the endpoint direction */
} ep_addr;
uint8_t ep_type; /*!< USB endpoint type */
uint8_t ep_stall; /*!< USB endpoint STALL status */
uint8_t frame_num; /*!< number of frame */
uint16_t max_len; /*!< maximum packet length */
/* transaction level variables */
uint8_t *xfer_buf; /*!< transmit buffer */
uint32_t xfer_len; /*!< transmit buffer length */
uint32_t xfer_count; /*!< transmit buffer count */
uint32_t remain_len; /*!< remain packet length */
uint32_t dma_addr; /*!< DMA address */
} usb_transc;
typedef struct _usb_core_driver usb_dev;
typedef struct _usb_class_core {
uint8_t command; /*!< device class request command */
uint8_t alter_set; /*!< alternative set */
uint8_t (*init)(usb_dev *udev, uint8_t config_index); /*!< initialize handler */
uint8_t (*deinit)(usb_dev *udev, uint8_t config_index); /*!< de-initialize handler */
uint8_t (*req_proc)(usb_dev *udev, usb_req *req); /*!< device request handler */
uint8_t (*set_intf)(usb_dev *udev, usb_req *req); /*!< device set interface callback */
uint8_t (*ctlx_in)(usb_dev *udev); /*!< device control IN callback */
uint8_t (*ctlx_out)(usb_dev *udev); /*!< device control OUT callback */
uint8_t (*data_in)(usb_dev *udev, uint8_t ep_num); /*!< device data IN handler */
uint8_t (*data_out)(usb_dev *udev, uint8_t ep_num); /*!< device data OUT handler */
uint8_t (*SOF)(usb_dev *udev); /*!< start of frame handler */
uint8_t (*incomplete_isoc_in)(usb_dev *udev); /*!< incomplete synchronization IN transfer handler */
uint8_t (*incomplete_isoc_out)(usb_dev *udev); /*!< incomplete synchronization OUT transfer handler */
} usb_class_core;
typedef struct _usb_perp_dev {
uint8_t config; /*!< configuration */
uint8_t dev_addr; /*!< device address */
__IO uint8_t cur_status; /*!< current status */
__IO uint8_t backup_status; /*!< backup status */
usb_transc transc_in[USBFS_MAX_TX_FIFOS]; /*!< endpoint IN transaction */
usb_transc transc_out[USBFS_MAX_TX_FIFOS]; /*!< endpoint OUT transaction */
usb_pm pm; /*!< power management */
usb_control control; /*!< USB control information */
usb_desc *desc; /*!< USB descriptors pointer */
usb_class_core *class_core; /*!< class driver */
void *class_data[6]; /*!< class data pointer */
void *user_data; /*!< user data pointer */
void *pdata; /*!< reserved data pointer */
} usb_perp_dev;
#endif /* USE_DEVICE_MODE */
#ifdef USE_HOST_MODE
typedef enum _usb_pipe_status {
PIPE_IDLE = 0U, /*!< host pipe IDLE status */
PIPE_XF, /*!< host pipe transfer completed status */
PIPE_HALTED, /*!< host pipe halted status */
PIPE_NAK, /*!< host pipe NAK status */
PIPE_NYET, /*!< host pipe NYET status */
PIPE_STALL, /*!< host pipe STALL status */
PIPE_TRACERR, /*!< host pipe transaction error status */
PIPE_BBERR, /*!< host pipe babble error status */
PIPE_REQOVR, /*!< host pipe frame overrun status */
PIPE_DTGERR /*!< host pipe data toggle error status */
} usb_pipe_status;
typedef enum _usb_pipe_mode {
PIPE_PERIOD = 0U, /*!< USB host pipe PERIOD mode */
PIPE_NON_PERIOD = 1U /*!< USB host pipe NOT PERIOD mode */
} usb_pipe_mode;
typedef enum _usb_urb_state {
URB_IDLE = 0U, /*!< USB URB IDLE state */
URB_DONE, /*!< USB URB DONE state */
URB_NOTREADY, /*!< USB URB NOT READY state */
URB_ERROR, /*!< USB URB ERROR state */
URB_STALL, /*!< USB URB STALL state */
URB_PING /*!< USB URB PING state */
} usb_urb_state;
typedef struct _usb_pipe {
uint8_t in_used; /*!< pipe used */
uint8_t dev_addr; /*!< USB device address */
uint32_t dev_speed; /*!< USB device speed */
struct {
uint8_t num; /*!< endpoint numbers */
uint8_t dir; /*!< endpoint direction */
uint8_t type; /*!< endpoint transfer type */
uint16_t mps; /*!< endpoint max packet size */
} ep;
__IO uint8_t supp_ping; /*!< host pipe support PING */
__IO uint8_t do_ping; /*!< host pipe do PING */
__IO uint32_t DPID; /*!< data PID */
uint8_t *xfer_buf; /*!< USB transfer buffer */
uint32_t xfer_len; /*!< USB transfer length */
uint32_t xfer_count; /*!< USB transfer count */
uint8_t data_toggle_in; /*!< toggle DATA IN */
uint8_t data_toggle_out; /*!< toggle DATA OUT */
__IO uint32_t err_count; /*!< error count */
__IO usb_pipe_status pp_status; /*!< USB pipe status */
__IO usb_urb_state urb_state; /*!< USB urb state */
} usb_pipe;
typedef struct _usb_host_drv {
__IO uint32_t connect_status; /*!< connect status */
__IO uint32_t port_enabled; /*!< USB port enable */
__IO uint32_t backup_xfercount[USBFS_MAX_TX_FIFOS]; /*!< USB backup transfer data count */
usb_pipe pipe[USBFS_MAX_TX_FIFOS]; /*!< USB host pipe handles */
void *data; /*!< user data pointer */
} usb_host_drv;
#endif /* USE_HOST_MODE */
typedef struct _usb_core_driver {
usb_core_basic bp; /*!< USB basic parameters */
usb_core_regs regs; /*!< USB registers */
#ifdef USE_DEVICE_MODE
usb_perp_dev dev; /*!< USB peripheral device */
#endif /* USE_DEVICE_MODE */
#ifdef USE_HOST_MODE
usb_host_drv host; /*!< USB peripheral host */
#endif /* USE_HOST_MODE */
} usb_core_driver;
/* static inline function definitions */
/*!
\brief get the global interrupts
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval interrupt status
*/
__STATIC_INLINE uint32_t usb_coreintr_get(usb_core_regs *usb_regs)
{
uint32_t reg_data = usb_regs->gr->GINTEN;
reg_data &= usb_regs->gr->GINTF;
return reg_data;
}
/*!
\brief set USB RX FIFO size
\param[in] usb_regs: pointer to USB core registers
\param[in] size: assigned FIFO size
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_set_rxfifo(usb_core_regs *usb_regs, uint16_t size)
{
usb_regs->gr->GRFLEN = size;
}
/*!
\brief enable the global interrupts
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_globalint_enable(usb_core_regs *usb_regs)
{
/* enable USB global interrupt */
usb_regs->gr->GAHBCS |= GAHBCS_GINTEN;
}
/*!
\brief disable the global interrupts
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_globalint_disable(usb_core_regs *usb_regs)
{
/* disable USB global interrupt */
usb_regs->gr->GAHBCS &= ~GAHBCS_GINTEN;
}
/* function declarations */
/* configure core capabilities */
usb_status usb_basic_init(usb_core_basic *usb_basic, usb_core_regs *usb_regs, usb_core_enum usb_core);
/* initializes the USB controller registers and prepares the core device mode or host mode operation */
usb_status usb_core_init(usb_core_basic usb_basic, usb_core_regs *usb_regs);
/* write a packet into the TX FIFO associated with the endpoint */
usb_status usb_txfifo_write(usb_core_regs *usb_regs, uint8_t *src_buf, uint8_t fifo_num, uint16_t byte_count);
/* read a packet from the RX FIFO associated with the endpoint */
void *usb_rxfifo_read(usb_core_regs *usb_regs, uint8_t *dest_buf, uint16_t byte_count);
/* flush a TX FIFO or all TX FIFOs */
usb_status usb_txfifo_flush(usb_core_regs *usb_regs, uint8_t fifo_num);
/* flush the entire RX FIFO */
usb_status usb_rxfifo_flush(usb_core_regs *usb_regs);
/* set endpoint or channel TX FIFO size */
void usb_set_txfifo(usb_core_regs *usb_regs, uint8_t fifo, uint16_t size);
/* set USB current mode */
void usb_curmode_set(usb_core_regs *usb_regs, uint8_t mode);
#endif /* DRV_USB_CORE_H */
@@ -0,0 +1,197 @@
/*!
\file drv_usb_dev.h
\brief USB device low level driver 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 DRV_USB_DEV_H
#define DRV_USB_DEV_H
#include "usbd_conf.h"
#include "drv_usb_core.h"
#define EP_IN(x) ((uint8_t)(0x80U | (x))) /*!< device IN endpoint */
#define EP_OUT(x) ((uint8_t)(x)) /*!< device OUT endpoint */
enum usb_ctl_status {
USB_CTL_IDLE = 0U, /*!< USB control transfer idle state */
USB_CTL_DATA_IN, /*!< USB control transfer data IN state */
USB_CTL_LAST_DATA_IN, /*!< USB control transfer last data IN state */
USB_CTL_DATA_OUT, /*!< USB control transfer data OUT state */
USB_CTL_LAST_DATA_OUT, /*!< USB control transfer last data OUT state */
USB_CTL_STATUS_IN, /*!< USB control transfer status IN state*/
USB_CTL_STATUS_OUT /*!< USB control transfer status OUT state */
};
/* static inline function definitions */
/*!
\brief configure the USB device to be disconnected
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_dev_disconnect(usb_core_driver *udev)
{
udev->regs.dr->DCTL |= DCTL_SD;
}
/*!
\brief configure the USB device to be connected
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_dev_connect(usb_core_driver *udev)
{
udev->regs.dr->DCTL &= ~DCTL_SD;
}
/*!
\brief set the USB device address
\param[in] udev: pointer to USB device
\param[in] dev_addr: device address for setting
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_devaddr_set(usb_core_driver *udev, uint8_t dev_addr)
{
udev->regs.dr->DCFG &= ~DCFG_DAR;
udev->regs.dr->DCFG |= (uint32_t)dev_addr << 4;
}
/*!
\brief read device all OUT endpoint interrupt register
\param[in] udev: pointer to USB device
\param[out] none
\retval interrupt status
*/
__STATIC_INLINE uint32_t usb_oepintnum_read(usb_core_driver *udev)
{
uint32_t value = udev->regs.dr->DAEPINT;
value &= udev->regs.dr->DAEPINTEN;
return (value & DAEPINT_OEPITB) >> 16;
}
/*!
\brief read device OUT endpoint interrupt flag register
\param[in] udev: pointer to USB device
\param[in] ep_num: endpoint number
\param[out] none
\retval interrupt status
*/
__STATIC_INLINE uint32_t usb_oepintr_read(usb_core_driver *udev, uint8_t ep_num)
{
uint32_t value = udev->regs.er_out[ep_num]->DOEPINTF;
value &= udev->regs.dr->DOEPINTEN;
return value;
}
/*!
\brief read device all IN endpoint interrupt register
\param[in] udev: pointer to USB device
\param[out] none
\retval interrupt status
*/
__STATIC_INLINE uint32_t usb_iepintnum_read(usb_core_driver *udev)
{
uint32_t value = udev->regs.dr->DAEPINT;
value &= udev->regs.dr->DAEPINTEN;
return value & DAEPINT_IEPITB;
}
/*!
\brief set remote wakeup signaling
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_rwkup_set(usb_core_driver *udev)
{
if(udev->dev.pm.dev_remote_wakeup) {
/* enable remote wakeup signaling */
udev->regs.dr->DCTL |= DCTL_RWKUP;
}
}
/*!
\brief reset remote wakeup signaling
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_rwkup_reset(usb_core_driver *udev)
{
if(udev->dev.pm.dev_remote_wakeup) {
/* disable remote wakeup signaling */
udev->regs.dr->DCTL &= ~DCTL_RWKUP;
}
}
/* function declarations */
/* initialize USB core registers for device mode */
usb_status usb_devcore_init(usb_core_driver *udev);
/* enable the USB device mode interrupts */
usb_status usb_devint_enable(usb_core_driver *udev);
/* active the USB endpoint 0 transaction */
usb_status usb_transc0_active(usb_core_driver *udev, usb_transc *transc);
/* active the USB transaction */
usb_status usb_transc_active(usb_core_driver *udev, usb_transc *transc);
/* deactivate the USB transaction */
usb_status usb_transc_deactivate(usb_core_driver *udev, usb_transc *transc);
/* configure USB transaction to start IN transfer */
usb_status usb_transc_inxfer(usb_core_driver *udev, usb_transc *transc);
/* configure USB transaction to start OUT transfer */
usb_status usb_transc_outxfer(usb_core_driver *udev, usb_transc *transc);
/* set the USB transaction STALL status */
usb_status usb_transc_stall(usb_core_driver *udev, usb_transc *transc);
/* clear the USB transaction STALL status */
usb_status usb_transc_clrstall(usb_core_driver *udev, usb_transc *transc);
/* read device IN endpoint interrupt flag register */
uint32_t usb_iepintr_read(usb_core_driver *udev, uint8_t ep_num);
/* configures OUT endpoint 0 to receive SETUP packets */
void usb_ctlep_startout(usb_core_driver *udev);
/* active remote wakeup signaling */
void usb_rwkup_active(usb_core_driver *udev);
/* active USB core clock */
void usb_clock_active(usb_core_driver *udev);
/* USB device suspend */
void usb_dev_suspend(usb_core_driver *udev);
/* stop the device and clean up FIFOs */
void usb_dev_stop(usb_core_driver *udev);
#endif /* DRV_USB_DEV_H */
@@ -0,0 +1,115 @@
/*!
\file drv_usb_host.h
\brief USB host mode low level driver 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 DRV_USB_HOST_H
#define DRV_USB_HOST_H
#include "drv_usb_core.h"
/*!
\brief get USB even frame
\param[in] udev: pointer to USB device
\param[out] none
\retval USB even or odd frame
*/
__STATIC_INLINE uint8_t usb_frame_even(usb_core_driver *udev)
{
return (uint8_t)!(udev->regs.hr->HFINFR & 0x01U);
}
/*!
\brief configure USB clock of PHY
\param[in] udev: pointer to USB device
\param[in] clock: PHY clock
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_phyclock_config(usb_core_driver *udev, uint8_t clock)
{
udev->regs.hr->HCTL &= ~HCTL_CLKSEL;
udev->regs.hr->HCTL |= clock;
}
/*!
\brief read USB port
\param[in] udev: pointer to USB device
\param[out] none
\retval port status
*/
__STATIC_INLINE uint32_t usb_port_read(usb_core_driver *udev)
{
return *udev->regs.HPCS & ~(HPCS_PE | HPCS_PCD | HPCS_PEDC);
}
/*!
\brief get USB current speed
\param[in] udev: pointer to USB device
\param[out] none
\retval USB current speed
*/
__STATIC_INLINE uint32_t usb_curspeed_get(usb_core_driver *udev)
{
return *udev->regs.HPCS & HPCS_PS;
}
/*!
\brief get USB current frame
\param[in] udev: pointer to USB device
\param[out] none
\retval USB current frame
*/
__STATIC_INLINE uint32_t usb_curframe_get(usb_core_driver *udev)
{
return (udev->regs.hr->HFINFR & 0xFFFFU);
}
/* function declarations */
/* initializes USB core for host mode */
usb_status usb_host_init(usb_core_driver *udev);
/* control the VBUS to power */
void usb_portvbus_switch(usb_core_driver *udev, uint8_t state);
/* reset host port */
uint32_t usb_port_reset(usb_core_driver *udev);
/* initialize host pipe */
usb_status usb_pipe_init(usb_core_driver *udev, uint8_t pipe_num);
/* prepare host pipe for transferring packets */
usb_status usb_pipe_xfer(usb_core_driver *udev, uint8_t pipe_num);
/* halt host pipe */
usb_status usb_pipe_halt(usb_core_driver *udev, uint8_t pipe_num);
/* configure host pipe to do ping operation */
usb_status usb_pipe_ping(usb_core_driver *udev, uint8_t pipe_num);
/* stop the USB host and clean up FIFO */
void usb_host_stop(usb_core_driver *udev);
#endif /* DRV_USB_HOST_H */
@@ -0,0 +1,62 @@
/*!
\file drv_usb_hw.h
\brief usb hardware configuration 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 DRV_USB_HW_H
#define DRV_USB_HW_H
#include "usb_conf.h"
/* function declarations */
/* configure USB clock */
void usb_rcu_config(void);
/* configure USB data line gpio */
void usb_gpio_config(void);
/* configure USB interrupt */
void usb_intr_config(void);
/* initializes delay unit using Timer2 */
void usb_timer_init(void);
/* delay in micro seconds */
void usb_udelay(const uint32_t usec);
/* delay in milliseconds */
void usb_mdelay(const uint32_t msec);
/* configures system clock after wakeup from STOP mode */
void system_clk_config_stop(void);
#ifdef USE_HOST_MODE
/* configure USB VBus */
void usb_vbus_config(void);
/* drive USB VBus */
void usb_vbus_drive(uint8_t State);
#endif /* USE_HOST_MODE */
#endif /* DRV_USB_HW_H */
@@ -0,0 +1,653 @@
/*!
\file drv_usb_regs.h
\brief USB cell registers definition and handle macros
\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 DRV_USB_REGS_H
#define DRV_USB_REGS_H
#include "usb_conf.h"
#define USBHS_REG_BASE 0x40040000L /*!< base address of USBHS registers */
#define USBFS_REG_BASE 0x50000000L /*!< base address of USBFS registers */
#define USBFS_MAX_TX_FIFOS 15U /*!< FIFO number */
#define USBFS_MAX_PACKET_SIZE 64U /*!< USBFS max packet size */
#define USBFS_MAX_CHANNEL_COUNT 8U /*!< USBFS host channel count */
#define USBFS_MAX_EP_COUNT 4U /*!< USBFS device endpoint count */
#define USBFS_MAX_FIFO_WORDLEN 320U /*!< USBFS max FIFO size in words */
#define USBHS_MAX_PACKET_SIZE 512U /*!< USBHS max packet size */
#define USBHS_MAX_CHANNEL_COUNT 12U /*!< USBHS host channel count */
#define USBHS_MAX_EP_COUNT 6U /*!< USBHS device endpoint count */
#define USBHS_MAX_FIFO_WORDLEN 1280U /*!< USBHS max FIFO size in words */
#define USB_DATA_FIFO_OFFSET 0x1000U /*!< USB data FIFO offset */
#define USB_DATA_FIFO_SIZE 0x1000U /*!< USB data FIFO size */
typedef enum {
USB_CORE_ENUM_HS = 0U, /*!< USB core type is HS */
USB_CORE_ENUM_FS = 1U /*!< USB core type is FS */
} usb_core_enum;
enum USB_SPEED {
USB_SPEED_UNKNOWN = 0U, /*!< USB speed unknown */
USB_SPEED_LOW, /*!< USB speed low */
USB_SPEED_FULL, /*!< USB speed full */
USB_SPEED_HIGH /*!< USB speed high */
};
enum usb_reg_offset {
USB_REG_OFFSET_CORE = 0x0000U, /*!< global OTG control and status register */
USB_REG_OFFSET_DEV = 0x0800U, /*!< device mode control and status registers */
USB_REG_OFFSET_EP = 0x0020U,
USB_REG_OFFSET_EP_IN = 0x0900U, /*!< device IN endpoint 0 control register */
USB_REG_OFFSET_EP_OUT = 0x0B00U, /*!< device OUT endpoint 0 control register */
USB_REG_OFFSET_HOST = 0x0400U, /*!< host control register */
USB_REG_OFFSET_CH = 0x0020U,
USB_REG_OFFSET_PORT = 0x0440U, /*!< host port control and status register */
USB_REG_OFFSET_CH_INOUT = 0x0500U, /*!< Host channel-x control registers */
USB_REG_OFFSET_PWRCLKCTL = 0x0E00U /*!< power and clock register */
};
typedef struct {
__IO uint32_t GOTGCS; /*!< USB global OTG control and status register 000h */
__IO uint32_t GOTGINTF; /*!< USB global OTG interrupt flag register 004h */
__IO uint32_t GAHBCS; /*!< USB global AHB control and status register 008h */
__IO uint32_t GUSBCS; /*!< USB global USB control and status register 00Ch */
__IO uint32_t GRSTCTL; /*!< USB global reset control register 010h */
__IO uint32_t GINTF; /*!< USB global interrupt flag register 014h */
__IO uint32_t GINTEN; /*!< USB global interrupt enable register 018h */
__IO uint32_t GRSTATR; /*!< USB receive status debug read register 01Ch */
__IO uint32_t GRSTATP; /*!< USB receive status and pop register 020h */
__IO uint32_t GRFLEN; /*!< USB global receive FIFO length register 024h */
__IO uint32_t DIEP0TFLEN_HNPTFLEN; /*!< USB device IN endpoint 0/host non-periodic transmit FIFO length register 028h */
__IO uint32_t HNPTFQSTAT; /*!< USB host non-periodic FIFO/queue status register 02Ch */
uint32_t Reserved30[2]; /*!< Reserved 030h */
__IO uint32_t GCCFG; /*!< USB global core configuration register 038h */
__IO uint32_t CID; /*!< USB core ID register 03Ch */
uint32_t Reserved40[48]; /*!< Reserved 040h-0FFh */
__IO uint32_t HPTFLEN; /*!< USB host periodic transmit FIFO length register 100h */
__IO uint32_t DIEPTFLEN[15]; /*!< USB device IN endpoint transmit FIFO length register 104h */
} usb_gr;
typedef struct {
__IO uint32_t HCTL; /*!< USB host control register 400h */
__IO uint32_t HFT; /*!< USB host frame interval register 404h */
__IO uint32_t HFINFR; /*!< USB host frame information remaining register 408h */
uint32_t Reserved40C; /*!< Reserved 40Ch */
__IO uint32_t HPTFQSTAT; /*!< USB host periodic transmit FIFO/queue status register 410h */
__IO uint32_t HACHINT; /*!< USB host all channels interrupt register 414h */
__IO uint32_t HACHINTEN; /*!< USB host all channels interrupt enable register 418h */
} usb_hr;
typedef struct {
__IO uint32_t HCHCTL; /*!< USB host channel control register 500h */
__IO uint32_t HCHSTCTL; /*!< Reserved 504h */
__IO uint32_t HCHINTF; /*!< USB host channel interrupt flag register 508h */
__IO uint32_t HCHINTEN; /*!< USB host channel interrupt enable register 50Ch */
__IO uint32_t HCHLEN; /*!< USB host channel transfer length register 510h */
__IO uint32_t HCHDMAADDR; /*!< USB host channel-x DMA address register 514h*/
uint32_t Reserved[2];
} usb_pr;
typedef struct {
__IO uint32_t DCFG; /*!< USB device configuration register 800h */
__IO uint32_t DCTL; /*!< USB device control register 804h */
__IO uint32_t DSTAT; /*!< USB device status register 808h */
uint32_t Reserved0C; /*!< Reserved 80Ch */
__IO uint32_t DIEPINTEN; /*!< USB device IN endpoint common interrupt enable register 810h */
__IO uint32_t DOEPINTEN; /*!< USB device OUT endpoint common interrupt enable register 814h */
__IO uint32_t DAEPINT; /*!< USB device all endpoints interrupt register 818h */
__IO uint32_t DAEPINTEN; /*!< USB device all endpoints interrupt enable register 81Ch */
uint32_t Reserved20; /*!< Reserved 820h */
uint32_t Reserved24; /*!< Reserved 824h */
__IO uint32_t DVBUSDT; /*!< USB device VBUS discharge time register 828h */
__IO uint32_t DVBUSPT; /*!< USB device VBUS pulsing time register 82Ch */
__IO uint32_t DTHRCTL; /*!< device threshold control 830h */
__IO uint32_t DIEPFEINTEN; /*!< USB Device IN endpoint FIFO empty interrupt enable register 834h */
__IO uint32_t DEP1INT; /*!< USB device endpoint 1 interrupt register 838h */
__IO uint32_t DEP1INTEN; /*!< USB device endpoint 1 interrupt enable register 83Ch */
uint32_t Reserved40; /*!< Reserved 840h */
__IO uint32_t DIEP1INTEN; /*!< USB device IN endpoint-1 interrupt enable register 844h */
uint32_t Reserved48[15]; /*!< Reserved 848-880h */
__IO uint32_t DOEP1INTEN; /*!< USB device OUT endpoint-1 interrupt enable register 884h */
} usb_dr;
typedef struct {
__IO uint32_t DIEPCTL; /*!< USB device IN endpoint control register 900h + (EpNum * 20h) + 00h */
uint32_t Reserved04; /*!< Reserved 900h + (EpNum * 20h) + 04h */
__IO uint32_t DIEPINTF; /*!< USB device IN endpoint interrupt flag register 900h + (EpNum * 20h) + 08h */
uint32_t Reserved0C; /*!< Reserved 900h + (EpNum * 20h) + 0Ch */
__IO uint32_t DIEPLEN; /*!< USB device IN endpoint transfer length register 900h + (EpNum * 20h) + 10h */
__IO uint32_t DIEPDMAADDR; /*!< Device IN endpoint-x DMA address register 900h + (EpNum * 20h) + 14h */
__IO uint32_t DIEPTFSTAT; /*!< USB device IN endpoint transmit FIFO status register 900h + (EpNum * 20h) + 18h */
} usb_erin;
typedef struct {
__IO uint32_t DOEPCTL; /*!< USB device IN endpoint control register B00h + (EpNum * 20h) + 00h */
uint32_t Reserved04; /*!< Reserved B00h + (EpNum * 20h) + 04h */
__IO uint32_t DOEPINTF; /*!< USB device IN endpoint interrupt flag register B00h + (EpNum * 20h) + 08h */
uint32_t Reserved0C; /*!< Reserved B00h + (EpNum * 20h) + 0Ch */
__IO uint32_t DOEPLEN; /*!< USB device IN endpoint transfer length register B00h + (EpNum * 20h) + 10h */
__IO uint32_t DOEPDMAADDR; /*!< Device OUT endpoint-x DMA address register B00h + (EpNum * 20h) + 0Ch */
} usb_erout;
typedef struct _usb_regs {
usb_gr *gr; /*!< USBFS global registers */
usb_dr *dr; /*!< Device control and status registers */
usb_hr *hr; /*!< Host control and status registers */
usb_erin *er_in[6]; /*!< USB device IN endpoint register */
usb_erout *er_out[6]; /*!< USB device OUT endpoint register */
usb_pr *pr[15]; /*!< USB Host channel-x control register */
__IO uint32_t *HPCS; /*!< USB host port control and status register */
__IO uint32_t *DFIFO[USBFS_MAX_TX_FIFOS];
__IO uint32_t *PWRCLKCTL; /*!< USB power and clock control register */
} usb_core_regs;
/* global OTG control and status register bits definitions */
#define GOTGCS_BSV BIT(19) /*!< B-Session Valid */
#define GOTGCS_ASV BIT(18) /*!< A-session valid */
#define GOTGCS_DI BIT(17) /*!< debounce interval */
#define GOTGCS_CIDPS BIT(16) /*!< id pin status */
#define GOTGCS_DHNPEN BIT(11) /*!< device HNP enable */
#define GOTGCS_HHNPEN BIT(10) /*!< host HNP enable */
#define GOTGCS_HNPREQ BIT(9) /*!< HNP request */
#define GOTGCS_HNPS BIT(8) /*!< HNP successes */
#define GOTGCS_SRPREQ BIT(1) /*!< SRP request */
#define GOTGCS_SRPS BIT(0) /*!< SRP successes */
/* global OTG interrupt flag register bits definitions */
#define GOTGINTF_DF BIT(19) /*!< debounce finish */
#define GOTGINTF_ADTO BIT(18) /*!< A-device timeout */
#define GOTGINTF_HNPDET BIT(17) /*!< host negotiation request detected */
#define GOTGINTF_HNPEND BIT(9) /*!< HNP end */
#define GOTGINTF_SRPEND BIT(8) /*!< SRP end */
#define GOTGINTF_SESEND BIT(2) /*!< session end */
/* global AHB control and status register bits definitions */
#define GAHBCS_PTXFTH BIT(8) /*!< periodic TX FIFO threshold */
#define GAHBCS_TXFTH BIT(7) /*!< TX FIFO threshold */
#define GAHBCS_DMAEN BIT(5) /*!< DMA function Enable */
#define GAHBCS_BURST BITS(1, 4) /*!< the AHB burst type used by DMA */
#define GAHBCS_GINTEN BIT(0) /*!< global interrupt enable */
/* global USB control and status register bits definitions */
#define GUSBCS_FDM BIT(30) /*!< force device mode */
#define GUSBCS_FHM BIT(29) /*!< force host mode */
#define GUSBCS_ULPIEOI BIT(21) /*!< ULPI external over-current indicator */
#define GUSBCS_ULPIEVD BIT(20) /*!< ULPI external VBUS driver */
#define GUSBCS_UTT BITS(10, 13) /*!< USB turnaround time */
#define GUSBCS_HNPCEN BIT(9) /*!< HNP capability enable */
#define GUSBCS_SRPCEN BIT(8) /*!< SRP capability enable */
#define GUSBCS_EMBPHY BIT(6) /*!< embedded PHY selected */
#define GUSBCS_TOC BITS(0, 2) /*!< timeout calibration */
/* global reset control register bits definitions */
#define GRSTCTL_DMAIDL BIT(31) /*!< DMA idle state */
#define GRSTCTL_DMABSY BIT(30) /*!< DMA busy */
#define GRSTCTL_TXFNUM BITS(6, 10) /*!< tx FIFO number */
#define GRSTCTL_TXFF BIT(5) /*!< tx FIFO flush */
#define GRSTCTL_RXFF BIT(4) /*!< rx FIFO flush */
#define GRSTCTL_HFCRST BIT(2) /*!< host frame counter reset */
#define GRSTCTL_HCSRST BIT(1) /*!< HCLK soft reset */
#define GRSTCTL_CSRST BIT(0) /*!< core soft reset */
/* global interrupt flag register bits definitions */
#define GINTF_WKUPIF BIT(31) /*!< wakeup interrupt flag */
#define GINTF_SESIF BIT(30) /*!< session interrupt flag */
#define GINTF_DISCIF BIT(29) /*!< disconnect interrupt flag */
#define GINTF_IDPSC BIT(28) /*!< id pin status change */
#define GINTF_PTXFEIF BIT(26) /*!< periodic tx FIFO empty interrupt flag */
#define GINTF_HCIF BIT(25) /*!< host channels interrupt flag */
#define GINTF_HPIF BIT(24) /*!< host port interrupt flag */
#define GINTF_PXNCIF BIT(21) /*!< periodic transfer not complete interrupt flag */
#define GINTF_ISOONCIF BIT(21) /*!< isochronous OUT transfer not complete interrupt flag */
#define GINTF_ISOINCIF BIT(20) /*!< isochronous IN transfer not complete interrupt flag */
#define GINTF_OEPIF BIT(19) /*!< OUT endpoint interrupt flag */
#define GINTF_IEPIF BIT(18) /*!< IN endpoint interrupt flag */
#define GINTF_EOPFIF BIT(15) /*!< end of periodic frame interrupt flag */
#define GINTF_ISOOPDIF BIT(14) /*!< isochronous OUT packet dropped interrupt flag */
#define GINTF_ENUMFIF BIT(13) /*!< enumeration finished */
#define GINTF_RST BIT(12) /*!< USB reset */
#define GINTF_SP BIT(11) /*!< USB suspend */
#define GINTF_ESP BIT(10) /*!< early suspend */
#define GINTF_GONAK BIT(7) /*!< global OUT NAK effective */
#define GINTF_GNPINAK BIT(6) /*!< global IN non-periodic NAK effective */
#define GINTF_NPTXFEIF BIT(5) /*!< non-periodic tx FIFO empty interrupt flag */
#define GINTF_RXFNEIF BIT(4) /*!< rx FIFO non-empty interrupt flag */
#define GINTF_SOF BIT(3) /*!< start of frame */
#define GINTF_OTGIF BIT(2) /*!< OTG interrupt flag */
#define GINTF_MFIF BIT(1) /*!< mode fault interrupt flag */
#define GINTF_COPM BIT(0) /*!< current operation mode */
/* global interrupt enable register bits definitions */
#define GINTEN_WKUPIE BIT(31) /*!< wakeup interrupt enable */
#define GINTEN_SESIE BIT(30) /*!< session interrupt enable */
#define GINTEN_DISCIE BIT(29) /*!< disconnect interrupt enable */
#define GINTEN_IDPSCIE BIT(28) /*!< id pin status change interrupt enable */
#define GINTEN_PTXFEIE BIT(26) /*!< periodic tx FIFO empty interrupt enable */
#define GINTEN_HCIE BIT(25) /*!< host channels interrupt enable */
#define GINTEN_HPIE BIT(24) /*!< host port interrupt enable */
#define GINTEN_IPXIE BIT(21) /*!< periodic transfer not complete interrupt enable */
#define GINTEN_ISOONCIE BIT(21) /*!< isochronous OUT transfer not complete interrupt enable */
#define GINTEN_ISOINCIE BIT(20) /*!< isochronous IN transfer not complete interrupt enable */
#define GINTEN_OEPIE BIT(19) /*!< OUT endpoints interrupt enable */
#define GINTEN_IEPIE BIT(18) /*!< IN endpoints interrupt enable */
#define GINTEN_EOPFIE BIT(15) /*!< end of periodic frame interrupt enable */
#define GINTEN_ISOOPDIE BIT(14) /*!< isochronous OUT packet dropped interrupt enable */
#define GINTEN_ENUMFIE BIT(13) /*!< enumeration finish enable */
#define GINTEN_RSTIE BIT(12) /*!< USB reset interrupt enable */
#define GINTEN_SPIE BIT(11) /*!< USB suspend interrupt enable */
#define GINTEN_ESPIE BIT(10) /*!< early suspend interrupt enable */
#define GINTEN_GONAKIE BIT(7) /*!< global OUT NAK effective interrupt enable */
#define GINTEN_GNPINAKIE BIT(6) /*!< global non-periodic IN NAK effective interrupt enable */
#define GINTEN_NPTXFEIE BIT(5) /*!< non-periodic TX FIFO empty interrupt enable */
#define GINTEN_RXFNEIE BIT(4) /*!< receive FIFO non-empty interrupt enable */
#define GINTEN_SOFIE BIT(3) /*!< start of frame interrupt enable */
#define GINTEN_OTGIE BIT(2) /*!< OTG interrupt enable */
#define GINTEN_MFIE BIT(1) /*!< mode fault interrupt enable */
/* global receive status read and pop register bits definitions */
#define GRSTATRP_RPCKST BITS(17, 20) /*!< received packet status */
#define GRSTATRP_DPID BITS(15, 16) /*!< data PID */
#define GRSTATRP_BCOUNT BITS(4, 14) /*!< byte count */
#define GRSTATRP_CNUM BITS(0, 3) /*!< channel number */
#define GRSTATRP_EPNUM BITS(0, 3) /*!< endpoint number */
/* global receive FIFO length register bits definitions */
#define GRFLEN_RXFD BITS(0, 15) /*!< rx FIFO depth */
/* host non-periodic transmit FIFO length register bits definitions */
#define HNPTFLEN_HNPTXFD BITS(16, 31) /*!< non-periodic TX FIFO depth */
#define HNPTFLEN_HNPTXRSAR BITS(0, 15) /*!< non-periodic TX RAM start address */
/* USB IN endpoint 0 transmit FIFO length register bits definitions */
#define DIEP0TFLEN_IEP0TXFD BITS(16, 31) /*!< IN Endpoint 0 TX FIFO depth */
#define DIEP0TFLEN_IEP0TXRSAR BITS(0, 15) /*!< IN Endpoint 0 TX RAM start address */
/* host non-periodic transmit FIFO/queue status register bits definitions */
#define HNPTFQSTAT_NPTXRQTOP BITS(24, 30) /*!< top entry of the non-periodic TX request queue */
#define HNPTFQSTAT_NPTXRQS BITS(16, 23) /*!< non-periodic TX request queue space */
#define HNPTFQSTAT_NPTXFS BITS(0, 15) /*!< non-periodic TX FIFO space */
#define HNPTFQSTAT_CNUM BITS(27, 30) /*!< channel number*/
#define HNPTFQSTAT_EPNUM BITS(27, 30) /*!< endpoint number */
#define HNPTFQSTAT_TYPE BITS(25, 26) /*!< token type */
#define HNPTFQSTAT_TMF BIT(24) /*!< terminate flag */
/* global core configuration register bits definitions */
#define GCCFG_VBUSIG BIT(21) /*!< vbus ignored */
#define GCCFG_SOFOEN BIT(20) /*!< SOF output enable */
#define GCCFG_VBUSBCEN BIT(19) /*!< the VBUS B-device comparer enable */
#define GCCFG_VBUSACEN BIT(18) /*!< the VBUS A-device comparer enable */
#define GCCFG_PWRON BIT(16) /*!< power on */
/* core ID register bits definitions */
#define CID_CID BITS(0, 31) /*!< core ID */
/* host periodic transmit FIFO length register bits definitions */
#define HPTFLEN_HPTXFD BITS(16, 31) /*!< host periodic TX FIFO depth */
#define HPTFLEN_HPTXFSAR BITS(0, 15) /*!< host periodic TX RAM start address */
/* device IN endpoint transmit FIFO length register bits definitions */
#define DIEPTFLEN_IEPTXFD BITS(16, 31) /*!< IN endpoint TX FIFO x depth */
#define DIEPTFLEN_IEPTXRSAR BITS(0, 15) /*!< IN endpoint FIFOx TX x RAM start address */
/* host control register bits definitions */
#define HCTL_SPDFSLS BIT(2) /*!< speed limited to FS and LS */
#define HCTL_CLKSEL BITS(0, 1) /*!< clock select for USB clock */
/* host frame interval register bits definitions */
#define HFT_FRI BITS(0, 15) /*!< frame interval */
/* host frame information remaining register bits definitions */
#define HFINFR_FRT BITS(16, 31) /*!< frame remaining time */
#define HFINFR_FRNUM BITS(0, 15) /*!< frame number */
/* host periodic transmit FIFO/queue status register bits definitions */
#define HPTFQSTAT_PTXREQT BITS(24, 31) /*!< top entry of the periodic TX request queue */
#define HPTFQSTAT_PTXREQS BITS(16, 23) /*!< periodic TX request queue space */
#define HPTFQSTAT_PTXFS BITS(0, 15) /*!< periodic TX FIFO space */
#define HPTFQSTAT_OEFRM BIT(31) /*!< odd/eveb frame */
#define HPTFQSTAT_CNUM BITS(27, 30) /*!< channel number */
#define HPTFQSTAT_EPNUM BITS(27, 30) /*!< endpoint number */
#define HPTFQSTAT_TYPE BITS(25, 26) /*!< token type */
#define HPTFQSTAT_TMF BIT(24) /*!< terminate flag */
#define TFQSTAT_TXFS BITS(0, 15)
#define TFQSTAT_CNUM BITS(27, 30)
/* host all channels interrupt register bits definitions */
#define HACHINT_HACHINT BITS(0, 11) /*!< host all channel interrupts */
/* host all channels interrupt enable register bits definitions */
#define HACHINTEN_CINTEN BITS(0, 11) /*!< channel interrupt enable */
/* host port control and status register bits definitions */
#define HPCS_PS BITS(17, 18) /*!< port speed */
#define HPCS_PP BIT(12) /*!< port power */
#define HPCS_PLST BITS(10, 11) /*!< port line status */
#define HPCS_PRST BIT(8) /*!< port reset */
#define HPCS_PSP BIT(7) /*!< port suspend */
#define HPCS_PREM BIT(6) /*!< port resume */
#define HPCS_PEDC BIT(3) /*!< port enable/disable change */
#define HPCS_PE BIT(2) /*!< port enable */
#define HPCS_PCD BIT(1) /*!< port connect detected */
#define HPCS_PCST BIT(0) /*!< port connect status */
/* host channel-x control register bits definitions */
#define HCHCTL_CEN BIT(31) /*!< channel enable */
#define HCHCTL_CDIS BIT(30) /*!< channel disable */
#define HCHCTL_ODDFRM BIT(29) /*!< odd frame */
#define HCHCTL_DAR BITS(22, 28) /*!< device address */
#define HCHCTL_MPC BITS(20, 21) /*!< multiple packet count */
#define HCHCTL_EPTYPE BITS(18, 19) /*!< endpoint type */
#define HCHCTL_LSD BIT(17) /*!< low-speed device */
#define HCHCTL_EPDIR BIT(15) /*!< endpoint direction */
#define HCHCTL_EPNUM BITS(11, 14) /*!< endpoint number */
#define HCHCTL_MPL BITS(0, 10) /*!< maximum packet length */
/* host channel-x split transaction register bits definitions */
#define HCHSTCTL_SPLEN BIT(31) /*!< enable high-speed split transaction */
#define HCHSTCTL_CSPLT BIT(16) /*!< complete-split enable */
#define HCHSTCTL_ISOPCE BITS(14, 15) /*!< isochronous OUT payload continuation encoding */
#define HCHSTCTL_HADDR BITS(7, 13) /*!< HUB address */
#define HCHSTCTL_PADDR BITS(0, 6) /*!< port address */
/* host channel-x interrupt flag register bits definitions */
#define HCHINTF_DTER BIT(10) /*!< data toggle error */
#define HCHINTF_REQOVR BIT(9) /*!< request queue overrun */
#define HCHINTF_BBER BIT(8) /*!< babble error */
#define HCHINTF_USBER BIT(7) /*!< USB bus Error */
#define HCHINTF_NYET BIT(6) /*!< NYET */
#define HCHINTF_ACK BIT(5) /*!< ACK */
#define HCHINTF_NAK BIT(4) /*!< NAK */
#define HCHINTF_STALL BIT(3) /*!< STALL */
#define HCHINTF_DMAER BIT(2) /*!< DMA error */
#define HCHINTF_CH BIT(1) /*!< channel halted */
#define HCHINTF_TF BIT(0) /*!< transfer finished */
/* host channel-x interrupt enable register bits definitions */
#define HCHINTEN_DTERIE BIT(10) /*!< data toggle error interrupt enable */
#define HCHINTEN_REQOVRIE BIT(9) /*!< request queue overrun interrupt enable */
#define HCHINTEN_BBERIE BIT(8) /*!< babble error interrupt enable */
#define HCHINTEN_USBERIE BIT(7) /*!< USB bus error interrupt enable */
#define HCHINTEN_NYETIE BIT(6) /*!< NYET interrupt enable */
#define HCHINTEN_ACKIE BIT(5) /*!< ACK interrupt enable */
#define HCHINTEN_NAKIE BIT(4) /*!< NAK interrupt enable */
#define HCHINTEN_STALLIE BIT(3) /*!< STALL interrupt enable */
#define HCHINTEN_DMAERIE BIT(2) /*!< DMA error interrupt enable */
#define HCHINTEN_CHIE BIT(1) /*!< channel halted interrupt enable */
#define HCHINTEN_TFIE BIT(0) /*!< transfer finished interrupt enable */
/* host channel-x transfer length register bits definitions */
#define HCHLEN_PING BIT(31) /*!< PING token request */
#define HCHLEN_DPID BITS(29, 30) /*!< data PID */
#define HCHLEN_PCNT BITS(19, 28) /*!< packet count */
#define HCHLEN_TLEN BITS(0, 18) /*!< transfer length */
/* host channel-x DMA address register bits definitions */
#define HCHDMAADDR_DMAADDR BITS(0, 31) /*!< DMA address */
#define PORT_SPEED(x) (((uint32_t)(x) << 17) & HPCS_PS) /*!< Port speed */
#define PORT_SPEED_HIGH PORT_SPEED(0U) /*!< high speed */
#define PORT_SPEED_FULL PORT_SPEED(1U) /*!< full speed */
#define PORT_SPEED_LOW PORT_SPEED(2U) /*!< low speed */
#define PIPE_CTL_DAR(x) (((uint32_t)(x) << 22) & HCHCTL_DAR) /*!< device address */
#define PIPE_CTL_EPTYPE(x) (((uint32_t)(x) << 18) & HCHCTL_EPTYPE) /*!< endpoint type */
#define PIPE_CTL_EPNUM(x) (((uint32_t)(x) << 11) & HCHCTL_EPNUM) /*!< endpoint number */
#define PIPE_CTL_EPDIR(x) (((uint32_t)(x) << 15) & HCHCTL_EPDIR) /*!< endpoint direction */
#define PIPE_CTL_EPMPL(x) (((uint32_t)(x) << 0) & HCHCTL_MPL) /*!< maximum packet length */
#define PIPE_CTL_LSD(x) (((uint32_t)(x) << 17) & HCHCTL_LSD) /*!< low-Speed device */
#define PIPE_XFER_PCNT(x) (((uint32_t)(x) << 19) & HCHLEN_PCNT) /*!< packet count */
#define PIPE_XFER_DPID(x) (((uint32_t)(x) << 29) & HCHLEN_DPID) /*!< data PID */
#define PIPE_DPID_DATA0 PIPE_XFER_DPID(0U) /*!< DATA0 */
#define PIPE_DPID_DATA1 PIPE_XFER_DPID(2U) /*!< DATA1 */
#define PIPE_DPID_DATA2 PIPE_XFER_DPID(1U) /*!< DATA2 */
#define PIPE_DPID_SETUP PIPE_XFER_DPID(3U) /*!< MDATA (non-control)/SETUP (control) */
extern const uint32_t PIPE_DPID[2];
/* device configuration registers bits definitions */
#define DCFG_EOPFT BITS(11, 12) /*!< end of periodic frame time */
#define DCFG_DAR BITS(4, 10) /*!< device address */
#define DCFG_NZLSOH BIT(2) /*!< non-zero-length status OUT handshake */
#define DCFG_DS BITS(0, 1) /*!< device speed */
/* device control registers bits definitions */
#define DCTL_POIF BIT(11) /*!< power-on initialization finished */
#define DCTL_CGONAK BIT(10) /*!< clear global OUT NAK */
#define DCTL_SGONAK BIT(9) /*!< set global OUT NAK */
#define DCTL_CGINAK BIT(8) /*!< clear global IN NAK */
#define DCTL_SGINAK BIT(7) /*!< set global IN NAK */
#define DCTL_GONS BIT(3) /*!< global OUT NAK status */
#define DCTL_GINS BIT(2) /*!< global IN NAK status */
#define DCTL_SD BIT(1) /*!< soft disconnect */
#define DCTL_RWKUP BIT(0) /*!< remote wakeup */
/* device status registers bits definitions */
#define DSTAT_FNRSOF BITS(8, 21) /*!< the frame number of the received SOF. */
#define DSTAT_ES BITS(1, 2) /*!< enumerated speed */
#define DSTAT_SPST BIT(0) /*!< suspend status */
/* device IN endpoint common interrupt enable registers bits definitions */
#define DIEPINTEN_NAKEN BIT(13) /*!< NAK handshake sent by USB interrupt enable bit */
#define DIEPINTEN_TXFEEN BIT(7) /*!< transmit FIFO empty interrupt enable bit */
#define DIEPINTEN_IEPNEEN BIT(6) /*!< IN endpoint NAK effective interrupt enable bit */
#define DIEPINTEN_EPTXFUDEN BIT(4) /*!< endpoint TX FIFO underrun interrupt enable bit */
#define DIEPINTEN_CITOEN BIT(3) /*!< control In Timeout interrupt enable bit */
#define DIEPINTEN_EPDISEN BIT(1) /*!< endpoint disabled interrupt enable bit */
#define DIEPINTEN_TFEN BIT(0) /*!< transfer finished interrupt enable bit */
/* device OUT endpoint common interrupt enable registers bits definitions */
#define DOEPINTEN_NYETEN BIT(14) /*!< NYET handshake is sent interrupt enable bit */
#define DOEPINTEN_BTBSTPEN BIT(6) /*!< back-to-back SETUP packets interrupt enable bit */
#define DOEPINTEN_EPRXFOVREN BIT(4) /*!< endpoint RX FIFO overrun interrupt enable bit */
#define DOEPINTEN_STPFEN BIT(3) /*!< SETUP phase finished interrupt enable bit */
#define DOEPINTEN_EPDISEN BIT(1) /*!< endpoint disabled interrupt enable bit */
#define DOEPINTEN_TFEN BIT(0) /*!< transfer finished interrupt enable bit */
/* device all endpoints interrupt registers bits definitions */
#define DAEPINT_OEPITB BITS(16, 21) /*!< device all OUT endpoint interrupt bits */
#define DAEPINT_IEPITB BITS(0, 5) /*!< device all IN endpoint interrupt bits */
/* device all endpoints interrupt enable registers bits definitions */
#define DAEPINTEN_OEPIE BITS(16, 21) /*!< OUT endpoint interrupt enable */
#define DAEPINTEN_IEPIE BITS(0, 3) /*!< IN endpoint interrupt enable */
/* device Vbus discharge time registers bits definitions */
#define DVBUSDT_DVBUSDT BITS(0, 15) /*!< device VBUS discharge time */
/* device Vbus pulsing time registers bits definitions */
#define DVBUSPT_DVBUSPT BITS(0, 11) /*!< device VBUS pulsing time */
/* device IN endpoint FIFO empty interrupt enable register bits definitions */
#define DIEPFEINTEN_IEPTXFEIE BITS(0, 5) /*!< IN endpoint TX FIFO empty interrupt enable bits */
/* device endpoint 0 control register bits definitions */
#define DEP0CTL_EPEN BIT(31) /*!< endpoint enable */
#define DEP0CTL_EPD BIT(30) /*!< endpoint disable */
#define DEP0CTL_SNAK BIT(27) /*!< set NAK */
#define DEP0CTL_CNAK BIT(26) /*!< clear NAK */
#define DIEP0CTL_TXFNUM BITS(22, 25) /*!< tx FIFO number */
#define DEP0CTL_STALL BIT(21) /*!< STALL handshake */
#define DOEP0CTL_SNOOP BIT(20) /*!< snoop mode */
#define DEP0CTL_EPTYPE BITS(18, 19) /*!< endpoint type */
#define DEP0CTL_NAKS BIT(17) /*!< NAK status */
#define DEP0CTL_EPACT BIT(15) /*!< endpoint active */
#define DEP0CTL_MPL BITS(0, 1) /*!< maximum packet length */
/* device endpoint x control register bits definitions */
#define DEPCTL_EPEN BIT(31) /*!< endpoint enable */
#define DEPCTL_EPD BIT(30) /*!< endpoint disable */
#define DEPCTL_SODDFRM BIT(29) /*!< set odd frame */
#define DEPCTL_SD1PID BIT(29) /*!< set DATA1 PID */
#define DEPCTL_SEVNFRM BIT(28) /*!< set even frame */
#define DEPCTL_SD0PID BIT(28) /*!< set DATA0 PID */
#define DEPCTL_SNAK BIT(27) /*!< set NAK */
#define DEPCTL_CNAK BIT(26) /*!< clear NAK */
#define DIEPCTL_TXFNUM BITS(22, 25) /*!< tx FIFO number */
#define DEPCTL_STALL BIT(21) /*!< STALL handshake */
#define DOEPCTL_SNOOP BIT(20) /*!< snoop mode */
#define DEPCTL_EPTYPE BITS(18, 19) /*!< endpoint type */
#define DEPCTL_NAKS BIT(17) /*!< NAK status */
#define DEPCTL_EOFRM BIT(16) /*!< even/odd frame */
#define DEPCTL_DPID BIT(16) /*!< endpoint data PID */
#define DEPCTL_EPACT BIT(15) /*!< endpoint active */
#define DEPCTL_MPL BITS(0, 10) /*!< maximum packet length */
/* device IN endpoint-x interrupt flag register bits definitions */
#define DIEPINTF_NAK BIT(13) /*!< NAK handshake sent by USB */
#define DIEPINTF_TXFE BIT(7) /*!< transmit FIFO empty */
#define DIEPINTF_IEPNE BIT(6) /*!< IN endpoint NAK effective */
#define DIEPINTF_EPTXFUD BIT(4) /*!< endpoint TX FIFO underrun */
#define DIEPINTF_CITO BIT(3) /*!< control In Timeout interrupt */
#define DIEPINTF_EPDIS BIT(1) /*!< endpoint disabled */
#define DIEPINTF_TF BIT(0) /*!< transfer finished */
/* device OUT endpoint-x interrupt flag register bits definitions */
#define DOEPINTF_NYET BIT(14) /*!< NYET handshake is sent */
#define DOEPINTF_BTBSTP BIT(6) /*!< back-to-back SETUP packets */
#define DOEPINTF_EPRXFOVR BIT(4) /*!< endpoint RX FIFO overrun */
#define DOEPINTF_STPF BIT(3) /*!< SETUP phase finished */
#define DOEPINTF_EPDIS BIT(1) /*!< endpoint disabled */
#define DOEPINTF_TF BIT(0) /*!< transfer finished */
/* device IN endpoint 0 transfer length register bits definitions */
#define DIEP0LEN_PCNT BITS(19, 20) /*!< packet count */
#define DIEP0LEN_TLEN BITS(0, 6) /*!< transfer length */
/* device OUT endpoint 0 transfer length register bits definitions */
#define DOEP0LEN_STPCNT BITS(29, 30) /*!< SETUP packet count */
#define DOEP0LEN_PCNT BIT(19) /*!< packet count */
#define DOEP0LEN_TLEN BITS(0, 6) /*!< transfer length */
/* device OUT endpoint-x transfer length register bits definitions */
#define DOEPLEN_RXDPID BITS(29, 30) /*!< received data PID */
#define DOEPLEN_STPCNT BITS(29, 30) /*!< SETUP packet count */
#define DIEPLEN_MCNT BITS(29, 30) /*!< multi count */
#define DEPLEN_PCNT BITS(19, 28) /*!< packet count */
#define DEPLEN_TLEN BITS(0, 18) /*!< transfer length */
/* device IN endpoint-x DMA address register bits definitions */
#define DIEPDMAADDR_DMAADDR BITS(0, 31) /*!< DMA address */
/* device OUT endpoint-x DMA address register bits definitions */
#define DOEPDMAADDR_DMAADDR BITS(0, 31) /*!< DMA address */
/* device IN endpoint-x transmit FIFO status register bits definitions */
#define DIEPTFSTAT_IEPTFS BITS(0, 15) /*!< IN endpoint TX FIFO space remaining */
/* USB power and clock registers bits definition */
#define PWRCLKCTL_SHCLK BIT(1) /*!< stop HCLK */
#define PWRCLKCTL_SUCLK BIT(0) /*!< stop the USB clock */
#define RSTAT_GOUT_NAK 1U /*!< global OUT NAK (triggers an interrupt) */
#define RSTAT_DATA_UPDT 2U /*!< OUT data packet received */
#define RSTAT_XFER_COMP 3U /*!< OUT transfer completed (triggers an interrupt) */
#define RSTAT_SETUP_COMP 4U /*!< SETUP transaction completed (triggers an interrupt) */
#define RSTAT_SETUP_UPDT 6U /*!< SETUP data packet received */
#define DSTAT_EM_HS_PHY_30MHZ_60MHZ 0U /*!< USB enumerate speed use high-speed PHY clock in 30MHz or 60MHz */
#define DSTAT_EM_FS_PHY_30MHZ_60MHZ 1U /*!< USB enumerate speed use full-speed PHY clock in 30MHz or 60MHz */
#define DSTAT_EM_LS_PHY_6MHZ 2U /*!< USB enumerate speed use low-speed PHY clock in 6MHz */
#define DSTAT_EM_FS_PHY_48MHZ 3U /*!< USB enumerate speed use full-speed PHY clock in 48MHz */
#define DPID_DATA0 0U /*!< device endpoint data PID is DATA0 */
#define DPID_DATA1 2U /*!< device endpoint data PID is DATA1 */
#define DPID_DATA2 1U /*!< device endpoint data PID is DATA2 */
#define DPID_MDATA 3U /*!< device endpoint data PID is MDATA */
#define GAHBCS_DMAINCR(regval) (GAHBCS_BURST & ((regval) << 1)) /*!< AHB burst type used by DMA*/
#define DMA_INCR0 GAHBCS_DMAINCR(0U) /*!< single burst type used by DMA*/
#define DMA_INCR1 GAHBCS_DMAINCR(1U) /*!< 4-beat incrementing burst type used by DMA*/
#define DMA_INCR4 GAHBCS_DMAINCR(3U) /*!< 8-beat incrementing burst type used by DMA*/
#define DMA_INCR8 GAHBCS_DMAINCR(5U) /*!< 16-beat incrementing burst type used by DMA*/
#define DMA_INCR16 GAHBCS_DMAINCR(7U) /*!< 32-beat incrementing burst type used by DMA*/
#define DCFG_PFRI(regval) (DCFG_EOPFT & ((regval) << 11)) /*!< end of periodic frame time configuration */
#define FRAME_INTERVAL_80 DCFG_PFRI(0U) /*!< 80% of the frame time */
#define FRAME_INTERVAL_85 DCFG_PFRI(1U) /*!< 85% of the frame time */
#define FRAME_INTERVAL_90 DCFG_PFRI(2U) /*!< 90% of the frame time */
#define FRAME_INTERVAL_95 DCFG_PFRI(3U) /*!< 95% of the frame time */
#define DCFG_DEVSPEED(regval) (DCFG_DS & ((regval) << 0)) /*!< device speed configuration */
#define USB_SPEED_EXP_HIGH DCFG_DEVSPEED(0U) /*!< device external PHY high speed */
#define USB_SPEED_EXP_FULL DCFG_DEVSPEED(1U) /*!< device external PHY full speed */
#define USB_SPEED_INP_FULL DCFG_DEVSPEED(3U) /*!< device internal PHY full speed */
#define DEP0_MPL(regval) (DEP0CTL_MPL & ((regval) << 0)) /*!< maximum packet length configuration */
#define EP0MPL_64 DEP0_MPL(0U) /*!< maximum packet length 64 bytes */
#define EP0MPL_32 DEP0_MPL(1U) /*!< maximum packet length 32 bytes */
#define EP0MPL_16 DEP0_MPL(2U) /*!< maximum packet length 16 bytes */
#define EP0MPL_8 DEP0_MPL(3U) /*!< maximum packet length 8 bytes */
#define DOEP0_TLEN(regval) (DOEP0LEN_TLEN & ((regval) << 0)) /*!< transfer length */
#define DOEP0_PCNT(regval) (DOEP0LEN_PCNT & ((regval) << 19)) /*!< packet count */
#define DOEP0_STPCNT(regval) (DOEP0LEN_STPCNT & ((regval) << 29)) /*!< SETUP packet count */
#define USB_ULPI_PHY 1U /*!< ULPI interface external PHY */
#define USB_EMBEDDED_PHY 2U /*!< embedded PHY */
#define GRXSTS_PKTSTS_IN 2U
#define GRXSTS_PKTSTS_IN_XFER_COMP 3U
#define GRXSTS_PKTSTS_DATA_TOGGLE_ERR 5U
#define GRXSTS_PKTSTS_CH_HALTED 7U
#define HCTL_30_60MHZ 0U /*!< USB clock 30-60MHZ */
#define HCTL_48MHZ 1U /*!< USB clock 48MHZ */
#define HCTL_6MHZ 2U /*!< USB clock 6MHZ */
#define EP0_OUT ((uint8_t)0x00U) /*!< endpoint OUT 0 */
#define EP0_IN ((uint8_t)0x80U) /*!< endpoint IN 0 */
#define EP1_OUT ((uint8_t)0x01U) /*!< endpoint OUT 1 */
#define EP1_IN ((uint8_t)0x81U) /*!< endpoint IN 1 */
#define EP2_OUT ((uint8_t)0x02U) /*!< endpoint OUT 2 */
#define EP2_IN ((uint8_t)0x82U) /*!< endpoint IN 2 */
#define EP3_OUT ((uint8_t)0x03U) /*!< endpoint OUT 3 */
#define EP3_IN ((uint8_t)0x83U) /*!< endpoint IN 3 */
#define EP4_OUT ((uint8_t)0x04U) /*!< endpoint OUT 4 */
#define EP4_IN ((uint8_t)0x84U) /*!< endpoint IN 4 */
#define EP5_OUT ((uint8_t)0x05U) /*!< endpoint OUT 5 */
#define EP5_IN ((uint8_t)0x85U) /*!< endpoint IN 5 */
#endif /* DRV_USB_REGS_H */
@@ -0,0 +1,50 @@
/*!
\file drv_usbd_int.h
\brief USB device mode interrupt 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 DRV_USBD_INT_H
#define DRV_USBD_INT_H
#include "drv_usb_dev.h"
/* function declarations */
/* USB device-mode interrupts global service routine handler */
void usbd_isr(usb_core_driver *udev);
#ifdef USB_HS_DEDICATED_EP1_ENABLED
/* USB dedicated IN endpoint 1 interrupt service routine handler */
uint32_t usbd_int_dedicated_ep1in(usb_core_driver *udev);
/* USB dedicated OUT endpoint 1 interrupt service routine handler */
uint32_t usbd_int_dedicated_ep1out(usb_core_driver *udev);
#endif /* USB_HS_DEDICATED_EP1_ENABLED */
#endif /* DRV_USBD_INT_H */
@@ -0,0 +1,53 @@
/*!
\file drv_usbh_int.h.h
\brief USB host mode interrupt management 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 DRV_USBH_INT_H
#define DRV_USBH_INT_H
#include "drv_usb_host.h"
#include "usbh_core.h"
typedef struct _usbh_ev_cb {
uint8_t (*connect)(usbh_host *uhost);
uint8_t (*disconnect)(usbh_host *uhost);
uint8_t (*SOF)(usbh_host *uhost);
} usbh_ev_cb;
extern usbh_ev_cb *usbh_int_fop;
/* function declarations */
/* handle global host interrupt */
uint32_t usbh_isr(usb_core_driver *udev);
#endif /* DRV_USBH_INT_H */
@@ -0,0 +1,371 @@
/*!
\file drv_usb_core.c
\brief USB core driver which can operate in host and device mode
\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 "drv_usb_core.h"
#include "drv_usb_hw.h"
/* local function prototypes ('static') */
static void usb_core_reset(usb_core_regs *usb_regs);
/*!
\brief configure USB core basic
\param[in] usb_basic: pointer to USB capabilities
\param[in] usb_regs: USB core registers
\param[in] usb_core: USB core
\param[out] none
\retval operation status
*/
usb_status usb_basic_init(usb_core_basic *usb_basic, \
usb_core_regs *usb_regs, \
usb_core_enum usb_core)
{
/* configure USB default transfer mode as FIFO mode */
usb_basic->transfer_mode = (uint8_t)USB_USE_FIFO;
/* USB default speed is full-speed */
usb_basic->core_speed = (uint8_t)USB_SPEED_FULL;
usb_basic->core_enum = (uint8_t)usb_core;
switch(usb_core) {
case USB_CORE_ENUM_HS:
usb_basic->base_reg = (uint32_t)USBHS_REG_BASE;
/* set the host channel numbers */
usb_basic->num_pipe = USBHS_MAX_CHANNEL_COUNT;
/* set the device endpoint numbers */
usb_basic->num_ep = USBHS_MAX_EP_COUNT;
#ifdef USB_ULPI_PHY_ENABLED
usb_basic->phy_itf = USB_ULPI_PHY;
#else
usb_basic->phy_itf = USB_EMBEDDED_PHY;
#endif /* USB_ULPI_PHY_ENABLED */
#ifdef USB_HS_INTERNAL_DMA_ENABLED
usb_basic->transfer_mode = USB_USE_DMA;
#endif /* USB_HS_INTERNAL_DMA_ENABLED */
#ifdef USB_HS_CORE
/* configure the SOF output and the low power support */
usb_basic->sof_enable = USBHS_SOF_OUTPUT;
usb_basic->low_power = USBHS_LOW_POWER;
#endif /* USB_HS_CORE */
break;
case USB_CORE_ENUM_FS:
usb_basic->base_reg = (uint32_t)USBFS_REG_BASE;
/* set the host channel numbers */
usb_basic->num_pipe = USBFS_MAX_CHANNEL_COUNT;
/* set the device endpoint numbers */
usb_basic->num_ep = USBFS_MAX_EP_COUNT;
/* USBFS core use embedded physical layer */
usb_basic->phy_itf = USB_EMBEDDED_PHY;
#ifdef USB_FS_CORE
/* configure the SOF output and the low power support */
usb_basic->sof_enable = USBFS_SOF_OUTPUT;
usb_basic->low_power = USBFS_LOW_POWER;
#endif /* USB_FS_CORE */
break;
default:
return USB_FAIL;
}
/* assign main registers address */
*usb_regs = (usb_core_regs) {
.gr = (usb_gr *)(usb_basic->base_reg + USB_REG_OFFSET_CORE),
.hr = (usb_hr *)(usb_basic->base_reg + USB_REG_OFFSET_HOST),
.dr = (usb_dr *)(usb_basic->base_reg + USB_REG_OFFSET_DEV),
.HPCS = (uint32_t *)(usb_basic->base_reg + USB_REG_OFFSET_PORT),
.PWRCLKCTL = (uint32_t *)(usb_basic->base_reg + USB_REG_OFFSET_PWRCLKCTL)
};
/* assign device endpoint registers address */
for(uint8_t i = 0U; i < usb_basic->num_ep; i++) {
usb_regs->er_in[i] = (usb_erin *) \
(usb_basic->base_reg + USB_REG_OFFSET_EP_IN + (i * USB_REG_OFFSET_EP));
usb_regs->er_out[i] = (usb_erout *)\
(usb_basic->base_reg + USB_REG_OFFSET_EP_OUT + (i * USB_REG_OFFSET_EP));
}
/* assign host pipe registers address */
for(uint8_t i = 0U; i < usb_basic->num_pipe; i++) {
usb_regs->pr[i] = (usb_pr *) \
(usb_basic->base_reg + USB_REG_OFFSET_CH_INOUT + (i * USB_REG_OFFSET_CH));
usb_regs->DFIFO[i] = (uint32_t *) \
(usb_basic->base_reg + USB_DATA_FIFO_OFFSET + (i * USB_DATA_FIFO_SIZE));
}
return USB_OK;
}
/*!
\brief initializes the USB controller registers and
prepares the core device mode or host mode operation
\param[in] usb_basic: pointer to USB capabilities
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval operation status
*/
usb_status usb_core_init(usb_core_basic usb_basic, usb_core_regs *usb_regs)
{
if(USB_ULPI_PHY == usb_basic.phy_itf) {
usb_regs->gr->GCCFG &= ~GCCFG_PWRON;
if(usb_basic.sof_enable) {
usb_regs->gr->GCCFG |= GCCFG_SOFOEN;
}
/* initialize the ULPI interface */
usb_regs->gr->GUSBCS &= ~(GUSBCS_EMBPHY | GUSBCS_ULPIEOI);
#ifdef USBHS_EXTERNAL_VBUS_ENABLED
/* use external VBUS driver */
usb_regs->gr->GUSBCS |= GUSBCS_ULPIEVD;
#else
/* use internal VBUS driver */
usb_regs->gr->GUSBCS &= ~GUSBCS_ULPIEVD;
#endif /* USBHS_EXTERNAL_VBUS_ENABLED */
/* soft reset the core */
usb_core_reset(usb_regs);
} else {
usb_regs->gr->GUSBCS |= GUSBCS_EMBPHY;
/* soft reset the core */
usb_core_reset(usb_regs);
/* active the transceiver and enable VBUS sensing */
usb_regs->gr->GCCFG |= GCCFG_PWRON | GCCFG_VBUSACEN | GCCFG_VBUSBCEN;
#ifndef VBUS_SENSING_ENABLED
usb_regs->gr->GCCFG |= GCCFG_VBUSIG;
#endif /* VBUS_SENSING_ENABLED */
/* enable SOF output */
if(usb_basic.sof_enable) {
usb_regs->gr->GCCFG |= GCCFG_SOFOEN;
}
usb_mdelay(20U);
}
if((uint8_t)USB_USE_DMA == usb_basic.transfer_mode) {
usb_regs->gr->GAHBCS &= ~GAHBCS_BURST;
usb_regs->gr->GAHBCS |= DMA_INCR8 | GAHBCS_DMAEN;
}
#ifdef USE_OTG_MODE
/* enable USB OTG features */
usb_regs->gr->GUSBCS |= GUSBCS_HNPCEN | GUSBCS_SRPCEN;
/* enable the USB wakeup and suspend interrupts */
usb_regs->gr->GINTF = 0xBFFFFFFFU;
usb_regs->gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE | \
GINTEN_OTGIE | GINTEN_SESIE | GINTEN_CIDPSCIE;
#endif /* USE_OTG_MODE */
return USB_OK;
}
/*!
\brief write a packet into the TX FIFO associated with the endpoint
\param[in] usb_regs: pointer to USB core registers
\param[in] src_buf: pointer to source buffer
\param[in] fifo_num: FIFO number which is in (0..3 or 0..5)
\param[in] byte_count: packet byte count
\param[out] none
\retval operation status
*/
usb_status usb_txfifo_write(usb_core_regs *usb_regs, \
uint8_t *src_buf, \
uint8_t fifo_num, \
uint16_t byte_count)
{
uint32_t word_count = (byte_count + 3U) / 4U;
__IO uint32_t *fifo = usb_regs->DFIFO[fifo_num];
while(word_count-- > 0U) {
*fifo = *((uint32_t *)src_buf);
src_buf += 4U;
}
return USB_OK;
}
/*!
\brief read a packet from the RX FIFO associated with the endpoint
\param[in] usb_regs: pointer to USB core registers
\param[in] dest_buf: pointer to destination buffer
\param[in] byte_count: packet byte count
\param[out] none
\retval void type pointer
*/
void *usb_rxfifo_read(usb_core_regs *usb_regs, uint8_t *dest_buf, uint16_t byte_count)
{
uint32_t word_count = (byte_count + 3U) / 4U;
__IO uint32_t *fifo = usb_regs->DFIFO[0];
while(word_count-- > 0U) {
*(uint32_t *)dest_buf = *fifo;
dest_buf += 4U;
}
return ((void *)dest_buf);
}
/*!
\brief flush a TX FIFO or all TX FIFOs
\param[in] usb_regs: pointer to USB core registers
\param[in] fifo_num: FIFO number which is in (0..3 or 0..5)
\param[out] none
\retval operation status
*/
usb_status usb_txfifo_flush(usb_core_regs *usb_regs, uint8_t fifo_num)
{
usb_regs->gr->GRSTCTL = ((uint32_t)fifo_num << 6) | GRSTCTL_TXFF;
/* wait for TX FIFO flush bit is set */
while(usb_regs->gr->GRSTCTL & GRSTCTL_TXFF) {
/* no operation */
}
/* wait for 3 PHY clocks*/
usb_udelay(3U);
return USB_OK;
}
/*!
\brief flush the entire RX FIFO
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval operation status
*/
usb_status usb_rxfifo_flush(usb_core_regs *usb_regs)
{
usb_regs->gr->GRSTCTL = GRSTCTL_RXFF;
/* wait for RX FIFO flush bit is set */
while(usb_regs->gr->GRSTCTL & GRSTCTL_RXFF) {
/* no operation */
}
/* wait for 3 PHY clocks */
usb_udelay(3U);
return USB_OK;
}
/*!
\brief set endpoint or channel TX FIFO size
\param[in] usb_regs: pointer to USB core registers
\param[in] fifo: TX FIFO number
\param[in] size: assigned TX FIFO size
\param[out] none
\retval none
*/
void usb_set_txfifo(usb_core_regs *usb_regs, uint8_t fifo, uint16_t size)
{
uint32_t tx_offset = usb_regs->gr->GRFLEN;
if(0U == fifo) {
usb_regs->gr->DIEP0TFLEN_HNPTFLEN = ((uint32_t)size << 16) | tx_offset;
} else {
tx_offset += (usb_regs->gr->DIEP0TFLEN_HNPTFLEN) >> 16;
for(uint8_t i = 0U; i < (fifo - 1U); i++) {
tx_offset += (usb_regs->gr->DIEPTFLEN[i] >> 16);
}
/* multiply Tx_Size by 2 to get higher performance */
usb_regs->gr->DIEPTFLEN[fifo - 1U] = ((uint32_t)size << 16) | tx_offset;
}
}
/*!
\brief set USB current mode
\param[in] usb_regs: pointer to USB core registers
\param[in] mode: USB current mode
\param[out] none
\retval none
*/
void usb_curmode_set(usb_core_regs *usb_regs, uint8_t mode)
{
usb_regs->gr->GUSBCS &= ~(GUSBCS_FDM | GUSBCS_FHM);
if(DEVICE_MODE == mode) {
usb_regs->gr->GUSBCS |= GUSBCS_FDM;
} else if(HOST_MODE == mode) {
usb_regs->gr->GUSBCS |= GUSBCS_FHM;
} else {
/* OTG mode and other mode can not be here! */
}
}
/*!
\brief configure USB core to soft reset
\param[in] usb_regs: pointer to USB core registers
\param[out] none
\retval none
*/
static void usb_core_reset(usb_core_regs *usb_regs)
{
/* enable core soft reset */
usb_regs->gr->GRSTCTL |= GRSTCTL_CSRST;
/* wait for the core to be soft reset */
while(usb_regs->gr->GRSTCTL & GRSTCTL_CSRST) {
/* no operation */
}
/* wait for additional 3 PHY clocks */
usb_udelay(3U);
}
@@ -0,0 +1,660 @@
/*!
\file drv_usb_dev.c
\brief USB device mode low level 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 "drv_usb_hw.h"
#include "drv_usb_dev.h"
/* endpoint 0 max packet length */
static const uint8_t EP0_MAXLEN[4] = {
[DSTAT_EM_HS_PHY_30MHZ_60MHZ] = EP0MPL_64,
[DSTAT_EM_FS_PHY_30MHZ_60MHZ] = EP0MPL_64,
[DSTAT_EM_FS_PHY_48MHZ] = EP0MPL_64,
[DSTAT_EM_LS_PHY_6MHZ] = EP0MPL_8
};
#ifdef USB_FS_CORE
/* USBFS endpoint TX FIFO size */
static uint16_t USBFS_TX_FIFO_SIZE[USBFS_MAX_EP_COUNT] = {
(uint16_t)TX0_FIFO_FS_SIZE,
(uint16_t)TX1_FIFO_FS_SIZE,
(uint16_t)TX2_FIFO_FS_SIZE,
(uint16_t)TX3_FIFO_FS_SIZE
};
#endif /* USBFS_CORE */
#ifdef USB_HS_CORE
/* USBHS endpoint TX FIFO size */
uint16_t USBHS_TX_FIFO_SIZE[USBHS_MAX_EP_COUNT] = {
(uint16_t)TX0_FIFO_HS_SIZE,
(uint16_t)TX1_FIFO_HS_SIZE,
(uint16_t)TX2_FIFO_HS_SIZE,
(uint16_t)TX3_FIFO_HS_SIZE,
(uint16_t)TX4_FIFO_HS_SIZE,
(uint16_t)TX5_FIFO_HS_SIZE
};
#endif /* USBHS_CORE */
/*!
\brief initialize USB core registers for device mode
\param[in] udev: pointer to USB device
\param[out] none
\retval operation status
*/
usb_status usb_devcore_init(usb_core_driver *udev)
{
uint8_t i = 0U;
/* restart the PHY clock (maybe don't need to...) */
*udev->regs.PWRCLKCTL = 0U;
/* configure periodic frame interval to default value */
udev->regs.dr->DCFG &= ~DCFG_EOPFT;
udev->regs.dr->DCFG |= FRAME_INTERVAL_80;
udev->regs.dr->DCFG &= ~DCFG_DS;
#ifdef USB_FS_CORE
if((uint8_t)USB_CORE_ENUM_FS == udev->bp.core_enum) {
/* set full-speed PHY */
udev->regs.dr->DCFG |= USB_SPEED_INP_FULL;
/* set RX FIFO size */
usb_set_rxfifo(&udev->regs, RX_FIFO_FS_SIZE);
/* set endpoint 0 to 3's TX FIFO length and RAM address */
for(i = 0U; i < USBFS_MAX_EP_COUNT; i++) {
usb_set_txfifo(&udev->regs, i, USBFS_TX_FIFO_SIZE[i]);
}
}
#endif /* USB_FS_CORE */
#ifdef USB_HS_CORE
if(USB_CORE_ENUM_HS == udev->bp.core_enum) {
if(USB_ULPI_PHY == udev->bp.phy_itf) {
udev->regs.dr->DCFG |= USB_SPEED_EXP_HIGH;
} else {/* set high speed PHY in full speed mode */
udev->regs.dr->DCFG |= USB_SPEED_EXP_FULL;
}
/* Set RX FIFO size */
usb_set_rxfifo(&udev->regs, RX_FIFO_HS_SIZE);
/* set endpoint 0 to 6's TX FIFO length and RAM address */
for(i = 0U; i < USBHS_MAX_EP_COUNT; i++) {
usb_set_txfifo(&udev->regs, i, USBHS_TX_FIFO_SIZE[i]);
}
}
#endif /* USB_FS_CORE */
/* make sure all FIFOs are flushed */
/* flush all TX FIFOs */
(void)usb_txfifo_flush(&udev->regs, 0x10U);
/* flush entire RX FIFO */
(void)usb_rxfifo_flush(&udev->regs);
/* clear all pending device interrupts */
udev->regs.dr->DIEPINTEN = 0U;
udev->regs.dr->DOEPINTEN = 0U;
udev->regs.dr->DAEPINT = 0xFFFFFFFFU;
udev->regs.dr->DAEPINTEN = 0U;
/* configure all IN/OUT endpoints */
for(i = 0U; i < udev->bp.num_ep; i++) {
if(udev->regs.er_in[i]->DIEPCTL & DEPCTL_EPEN) {
udev->regs.er_in[i]->DIEPCTL |= DEPCTL_EPD | DEPCTL_SNAK;
} else {
udev->regs.er_in[i]->DIEPCTL = 0U;
}
/* set IN endpoint transfer length to 0 */
udev->regs.er_in[i]->DIEPLEN = 0U;
/* clear all pending IN endpoint interrupts */
udev->regs.er_in[i]->DIEPINTF = 0xFFU;
if(udev->regs.er_out[i]->DOEPCTL & DEPCTL_EPEN) {
udev->regs.er_out[i]->DOEPCTL |= DEPCTL_EPD | DEPCTL_SNAK;
} else {
udev->regs.er_out[i]->DOEPCTL = 0U;
}
/* set OUT endpoint transfer length to 0 */
udev->regs.er_out[i]->DOEPLEN = 0U;
/* clear all pending OUT endpoint interrupts */
udev->regs.er_out[i]->DOEPINTF = 0xFFU;
}
udev->regs.dr->DIEPINTEN |= DIEPINTEN_EPTXFUDEN;
(void)usb_devint_enable(udev);
return USB_OK;
}
/*!
\brief enable the USB device mode interrupts
\param[in] udev: pointer to USB device
\param[out] none
\retval operation status
*/
usb_status usb_devint_enable(usb_core_driver *udev)
{
/* clear any pending USB OTG interrupts */
udev->regs.gr->GOTGINTF = 0xFFFFFFFFU;
/* clear any pending interrupts */
udev->regs.gr->GINTF = 0xBFFFFFFFU;
/* enable the USB wakeup and suspend interrupts */
udev->regs.gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE;
/* enable device_mode-related interrupts */
if((uint8_t)USB_USE_FIFO == udev->bp.transfer_mode) {
udev->regs.gr->GINTEN |= GINTEN_RXFNEIE;
}
udev->regs.gr->GINTEN |= GINTEN_RSTIE | GINTEN_ENUMFIE | GINTEN_IEPIE | \
GINTEN_OEPIE | GINTEN_SOFIE | GINTEN_ISOONCIE | GINTEN_ISOINCIE;
#ifdef VBUS_SENSING_ENABLED
udev->regs.gr->GINTEN |= GINTEN_SESIE | GINTEN_OTGIE;
#endif /* VBUS_SENSING_ENABLED */
return USB_OK;
}
/*!
\brief active the USB endpoint0 transaction
\param[in] udev: pointer to USB device
\param[in] transc: the USB endpoint0 transaction
\param[out] none
\retval operation status
*/
usb_status usb_transc0_active(usb_core_driver *udev, usb_transc *transc)
{
__IO uint32_t *reg_addr = NULL;
uint32_t enum_speed = udev->regs.dr->DSTAT & DSTAT_ES;
/* get the endpoint number */
uint8_t ep_num = transc->ep_addr.num;
if(ep_num) {
/* not endpoint 0 */
return USB_FAIL;
}
if(transc->ep_addr.dir) {
reg_addr = &udev->regs.er_in[0]->DIEPCTL;
} else {
reg_addr = &udev->regs.er_out[0]->DOEPCTL;
}
/* endpoint 0 is activated after USB clock is enabled */
*reg_addr &= ~(DEPCTL_MPL | DEPCTL_EPTYPE | DIEPCTL_TXFNUM);
/* set endpoint 0 maximum packet length */
*reg_addr |= EP0_MAXLEN[enum_speed];
/* activate endpoint */
*reg_addr |= ((uint32_t)transc->ep_type << 18) | ((uint32_t)ep_num << 22) | DEPCTL_SD0PID | DEPCTL_EPACT;
return USB_OK;
}
/*!
\brief active the USB transaction
\param[in] udev: pointer to USB device
\param[in] transc: the USB transaction
\param[out] none
\retval status
*/
usb_status usb_transc_active(usb_core_driver *udev, usb_transc *transc)
{
__IO uint32_t *reg_addr = NULL;
uint32_t epinten = 0U;
uint32_t enum_speed = udev->regs.dr->DSTAT & DSTAT_ES;
/* get the endpoint number */
uint8_t ep_num = transc->ep_addr.num;
/* enable endpoint interrupt number */
if(transc->ep_addr.dir) {
reg_addr = &udev->regs.er_in[ep_num]->DIEPCTL;
epinten = 1U << ep_num;
} else {
reg_addr = &udev->regs.er_out[ep_num]->DOEPCTL;
epinten = 1U << (16U + ep_num);
}
/* if the endpoint is not active, need change the endpoint control register */
if(!(*reg_addr & DEPCTL_EPACT)) {
*reg_addr &= ~(DEPCTL_MPL | DEPCTL_EPTYPE | DIEPCTL_TXFNUM);
/* set endpoint maximum packet length */
if(0U == ep_num) {
*reg_addr |= EP0_MAXLEN[enum_speed];
} else {
*reg_addr |= transc->max_len;
}
/* activate endpoint */
*reg_addr |= ((uint32_t)transc->ep_type << 18) | ((uint32_t)ep_num << 22) | DEPCTL_SD0PID | DEPCTL_EPACT;
}
#ifdef USB_HS_DEDICATED_EP1_ENABLED
if((1U == ep_num) && (USB_CORE_ENUM_HS == udev->bp.core_enum)) {
udev->regs.dr->DEP1INTEN |= epinten;
} else
#endif /* USB_HS_DEDICATED_EP1_ENABLED */
{
/* enable the interrupts for this endpoint */
udev->regs.dr->DAEPINTEN |= epinten;
}
return USB_OK;
}
/*!
\brief deactivate the USB transaction
\param[in] udev: pointer to USB device
\param[in] transc: the USB transaction
\param[out] none
\retval status
*/
usb_status usb_transc_deactivate(usb_core_driver *udev, usb_transc *transc)
{
uint32_t epinten = 0U;
uint8_t ep_num = transc->ep_addr.num;
/* disable endpoint interrupt number */
if(transc->ep_addr.dir) {
epinten = 1U << ep_num;
udev->regs.er_in[ep_num]->DIEPCTL &= ~DEPCTL_EPACT;
} else {
epinten = 1U << (ep_num + 16U);
udev->regs.er_out[ep_num]->DOEPCTL &= ~DEPCTL_EPACT;
}
#ifdef USB_HS_DEDICATED_EP1_ENABLED
if((1U == ep_num) && (USB_CORE_ENUM_HS == udev->bp.core_enum)) {
udev->regs.dr->DEP1INTEN &= ~epinten;
} else
#endif /* USB_HS_DEDICATED_EP1_ENABLED */
{
/* disable the interrupts for this endpoint */
udev->regs.dr->DAEPINTEN &= ~epinten;
}
return USB_OK;
}
/*!
\brief configure USB transaction to start IN transfer
\param[in] udev: pointer to USB device
\param[in] transc: the USB IN transaction
\param[out] none
\retval operation status
*/
usb_status usb_transc_inxfer(usb_core_driver *udev, usb_transc *transc)
{
usb_status status = USB_OK;
uint8_t ep_num = transc->ep_addr.num;
__IO uint32_t epctl = udev->regs.er_in[ep_num]->DIEPCTL;
__IO uint32_t eplen = udev->regs.er_in[ep_num]->DIEPLEN;
eplen &= ~(DEPLEN_TLEN | DEPLEN_PCNT);
/* zero length packet or endpoint 0 */
if(0U == transc->xfer_len) {
/* set transfer packet count to 1 */
eplen |= 1U << 19;
} else {
/* set transfer packet count */
if(0U == ep_num) {
transc->xfer_len = USB_MIN(transc->xfer_len, transc->max_len);
eplen |= 1U << 19;
} else {
eplen |= (((transc->xfer_len - 1U) + transc->max_len) / transc->max_len) << 19;
}
/* set endpoint transfer length */
eplen |= transc->xfer_len;
if((uint8_t)USB_EPTYPE_ISOC == transc->ep_type) {
eplen |= DIEPLEN_MCNT & (1U << 29);
}
}
udev->regs.er_in[ep_num]->DIEPLEN = eplen;
if((uint8_t)USB_EPTYPE_ISOC == transc->ep_type) {
if(((udev->regs.dr->DSTAT & DSTAT_FNRSOF) >> 8) & 0x01U) {
epctl |= DEPCTL_SEVNFRM;
} else {
epctl |= DEPCTL_SODDFRM;
}
}
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
udev->regs.er_in[ep_num]->DIEPDMAADDR = transc->dma_addr;
}
/* enable the endpoint and clear the NAK */
epctl |= DEPCTL_CNAK | DEPCTL_EPEN;
udev->regs.er_in[ep_num]->DIEPCTL = epctl;
if((uint8_t)USB_USE_FIFO == udev->bp.transfer_mode) {
if((uint8_t)USB_EPTYPE_ISOC != transc->ep_type) {
/* enable the TX FIFO empty interrupt for this endpoint */
if(transc->xfer_len > 0U) {
udev->regs.dr->DIEPFEINTEN |= 1U << ep_num;
}
} else {
(void)usb_txfifo_write(&udev->regs, transc->xfer_buf, ep_num, (uint16_t)transc->xfer_len);
}
}
return status;
}
/*!
\brief configure USB transaction to start OUT transfer
\param[in] udev: pointer to USB device
\param[in] transc: the USB OUT transaction
\param[out] none
\retval status
*/
usb_status usb_transc_outxfer(usb_core_driver *udev, usb_transc *transc)
{
usb_status status = USB_OK;
uint8_t ep_num = transc->ep_addr.num;
uint32_t epctl = udev->regs.er_out[ep_num]->DOEPCTL;
uint32_t eplen = udev->regs.er_out[ep_num]->DOEPLEN;
eplen &= ~(DEPLEN_TLEN | DEPLEN_PCNT);
/* zero length packet or endpoint 0 */
if((0U == transc->xfer_len) || (0U == ep_num)) {
/* set the transfer length to max packet size */
eplen |= transc->max_len;
/* set the transfer packet count to 1 */
eplen |= 1U << 19;
} else {
/* configure the transfer size and packet count as follows:
* pktcnt = N
* xfersize = N * maxpacket
*/
uint32_t packet_count = (transc->xfer_len + transc->max_len - 1U) / transc->max_len;
eplen |= packet_count << 19;
eplen |= packet_count * transc->max_len;
#ifdef INT_HIGH_BW
if(transc->ep_type == (uint8_t)USB_EPTYPE_INTR) {
eplen |= DIEPLEN_MCNT & (3U << 29);
}
#endif /* INT_HIGH_BW */
}
udev->regs.er_out[ep_num]->DOEPLEN = eplen;
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
udev->regs.er_out[ep_num]->DOEPDMAADDR = transc->dma_addr;
}
if((uint8_t)USB_EPTYPE_ISOC == transc->ep_type) {
if(transc->frame_num) {
epctl |= DEPCTL_SD1PID;
} else {
epctl |= DEPCTL_SD0PID;
}
}
/* enable the endpoint and clear the NAK */
epctl |= DEPCTL_EPEN | DEPCTL_CNAK;
udev->regs.er_out[ep_num]->DOEPCTL = epctl;
return status;
}
/*!
\brief set the USB transaction STALL status
\param[in] udev: pointer to USB device
\param[in] transc: the USB transaction
\param[out] none
\retval status
*/
usb_status usb_transc_stall(usb_core_driver *udev, usb_transc *transc)
{
__IO uint32_t *reg_addr = NULL;
uint8_t ep_num = transc->ep_addr.num;
if(transc->ep_addr.dir) {
reg_addr = &(udev->regs.er_in[ep_num]->DIEPCTL);
/* set the endpoint disable bit */
if(*reg_addr & DEPCTL_EPEN) {
*reg_addr |= DEPCTL_EPD;
}
} else {
/* set the endpoint STALL bit */
reg_addr = &(udev->regs.er_out[ep_num]->DOEPCTL);
}
/* set the endpoint STALL bit */
*reg_addr |= DEPCTL_STALL;
return USB_OK;
}
/*!
\brief clear the USB transaction STALL status
\param[in] udev: pointer to USB device
\param[in] transc: the USB transaction
\param[out] none
\retval operation status
*/
usb_status usb_transc_clrstall(usb_core_driver *udev, usb_transc *transc)
{
__IO uint32_t *reg_addr = NULL;
uint8_t ep_num = transc->ep_addr.num;
if(transc->ep_addr.dir) {
reg_addr = &(udev->regs.er_in[ep_num]->DIEPCTL);
} else {
reg_addr = &(udev->regs.er_out[ep_num]->DOEPCTL);
}
/* clear the endpoint STALL bit */
*reg_addr &= ~DEPCTL_STALL;
/* reset data PID of the periodic endpoints */
if(((uint8_t)USB_EPTYPE_INTR == transc->ep_type) || ((uint8_t)USB_EPTYPE_BULK == transc->ep_type)) {
*reg_addr |= DEPCTL_SD0PID;
}
return USB_OK;
}
/*!
\brief read device IN endpoint interrupt flag register
\param[in] udev: pointer to USB device
\param[in] ep_num: endpoint number
\param[out] none
\retval interrupt value
*/
uint32_t usb_iepintr_read(usb_core_driver *udev, uint8_t ep_num)
{
uint32_t value = 0U, fifoemptymask, commonintmask;
commonintmask = udev->regs.dr->DIEPINTEN;
fifoemptymask = udev->regs.dr->DIEPFEINTEN;
/* check FIFO empty interrupt enable bit */
commonintmask |= ((fifoemptymask >> ep_num) & 0x1U) << 7;
value = udev->regs.er_in[ep_num]->DIEPINTF & commonintmask;
return value;
}
/*!
\brief configures OUT endpoint 0 to receive SETUP packets
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_ctlep_startout(usb_core_driver *udev)
{
/* set OUT endpoint 0 receive length to 24 bytes, 1 packet and 3 SETUP packets */
udev->regs.er_out[0]->DOEPLEN = DOEP0_TLEN(8U * 3U) | DOEP0_PCNT(1U) | DOEP0_STPCNT(3U);
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
udev->regs.er_out[0]->DOEPDMAADDR = (uint32_t)&udev->dev.control.req;
/* endpoint enable */
udev->regs.er_out[0]->DOEPCTL |= DEPCTL_EPACT | DEPCTL_EPEN;
}
}
/*!
\brief active remote wakeup signaling
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_rwkup_active(usb_core_driver *udev)
{
if(udev->dev.pm.dev_remote_wakeup) {
if(udev->regs.dr->DSTAT & DSTAT_SPST) {
if(udev->bp.low_power) {
/* ungate USB core clock */
*udev->regs.PWRCLKCTL &= ~(PWRCLKCTL_SHCLK | PWRCLKCTL_SUCLK);
}
/* active remote wakeup signaling */
udev->regs.dr->DCTL |= DCTL_RWKUP;
usb_mdelay(5U);
udev->regs.dr->DCTL &= ~DCTL_RWKUP;
}
}
}
/*!
\brief active USB core clock
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_clock_active(usb_core_driver *udev)
{
if(udev->bp.low_power) {
if(udev->regs.dr->DSTAT & DSTAT_SPST) {
/* ungate USB Core clock */
*udev->regs.PWRCLKCTL &= ~(PWRCLKCTL_SHCLK | PWRCLKCTL_SUCLK);
}
}
}
/*!
\brief USB device suspend
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_dev_suspend(usb_core_driver *udev)
{
__IO uint32_t devstat = udev->regs.dr->DSTAT;
if((udev->bp.low_power) && (devstat & DSTAT_SPST)) {
/* switch-off the USB clocks */
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SHCLK;
/* enter DEEP_SLEEP mode with LDO in low power mode */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_DISABLE, WFI_CMD);
}
}
/*!
\brief stop the device and clean up FIFOs
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_dev_stop(usb_core_driver *udev)
{
uint32_t i;
udev->dev.cur_status = 1U;
/* clear all interrupt flag and enable bits */
for(i = 0U; i < udev->bp.num_ep; i++) {
udev->regs.er_in[i]->DIEPINTF = 0xFFU;
udev->regs.er_out[i]->DOEPINTF = 0xFFU;
}
udev->regs.dr->DIEPINTEN = 0U;
udev->regs.dr->DOEPINTEN = 0U;
udev->regs.dr->DAEPINTEN = 0U;
udev->regs.dr->DAEPINT = 0xFFFFFFFFU;
/* flush the FIFO */
(void)usb_rxfifo_flush(&udev->regs);
(void)usb_txfifo_flush(&udev->regs, 0x10U);
}
@@ -0,0 +1,472 @@
/*!
\file drv_usb_host.c
\brief USB host mode low level 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 "drv_usb_hw.h"
#include "drv_usb_host.h"
const uint32_t PIPE_DPID[2] = {
PIPE_DPID_DATA0,
PIPE_DPID_DATA1
};
/*!
\brief initializes USB core for host mode
\param[in] udev: pointer to selected USB host
\param[out] none
\retval operation status
*/
usb_status usb_host_init(usb_core_driver *udev)
{
uint32_t i = 0U, inten = 0U;
uint32_t nptxfifolen = 0U;
uint32_t ptxfifolen = 0U;
/* restart the PHY Clock */
*udev->regs.PWRCLKCTL = 0U;
/* configure USB clock of PHY */
if(USB_ULPI_PHY == udev->bp.phy_itf) {
usb_phyclock_config(udev, HCTL_30_60MHZ);
} else {
usb_phyclock_config(udev, HCTL_48MHZ);
}
/* support FS/LS only */
udev->regs.hr->HCTL &= ~HCTL_SPDFSLS;
/* configure data FIFOs size */
#ifdef USB_FS_CORE
if(USB_CORE_ENUM_FS == udev->bp.core_enum) {
/* set RX FIFO size */
udev->regs.gr->GRFLEN = USB_RX_FIFO_FS_SIZE;
/* set non-periodic TX FIFO size and address */
nptxfifolen |= USB_RX_FIFO_FS_SIZE;
nptxfifolen |= USB_HTX_NPFIFO_FS_SIZE << 16;
udev->regs.gr->DIEP0TFLEN_HNPTFLEN = nptxfifolen;
/* set periodic TX FIFO size and address */
ptxfifolen |= USB_RX_FIFO_FS_SIZE + USB_HTX_NPFIFO_FS_SIZE;
ptxfifolen |= USB_HTX_PFIFO_FS_SIZE << 16;
udev->regs.gr->HPTFLEN = ptxfifolen;
}
#endif /* USB_FS_CORE */
#ifdef USB_HS_CORE
if(USB_CORE_ENUM_HS == udev->bp.core_enum) {
/* set RX FIFO size */
udev->regs.gr->GRFLEN = USB_RX_FIFO_HS_SIZE;
/* set non-periodic TX FIFO size and address */
nptxfifolen |= USB_RX_FIFO_HS_SIZE;
nptxfifolen |= USB_HTX_NPFIFO_HS_SIZE << 16;
udev->regs.gr->DIEP0TFLEN_HNPTFLEN = nptxfifolen;
/* set periodic TX FIFO size and address */
ptxfifolen |= USB_RX_FIFO_HS_SIZE + USB_HTX_NPFIFO_HS_SIZE;
ptxfifolen |= USB_HTX_PFIFO_HS_SIZE << 16;
udev->regs.gr->HPTFLEN = ptxfifolen;
}
#endif /* USB_HS_CORE */
#ifdef USE_OTG_MODE
/* clear host set HNP enable in the usb_otg control register */
udev->regs.gr->GOTGCS &= ~GOTGCS_HHNPEN;
#endif /* USE_OTG_MODE */
/* make sure the FIFOs are flushed */
/* flush all TX FIFOs in device or host mode */
usb_txfifo_flush(&udev->regs, 0x10U);
/* flush the entire RX FIFO */
usb_rxfifo_flush(&udev->regs);
/* disable all interrupts */
udev->regs.gr->GINTEN = 0U;
/* clear any pending USB OTG interrupts */
udev->regs.gr->GOTGINTF = 0xFFFFFFFFU;
/* enable the USB wakeup and suspend interrupts */
udev->regs.gr->GINTF = 0xBFFFFFFFU;
/* clear all pending host channel interrupts */
for(i = 0U; i < udev->bp.num_pipe; i++) {
udev->regs.pr[i]->HCHINTF = 0xFFFFFFFFU;
udev->regs.pr[i]->HCHINTEN = 0U;
}
#ifndef USE_OTG_MODE
usb_portvbus_switch(udev, 1U);
#endif /* USE_OTG_MODE */
udev->regs.gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE;
/* enable host_mode-related interrupts */
if(USB_USE_FIFO == udev->bp.transfer_mode) {
inten = GINTEN_RXFNEIE;
}
inten |= GINTEN_SESIE | GINTEN_HPIE | GINTEN_HCIE | GINTEN_ISOINCIE;
udev->regs.gr->GINTEN |= inten;
inten = GINTEN_DISCIE | GINTEN_SOFIE;
udev->regs.gr->GINTEN &= ~inten;
return USB_OK;
}
/*!
\brief control the VBUS to power
\param[in] udev: pointer to selected USB host
\param[in] state: VBUS state
\param[out] none
\retval none
*/
void usb_portvbus_switch(usb_core_driver *udev, uint8_t state)
{
uint32_t port = 0U;
/* enable or disable the external charge pump */
usb_vbus_drive(state);
/* turn on the host port power. */
port = usb_port_read(udev);
if(!(port & HPCS_PP) && (1U == state)) {
port |= HPCS_PP;
}
if((port & HPCS_PP) && (0U == state)) {
port &= ~HPCS_PP;
}
*udev->regs.HPCS = port;
usb_mdelay(200U);
}
/*!
\brief reset host port
\param[in] udev: pointer to USB device
\param[out] none
\retval operation status
*/
uint32_t usb_port_reset(usb_core_driver *udev)
{
__IO uint32_t port = usb_port_read(udev);
*udev->regs.HPCS = port | HPCS_PRST;
usb_mdelay(20U); /* see note */
*udev->regs.HPCS = port & ~HPCS_PRST;
usb_mdelay(20U);
return 1U;
}
/*!
\brief initialize host pipe
\param[in] udev: pointer to USB device
\param[in] pipe_num: host pipe number which is in (0..7)
\param[out] none
\retval operation status
*/
usb_status usb_pipe_init(usb_core_driver *udev, uint8_t pipe_num)
{
usb_status status = USB_OK;
__IO uint32_t pp_ctl = 0U;
__IO uint32_t pp_inten = HCHINTEN_TFIE;
usb_pipe *pp = &udev->host.pipe[pipe_num];
/* clear old interrupt conditions for this host channel */
udev->regs.pr[pipe_num]->HCHINTF = 0xFFFFFFFFU;
if(USB_USE_DMA == udev->bp.transfer_mode) {
pp_inten |= HCHINTEN_DMAERIE;
}
if(pp->ep.dir) {
pp_inten |= HCHINTEN_BBERIE;
}
/* enable channel interrupts required for this transfer */
switch(pp->ep.type) {
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
pp_inten |= HCHINTEN_STALLIE | HCHINTEN_USBERIE \
| HCHINTEN_DTERIE | HCHINTEN_NAKIE;
if(!pp->ep.dir) {
if(PORT_SPEED_HIGH == pp->dev_speed) {
pp_inten |= HCHINTEN_NYETIE;
pp_inten |= HCHINTEN_ACKIE;
}
}
break;
case USB_EPTYPE_INTR:
pp_inten |= HCHINTEN_STALLIE | HCHINTEN_USBERIE | HCHINTEN_DTERIE \
| HCHINTEN_NAKIE | HCHINTEN_REQOVRIE;
break;
case USB_EPTYPE_ISOC:
pp_inten |= HCHINTEN_REQOVRIE | HCHINTEN_ACKIE;
if(pp->ep.dir) {
pp_inten |= HCHINTEN_USBERIE;
}
break;
default:
break;
}
udev->regs.pr[pipe_num]->HCHINTEN = pp_inten;
/* enable the top level host channel interrupt */
udev->regs.hr->HACHINTEN |= 1U << pipe_num;
/* make sure host channel interrupts are enabled */
udev->regs.gr->GINTEN |= GINTEN_HCIE;
/* program the host channel control register */
pp_ctl |= PIPE_CTL_DAR(pp->dev_addr);
pp_ctl |= PIPE_CTL_EPNUM(pp->ep.num);
pp_ctl |= PIPE_CTL_EPDIR(pp->ep.dir);
pp_ctl |= PIPE_CTL_EPTYPE(pp->ep.type);
pp_ctl |= PIPE_CTL_LSD(PORT_SPEED_LOW == pp->dev_speed);
pp_ctl |= pp->ep.mps;
pp_ctl |= ((uint32_t)(USB_EPTYPE_INTR == pp->ep.type) << 29) & HCHCTL_ODDFRM;
udev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
return status;
}
/*!
\brief prepare host channel for transferring packets
\param[in] udev: pointer to USB device
\param[in] pipe_num: host pipe number which is in (0..7)
\param[out] none
\retval operation status
*/
usb_status usb_pipe_xfer(usb_core_driver *udev, uint8_t pipe_num)
{
usb_status status = USB_OK;
uint16_t dword_len = 0U;
uint16_t packet_count = 0U;
__IO uint32_t pp_ctl = 0U;
usb_pipe *pp = &udev->host.pipe[pipe_num];
uint16_t max_packet_len = pp->ep.mps;
/* compute the expected number of packets associated to the transfer */
if(pp->xfer_len > 0U) {
packet_count = (uint16_t)((pp->xfer_len + max_packet_len - 1U) / max_packet_len);
if(packet_count > HC_MAX_PACKET_COUNT) {
packet_count = HC_MAX_PACKET_COUNT;
pp->xfer_len = (uint16_t)(packet_count * max_packet_len);
}
} else {
packet_count = 1U;
}
if(pp->ep.dir) {
pp->xfer_len = (uint16_t)(packet_count * max_packet_len);
}
/* initialize the host channel transfer information */
udev->regs.pr[pipe_num]->HCHLEN = pp->xfer_len | pp->DPID | PIPE_XFER_PCNT(packet_count);
if(USB_USE_DMA == udev->bp.transfer_mode) {
udev->regs.pr[pipe_num]->HCHDMAADDR = (unsigned int)pp->xfer_buf;
}
pp_ctl = udev->regs.pr[pipe_num]->HCHCTL;
if(usb_frame_even(udev)) {
pp_ctl |= HCHCTL_ODDFRM;
} else {
pp_ctl &= ~HCHCTL_ODDFRM;
}
/* set host channel enabled */
pp_ctl |= HCHCTL_CEN;
pp_ctl &= ~HCHCTL_CDIS;
udev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
if(USB_USE_FIFO == udev->bp.transfer_mode) {
if((0U == pp->ep.dir) && (pp->xfer_len > 0U)) {
switch(pp->ep.type) {
/* non-periodic transfer */
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
dword_len = (uint16_t)((pp->xfer_len + 3U) / 4U);
/* check if there is enough space in FIFO space */
if(dword_len > (udev->regs.gr->HNPTFQSTAT & HNPTFQSTAT_NPTXFS)) {
/* need to process data in nptxfempty interrupt */
udev->regs.gr->GINTEN |= GINTEN_NPTXFEIE;
}
break;
/* periodic transfer */
case USB_EPTYPE_INTR:
case USB_EPTYPE_ISOC:
dword_len = (uint16_t)((pp->xfer_len + 3U) / 4U);
/* check if there is enough space in FIFO space */
if(dword_len > (udev->regs.hr->HPTFQSTAT & HPTFQSTAT_PTXFS)) {
/* need to process data in ptxfempty interrupt */
udev->regs.gr->GINTEN |= GINTEN_PTXFEIE;
}
break;
default:
break;
}
/* write packet into the TX FIFO */
usb_txfifo_write(&udev->regs, pp->xfer_buf, pipe_num, (uint16_t)pp->xfer_len);
}
}
return status;
}
/*!
\brief halt pipe
\param[in] udev: pointer to USB device
\param[in] pipe_num: host pipe number which is in (0..7)
\param[out] none
\retval operation status
*/
usb_status usb_pipe_halt(usb_core_driver *udev, uint8_t pipe_num)
{
__IO uint32_t pp_ctl = udev->regs.pr[pipe_num]->HCHCTL;
uint8_t ep_type = (uint8_t)((pp_ctl & HCHCTL_EPTYPE) >> 18U);
pp_ctl |= HCHCTL_CEN | HCHCTL_CDIS;
switch(ep_type) {
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
if(0U == (udev->regs.gr->HNPTFQSTAT & HNPTFQSTAT_NPTXFS)) {
pp_ctl &= ~HCHCTL_CEN;
}
break;
case USB_EPTYPE_INTR:
case USB_EPTYPE_ISOC:
if(0U == (udev->regs.hr->HPTFQSTAT & HPTFQSTAT_PTXFS)) {
pp_ctl &= ~HCHCTL_CEN;
}
break;
default:
break;
}
udev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
return USB_OK;
}
/*!
\brief configure host pipe to do ping operation
\param[in] udev: pointer to USB device
\param[in] pipe_num: host pipe number which is in (0..7 or 0..11)
\param[out] none
\retval operation status
*/
usb_status usb_pipe_ping(usb_core_driver *udev, uint8_t pipe_num)
{
uint32_t pp_ctl = 0U;
udev->regs.pr[pipe_num]->HCHLEN = HCHLEN_PING;
pp_ctl = udev->regs.pr[pipe_num]->HCHCTL;
pp_ctl |= HCHCTL_CEN;
pp_ctl &= ~HCHCTL_CDIS;
udev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
return USB_OK;
}
/*!
\brief stop the USB host and clean up FIFO
\param[in] udev: pointer to USB device
\param[out] none
\retval none
*/
void usb_host_stop(usb_core_driver *udev)
{
uint32_t i;
__IO uint32_t pp_ctl = 0U;
udev->regs.hr->HACHINTEN = 0x0U;
udev->regs.hr->HACHINT = 0xFFFFFFFFU;
/* flush out any leftover queued requests. */
for(i = 0U; i < udev->bp.num_pipe; i++) {
pp_ctl = udev->regs.pr[i]->HCHCTL;
pp_ctl &= ~(HCHCTL_CEN | HCHCTL_EPDIR);
pp_ctl |= HCHCTL_CDIS;
udev->regs.pr[i]->HCHCTL = pp_ctl;
}
/* flush the FIFO */
usb_rxfifo_flush(&udev->regs);
usb_txfifo_flush(&udev->regs, 0x10U);
}
@@ -0,0 +1,591 @@
/*!
\file drv_usbd_int.c
\brief USB device mode interrupt routines
\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 "drv_usbd_int.h"
#include "usbd_transc.h"
static const uint8_t USB_SPEED[4] = {
[DSTAT_EM_HS_PHY_30MHZ_60MHZ] = (uint8_t)USB_SPEED_HIGH,
[DSTAT_EM_FS_PHY_30MHZ_60MHZ] = (uint8_t)USB_SPEED_FULL,
[DSTAT_EM_FS_PHY_48MHZ] = (uint8_t)USB_SPEED_FULL,
[DSTAT_EM_LS_PHY_6MHZ] = (uint8_t)USB_SPEED_LOW
};
/* local function prototypes ('static') */
static uint32_t usbd_int_epout(usb_core_driver *udev);
static uint32_t usbd_int_epin(usb_core_driver *udev);
static uint32_t usbd_int_rxfifo(usb_core_driver *udev);
static uint32_t usbd_int_reset(usb_core_driver *udev);
static uint32_t usbd_int_enumfinish(usb_core_driver *udev);
static uint32_t usbd_int_suspend(usb_core_driver *udev);
static uint32_t usbd_emptytxfifo_write(usb_core_driver *udev, uint32_t ep_num);
/*!
\brief USB device-mode interrupts global service routine handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void usbd_isr(usb_core_driver *udev)
{
if(HOST_MODE != (udev->regs.gr->GINTF & GINTF_COPM)) {
uint32_t intr = udev->regs.gr->GINTF;
intr &= udev->regs.gr->GINTEN;
/* there are no interrupts, avoid spurious interrupt */
if(!intr) {
return;
}
/* OUT endpoints interrupts */
if(intr & GINTF_OEPIF) {
(void)usbd_int_epout(udev);
}
/* IN endpoints interrupts */
if(intr & GINTF_IEPIF) {
(void)usbd_int_epin(udev);
}
/* suspend interrupt */
if(intr & GINTF_SP) {
(void)usbd_int_suspend(udev);
}
/* wakeup interrupt */
if(intr & GINTF_WKUPIF) {
if(USBD_SUSPENDED == udev->dev.cur_status) {
/* inform upper layer by the resume event */
udev->dev.cur_status = udev->dev.backup_status;
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_WKUPIF;
}
/* start of frame interrupt */
if(intr & GINTF_SOF) {
if(udev->dev.class_core->SOF) {
(void)udev->dev.class_core->SOF(udev);
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_SOF;
}
/* receive FIFO not empty interrupt */
if(intr & GINTF_RXFNEIF) {
(void)usbd_int_rxfifo(udev);
}
/* USB reset interrupt */
if(intr & GINTF_RST) {
(void)usbd_int_reset(udev);
}
/* enumeration has been done interrupt */
if(intr & GINTF_ENUMFIF) {
(void)usbd_int_enumfinish(udev);
}
/* incomplete synchronization IN transfer interrupt*/
if(intr & GINTF_ISOINCIF) {
if(NULL != udev->dev.class_core->incomplete_isoc_in) {
(void)udev->dev.class_core->incomplete_isoc_in(udev);
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_ISOINCIF;
}
/* incomplete synchronization OUT transfer interrupt*/
if(intr & GINTF_ISOONCIF) {
if(NULL != udev->dev.class_core->incomplete_isoc_out) {
(void)udev->dev.class_core->incomplete_isoc_out(udev);
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_ISOONCIF;
}
#ifdef VBUS_SENSING_ENABLED
/* session request interrupt */
if(intr & GINTF_SESIF) {
udev->regs.gr->GINTF = GINTF_SESIF;
}
/* OTG mode interrupt */
if(intr & GINTF_OTGIF) {
if(udev->regs.gr->GOTGINTF & GOTGINTF_SESEND) {
}
/* clear OTG interrupt */
udev->regs.gr->GINTF = GINTF_OTGIF;
}
#endif /* VBUS_SENSING_ENABLED */
}
}
#ifdef USB_HS_DEDICATED_EP1_ENABLED
/*!
\brief USB dedicated OUT endpoint 1 interrupt service routine handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
uint32_t usbd_int_dedicated_ep1out(usb_core_driver *udev)
{
uint32_t oepintr = 0U;
uint32_t oeplen = 0U;
oepintr = udev->regs.er_out[1]->DOEPINTF;
oepintr &= udev->regs.dr->DOEP1INTEN;
/* transfer complete */
if(oepintr & DOEPINTF_TF) {
/* clear the bit in DOEPINTn for this interrupt */
udev->regs.er_out[1]->DOEPINTF = DOEPINTF_TF;
if(USB_USE_DMA == udev->bp.transfer_mode) {
usb_transc *transc = &udev->dev.transc_out[1];
uint32_t set_len = ((transc->xfer_len + transc->max_len - 1U) / transc->max_len) * transc->max_len;
oeplen = udev->regs.er_out[1]->DOEPLEN;
/* to do : handle more than one single max packet size packet */
udev->dev.transc_out[1].xfer_count = set_len - (oeplen & DEPLEN_TLEN);
}
/* RX complete */
usbd_out_transc(udev, 1U);
}
return 1U;
}
/*!
\brief USB dedicated IN endpoint 1 interrupt service routine handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
uint32_t usbd_int_dedicated_ep1in(usb_core_driver *udev)
{
uint32_t inten, intr, emptyen;
inten = udev->regs.dr->DIEP1INTEN;
emptyen = udev->regs.dr->DIEPFEINTEN;
inten |= ((emptyen >> 1U) & 0x1U) << 7;
intr = udev->regs.er_in[1]->DIEPINTF & inten;
if(intr & DIEPINTF_TF) {
udev->regs.dr->DIEPFEINTEN &= ~(0x1U << 1);
udev->regs.er_in[1]->DIEPINTF = DIEPINTF_TF;
/* TX complete */
usbd_in_transc(udev, 1U);
}
if(intr & DIEPINTF_TXFE) {
usbd_emptytxfifo_write(udev, 1U);
udev->regs.er_in[1]->DIEPINTF = DIEPINTF_TXFE;
}
return 1U;
}
#endif /* USB_HS_DEDICATED_EP1_ENABLED */
/*!
\brief indicates that an OUT endpoint has a pending interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_epout(usb_core_driver *udev)
{
uint32_t epintnum = 0U;
uint8_t ep_num = 0U;
for(epintnum = usb_oepintnum_read(udev); epintnum; epintnum >>= 1, ep_num++) {
if(epintnum & 0x01U) {
__IO uint32_t oepintr = usb_oepintr_read(udev, ep_num);
/* transfer complete interrupt */
if(oepintr & DOEPINTF_TF) {
/* clear the bit in DOEPINTF for this interrupt */
udev->regs.er_out[ep_num]->DOEPINTF = DOEPINTF_TF;
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
usb_transc *transc = &udev->dev.transc_out[ep_num];
__IO uint32_t eplen = udev->regs.er_out[ep_num]->DOEPLEN;
uint32_t set_len = ((transc->xfer_len + transc->max_len - 1U) / transc->max_len) * transc->max_len;
udev->dev.transc_out[ep_num].xfer_count = set_len - (eplen & DEPLEN_TLEN);
}
/* inform upper layer: data ready */
(void)usbd_out_transc(udev, ep_num);
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
if((0U == ep_num) && ((uint8_t)USB_CTL_STATUS_OUT == udev->dev.control.ctl_state)) {
usb_ctlep_startout(udev);
}
}
}
/* SETUP phase finished interrupt (control endpoints) */
if(oepintr & DOEPINTF_STPF) {
/* inform the upper layer that a SETUP packet is available */
(void)usbd_setup_transc(udev);
udev->regs.er_out[ep_num]->DOEPINTF = DOEPINTF_STPF;
}
}
}
return 1U;
}
/*!
\brief indicates that an IN endpoint has a pending interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_epin(usb_core_driver *udev)
{
uint32_t epintnum = 0U;
uint8_t ep_num = 0U;
for(epintnum = usb_iepintnum_read(udev); epintnum; epintnum >>= 1, ep_num++) {
if(epintnum & 0x1U) {
__IO uint32_t iepintr = usb_iepintr_read(udev, ep_num);
if(iepintr & DIEPINTF_TF) {
udev->regs.er_in[ep_num]->DIEPINTF = DIEPINTF_TF;
/* data transmission is completed */
(void)usbd_in_transc(udev, ep_num);
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
if((0U == ep_num) && ((uint8_t)USB_CTL_STATUS_IN == udev->dev.control.ctl_state)) {
usb_ctlep_startout(udev);
}
}
}
if(iepintr & DIEPINTF_TXFE) {
usbd_emptytxfifo_write(udev, (uint32_t)ep_num);
udev->regs.er_in[ep_num]->DIEPINTF = DIEPINTF_TXFE;
}
}
}
return 1U;
}
/*!
\brief handle the RX status queue level interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_rxfifo(usb_core_driver *udev)
{
usb_transc *transc = NULL;
uint8_t data_PID = 0U;
uint32_t bcount = 0U;
__IO uint32_t devrxstat = 0U;
/* disable the RX status queue non-empty interrupt */
udev->regs.gr->GINTEN &= ~GINTEN_RXFNEIE;
/* get the status from the top of the FIFO */
devrxstat = udev->regs.gr->GRSTATP;
uint8_t ep_num = (uint8_t)(devrxstat & GRSTATRP_EPNUM);
transc = &udev->dev.transc_out[ep_num];
bcount = (devrxstat & GRSTATRP_BCOUNT) >> 4;
data_PID = (uint8_t)((devrxstat & GRSTATRP_DPID) >> 15);
#if defined(USE_USB_HS) && defined(USE_ULPI_PHY)
#ifndef USE_450Z_EVAL
/* ensure no-DMA mode can work */
if(0U == (udev->regs.er_out[ep_num]->DOEPLEN & DEPLEN_PCNT)) {
uint32_t devepctl = udev->regs.er_out[ep_num]->DOEPCTL;
devepctl |= DEPCTL_SNAK;
devepctl &= ~DEPCTL_EPEN;
devepctl &= ~DEPCTL_EPD;
udev->regs.er_out[ep_num]->DOEPCTL = devepctl;
}
#endif /* USE_450Z_EVAL */
#endif /* USE_USB_HS && USE_ULPI_PHY */
switch((devrxstat & GRSTATRP_RPCKST) >> 17) {
case RSTAT_GOUT_NAK:
break;
case RSTAT_DATA_UPDT:
if(bcount > 0U) {
(void)usb_rxfifo_read(&udev->regs, transc->xfer_buf, (uint16_t)bcount);
transc->xfer_buf += bcount;
transc->xfer_count += bcount;
}
break;
case RSTAT_XFER_COMP:
/* trigger the OUT endpoint interrupt */
break;
case RSTAT_SETUP_COMP:
/* trigger the OUT endpoint interrupt */
break;
case RSTAT_SETUP_UPDT:
if((0U == transc->ep_addr.num) && (8U == bcount) && (DPID_DATA0 == data_PID)) {
/* copy the SETUP packet received in FIFO into the setup buffer in RAM */
(void)usb_rxfifo_read(&udev->regs, (uint8_t *)&udev->dev.control.req, (uint16_t)bcount);
transc->xfer_count += bcount;
}
break;
default:
break;
}
/* enable the RX status queue level interrupt */
udev->regs.gr->GINTEN |= GINTEN_RXFNEIE;
return 1U;
}
/*!
\brief handle USB reset interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval status
*/
static uint32_t usbd_int_reset(usb_core_driver *udev)
{
uint32_t i;
/* clear the remote wakeup signaling */
udev->regs.dr->DCTL &= ~DCTL_RWKUP;
/* flush the TX FIFO */
(void)usb_txfifo_flush(&udev->regs, 0U);
for(i = 0U; i < udev->bp.num_ep; i++) {
udev->regs.er_in[i]->DIEPINTF = 0xFFU;
udev->regs.er_out[i]->DOEPINTF = 0xFFU;
}
/* clear all pending device endpoint interrupts */
udev->regs.dr->DAEPINT = 0xFFFFFFFFU;
/* enable endpoint 0 interrupts */
udev->regs.dr->DAEPINTEN = 1U | (1U << 16);
/* enable OUT endpoint interrupts */
udev->regs.dr->DOEPINTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN;
#ifdef USB_HS_DEDICATED_EP1_ENABLED
udev->regs.dr->DOEP1INTEN = DOEPINTEN_STPFEN | DOEPINTEN_TFEN;
#endif /* USB_HS_DEDICATED_EP1_ENABLED */
/* enable IN endpoint interrupts */
udev->regs.dr->DIEPINTEN = DIEPINTEN_TFEN;
#ifdef USB_HS_DEDICATED_EP1_ENABLED
udev->regs.dr->DIEP1INTEN = DIEPINTEN_TFEN;
#endif /* USB_HS_DEDICATED_EP1_ENABLED */
/* reset device address */
udev->regs.dr->DCFG &= ~DCFG_DAR;
/* configure endpoint 0 to receive SETUP packets */
usb_ctlep_startout(udev);
/* clear USB reset interrupt */
udev->regs.gr->GINTF = GINTF_RST;
udev->dev.transc_out[0] = (usb_transc) {
.ep_type = USB_EPTYPE_CTRL,
.max_len = USB_FS_EP0_MAX_LEN
};
(void)usb_transc_active(udev, &udev->dev.transc_out[0]);
udev->dev.transc_in[0] = (usb_transc) {
.ep_addr = {
.dir = 1U
},
.ep_type = USB_EPTYPE_CTRL,
.max_len = USB_FS_EP0_MAX_LEN
};
(void)usb_transc_active(udev, &udev->dev.transc_in[0]);
/* upon reset call user call back */
udev->dev.cur_status = (uint8_t)USBD_DEFAULT;
return 1U;
}
/*!
\brief handle USB speed enumeration finish interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval status
*/
static uint32_t usbd_int_enumfinish(usb_core_driver *udev)
{
uint8_t enum_speed = (uint8_t)((udev->regs.dr->DSTAT & DSTAT_ES) >> 1);
udev->regs.dr->DCTL &= ~DCTL_CGINAK;
udev->regs.dr->DCTL |= DCTL_CGINAK;
udev->regs.gr->GUSBCS &= ~GUSBCS_UTT;
/* set USB turn-around time based on device speed and PHY interface */
if((uint8_t)USB_SPEED_HIGH == USB_SPEED[enum_speed]) {
udev->bp.core_speed = (uint8_t)USB_SPEED_HIGH;
udev->regs.gr->GUSBCS |= 0x09U << 10;
} else {
udev->bp.core_speed = (uint8_t)USB_SPEED_FULL;
udev->regs.gr->GUSBCS |= 0x05U << 10;
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_ENUMFIF;
return 1U;
}
/*!
\brief USB suspend interrupt handler
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbd_int_suspend(usb_core_driver *udev)
{
__IO uint8_t low_power = udev->bp.low_power;
__IO uint8_t suspend = (uint8_t)(udev->regs.dr->DSTAT & DSTAT_SPST);
__IO uint8_t is_configured = ((uint8_t)USBD_CONFIGURED == udev->dev.cur_status) ? 1U : 0U;
udev->dev.backup_status = udev->dev.cur_status;
udev->dev.cur_status = (uint8_t)USBD_SUSPENDED;
if(low_power && suspend && is_configured) {
/* switch-off the USB clocks */
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SUCLK | PWRCLKCTL_SHCLK;
/* enter DEEP_SLEEP mode with LDO in low power mode */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_DISABLE, WFI_CMD);
}
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_SP;
return 1U;
}
/*!
\brief check FIFO for the next packet to be loaded
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier which is in (0..3)
\param[out] none
\retval status
*/
static uint32_t usbd_emptytxfifo_write(usb_core_driver *udev, uint32_t ep_num)
{
uint32_t len;
uint32_t word_count;
usb_transc *transc = &udev->dev.transc_in[ep_num];
len = transc->xfer_len - transc->xfer_count;
/* get the data length to write */
if(len > transc->max_len) {
len = transc->max_len;
}
word_count = (len + 3U) / 4U;
while(((udev->regs.er_in[ep_num]->DIEPTFSTAT & DIEPTFSTAT_IEPTFS) >= word_count) && \
(transc->xfer_count < transc->xfer_len)) {
len = transc->xfer_len - transc->xfer_count;
if(len > transc->max_len) {
len = transc->max_len;
}
/* write FIFO in word(4bytes) */
word_count = (len + 3U) / 4U;
/* write the FIFO */
(void)usb_txfifo_write(&udev->regs, transc->xfer_buf, (uint8_t)ep_num, (uint16_t)len);
transc->xfer_buf += len;
transc->xfer_count += len;
if(transc->xfer_count == transc->xfer_len) {
/* disable the device endpoint FIFO empty interrupt */
udev->regs.dr->DIEPFEINTEN &= ~(0x01U << ep_num);
}
}
return 1U;
}
@@ -0,0 +1,643 @@
/*!
\file drv_usbh_int.c
\brief USB host mode interrupt handler 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.
*/
#include "drv_usbh_int.h"
#if defined (__CC_ARM) /*!< ARM compiler */
#pragma O0
#elif defined (__GNUC__) /*!< GNU compiler */
#pragma GCC optimize ("O0")
#elif defined (__TASKING__) /*!< TASKING compiler */
#pragma optimize=0
#endif /* __CC_ARM */
/* local function prototypes ('static') */
static uint32_t usbh_int_port(usb_core_driver *udev);
static uint32_t usbh_int_pipe(usb_core_driver *udev);
static uint32_t usbh_int_pipe_in(usb_core_driver *udev, uint32_t pp_num);
static uint32_t usbh_int_pipe_out(usb_core_driver *udev, uint32_t pp_num);
static uint32_t usbh_int_rxfifonoempty(usb_core_driver *udev);
static uint32_t usbh_int_txfifoempty(usb_core_driver *udev, usb_pipe_mode pp_mode);
/*!
\brief handle global host interrupt
\param[in] udev: pointer to USB core instance
\param[out] none
\retval operation status
*/
uint32_t usbh_isr(usb_core_driver *udev)
{
uint32_t retval = 0U;
__IO uint32_t intr = 0U;
/* check if host mode */
if(HOST_MODE == (udev->regs.gr->GINTF & GINTF_COPM)) {
intr = usb_coreintr_get(&udev->regs);
if(!intr) {
return 0U;
}
if(intr & GINTF_SOF) {
usbh_int_fop->SOF(udev->host.data);
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_SOF;
}
if(intr & GINTF_RXFNEIF) {
retval |= usbh_int_rxfifonoempty(udev);
}
if(intr & GINTF_NPTXFEIF) {
retval |= usbh_int_txfifoempty(udev, PIPE_NON_PERIOD);
}
if(intr & GINTF_PTXFEIF) {
retval |= usbh_int_txfifoempty(udev, PIPE_PERIOD);
}
if(intr & GINTF_HCIF) {
retval |= usbh_int_pipe(udev);
}
if(intr & GINTF_HPIF) {
retval |= usbh_int_port(udev);
}
if(intr & GINTF_WKUPIF) {
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_WKUPIF;
}
if(intr & GINTF_DISCIF) {
usbh_int_fop->disconnect(udev->host.data);
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_DISCIF;
}
if(intr & GINTF_ISOONCIF) {
udev->regs.pr[0]->HCHCTL |= HCHCTL_CEN | HCHCTL_CDIS;
/* clear interrupt */
udev->regs.gr->GINTF = GINTF_ISOONCIF;
}
if(intr & GINTF_SESIF) {
usb_portvbus_switch(udev, 1U);
udev->regs.gr->GINTF = GINTF_SESIF;
}
}
return retval;
}
/*!
\brief handle USB pipe halt
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pp_num: host channel number which is in (0..7)
\param[in] pp_int: pipe interrupt
\param[in] pp_status: pipe status
\param[out] none
\retval none
*/
static inline void usb_pp_halt(usb_core_driver *udev, \
uint8_t pp_num, \
uint32_t pp_int, \
usb_pipe_status pp_status)
{
udev->regs.pr[pp_num]->HCHINTEN |= HCHINTEN_CHIE;
usb_pipe_halt(udev, pp_num);
udev->regs.pr[pp_num]->HCHINTF = pp_int;
udev->host.pipe[pp_num].pp_status = pp_status;
}
/*!
\brief handle the host port interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
#if defined (__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_port(usb_core_driver *udev)
{
uint32_t retval = 0U;
/* note: when the USB PHY use USB HS PHY, the flag is needed */
uint8_t port_reset = 0U;
__IO uint32_t port_state = *udev->regs.HPCS;
/* clear the interrupt bit in GINTF */
port_state &= ~(HPCS_PE | HPCS_PCD | HPCS_PEDC);
/* port connect detected */
if(*udev->regs.HPCS & HPCS_PCD) {
port_state |= HPCS_PCD;
usbh_int_fop->connect(udev->host.data);
retval |= 1U;
}
/* port enable changed */
if(*udev->regs.HPCS & HPCS_PEDC) {
port_state |= HPCS_PEDC;
if(*udev->regs.HPCS & HPCS_PE) {
uint32_t port_speed = usb_curspeed_get(udev);
uint32_t clock_type = udev->regs.hr->HCTL & HCTL_CLKSEL;
udev->host.connect_status = 1U;
if(PORT_SPEED_LOW == port_speed) {
udev->regs.hr->HFT = 6000U;
if(HCTL_6MHZ != clock_type) {
if(USB_EMBEDDED_PHY == udev->bp.phy_itf) {
usb_phyclock_config(udev, HCTL_6MHZ);
}
port_reset = 1U;
}
} else if(PORT_SPEED_FULL == port_speed) {
udev->regs.hr->HFT = 48000U;
if(HCTL_48MHZ != clock_type) {
if(USB_EMBEDDED_PHY == udev->bp.phy_itf) {
usb_phyclock_config(udev, HCTL_48MHZ);
}
port_reset = 1U;
}
} else {
/* for high speed device and others */
port_reset = 1U;
}
udev->host.port_enabled = 1U;
udev->regs.gr->GINTEN |= GINTEN_DISCIE | GINTEN_SOFIE;
} else {
udev->host.port_enabled = 0U;
}
}
if(port_reset) {
usb_port_reset(udev);
}
/* clear port interrupts */
*udev->regs.HPCS = port_state;
return retval;
}
/*!
\brief handle all host channels interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
static uint32_t usbh_int_pipe(usb_core_driver *udev)
{
uint32_t pp_num = 0U;
uint32_t retval = 0U;
for(pp_num = 0U; pp_num < udev->bp.num_pipe; pp_num++) {
if((udev->regs.hr->HACHINT & HACHINT_HACHINT) & (1UL << pp_num)) {
if(udev->regs.pr[pp_num]->HCHCTL & HCHCTL_EPDIR) {
retval |= usbh_int_pipe_in(udev, pp_num);
} else {
retval |= usbh_int_pipe_out(udev, pp_num);
}
}
}
return retval;
}
/*!
\brief handle the IN channel interrupt
\param[in] udev: pointer to USB device instance
\param[in] pp_num: host channel number which is in (0..7)
\param[out] none
\retval operation status
*/
#if defined (__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_pipe_in(usb_core_driver *udev, uint32_t pp_num)
{
usb_pr *pp_reg = udev->regs.pr[pp_num];
usb_pipe *pp = &udev->host.pipe[pp_num];
uint32_t intr_pp = pp_reg->HCHINTF;
intr_pp &= pp_reg->HCHINTEN;
uint8_t ep_type = (uint8_t)((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18);
if(intr_pp & HCHINTF_ACK) {
pp_reg->HCHINTF = HCHINTF_ACK;
} else if(intr_pp & HCHINTF_STALL) {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_STALL, PIPE_STALL);
pp_reg->HCHINTF = HCHINTF_NAK;
/* note: When there is a 'STALL', reset also NAK,
else, the udev->host.pp_status = HC_STALL
will be overwritten by 'NAK' in code below */
intr_pp &= ~HCHINTF_NAK;
} else if(intr_pp & HCHINTF_DTER) {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_DTER, PIPE_DTGERR);
pp_reg->HCHINTF = HCHINTF_NAK;
} else {
/* no operation */
}
if(intr_pp & HCHINTF_REQOVR) {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_REQOVR, PIPE_REQOVR);
} else if(intr_pp & HCHINTF_TF) {
if((uint8_t)USB_USE_DMA == udev->bp.transfer_mode) {
udev->host.backup_xfercount[pp_num] = pp->xfer_len - (pp_reg->HCHLEN & HCHLEN_TLEN);
}
pp->pp_status = PIPE_XF;
pp->err_count = 0U;
pp_reg->HCHINTF = HCHINTF_TF;
switch(ep_type) {
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
if(USB_USE_DMA == udev->bp.transfer_mode) {
udev->regs.pr[pp_num]->HCHINTEN |= HCHINTEN_CHIE;
} else {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_XF);
}
pp->data_toggle_in ^= 1U;
break;
case USB_EPTYPE_INTR:
case USB_EPTYPE_ISOC:
pp_reg->HCHCTL |= HCHCTL_ODDFRM;
pp->urb_state = URB_DONE;
break;
default:
break;
}
} else if(intr_pp & HCHINTF_CH) {
pp_reg->HCHINTEN &= ~HCHINTEN_CHIE;
switch(pp->pp_status) {
case PIPE_XF:
pp->urb_state = URB_DONE;
break;
case PIPE_STALL:
pp->urb_state = URB_STALL;
break;
case PIPE_TRACERR:
case PIPE_DTGERR:
pp->err_count = 0U;
pp->urb_state = URB_ERROR;
pp->data_toggle_in ^= 1U;
break;
case PIPE_IDLE:
case PIPE_HALTED:
case PIPE_NAK:
case PIPE_NYET:
case PIPE_BBERR:
case PIPE_REQOVR:
default:
if((uint8_t)USB_EPTYPE_INTR == ep_type) {
pp->data_toggle_in ^= 1U;
}
break;
}
pp_reg->HCHINTF = HCHINTF_CH;
} else if(intr_pp & HCHINTF_USBER) {
pp->err_count++;
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_USBER, PIPE_TRACERR);
} else if(intr_pp & HCHINTF_NAK) {
switch(ep_type) {
case USB_EPTYPE_CTRL:
case USB_EPTYPE_BULK:
/* re-activate the channel */
pp_reg->HCHCTL = (pp_reg->HCHCTL | HCHCTL_CEN) & ~HCHCTL_CDIS;
break;
case USB_EPTYPE_INTR:
pp_reg->HCHINTEN |= HCHINTEN_CHIE;
(void)usb_pipe_halt(udev, (uint8_t)pp_num);
break;
default:
break;
}
pp->pp_status = PIPE_NAK;
pp_reg->HCHINTF = HCHINTF_NAK;
} else {
/* no operation */
}
return 1U;
}
/*!
\brief handle the OUT channel interrupt
\param[in] udev: pointer to USB device instance
\param[in] pp_num: host channel number which is in (0..7)
\param[out] none
\retval operation status
*/
#if defined (__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_pipe_out(usb_core_driver *udev, uint32_t pp_num)
{
usbh_host *uhost = udev->host.data;
usb_pr *pp_reg = udev->regs.pr[pp_num];
usb_pipe *pp = &udev->host.pipe[pp_num];
uint32_t intr_pp = pp_reg->HCHINTF;
intr_pp &= pp_reg->HCHINTEN;
if(intr_pp & HCHINTF_ACK) {
if(1U == udev->host.pipe[pp_num].do_ping) {
udev->host.pipe[pp_num].do_ping = 0U;
pp->err_count = 0U;
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_ACK, PIPE_NAK);
}
pp_reg->HCHINTF = HCHINTF_ACK;
} else if(intr_pp & HCHINTF_STALL) {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_STALL, PIPE_STALL);
} else if(intr_pp & HCHINTF_DTER) {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_DTER, PIPE_DTGERR);
pp_reg->HCHINTF = HCHINTF_NAK;
} else if(intr_pp & HCHINTF_REQOVR) {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_REQOVR, PIPE_REQOVR);
} else if(intr_pp & HCHINTF_TF) {
pp->err_count = 0U;
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_TF, PIPE_XF);
} else if(intr_pp & HCHINTF_NAK) {
if(0U == udev->host.pipe[pp_num].do_ping) {
if(1U == udev->host.pipe[pp_num].supp_ping) {
udev->host.pipe[pp_num].do_ping = 1U;
}
}
pp->err_count = 0U;
if(USB_USE_FIFO == udev->bp.transfer_mode) {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_NAK);
} else {
pp_reg->HCHINTF = HCHINTF_NAK;
}
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NAK, PIPE_NAK);
} else if(intr_pp & HCHINTF_USBER) {
pp->err_count++;
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_USBER, PIPE_TRACERR);
} else if(intr_pp & HCHINTF_NYET) {
if(CTL_STATUS_OUT != uhost->control.ctl_state) {
if(0U == udev->host.pipe[pp_num].do_ping) {
if(1U == udev->host.pipe[pp_num].supp_ping) {
udev->host.pipe[pp_num].do_ping = 1U;
}
}
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NYET, PIPE_NYET);
} else {
usb_pp_halt(udev, (uint8_t)pp_num, HCHINTF_NYET, PIPE_XF);
}
pp->err_count = 0U;
} else if(intr_pp & HCHINTF_CH) {
udev->regs.pr[pp_num]->HCHINTEN &= ~HCHINTEN_CHIE;
switch(pp->pp_status) {
case PIPE_XF:
pp->urb_state = URB_DONE;
if((uint8_t)USB_EPTYPE_BULK == ((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18)) {
pp->data_toggle_out ^= 1U;
}
break;
case PIPE_NAK:
pp->urb_state = URB_NOTREADY;
break;
case PIPE_NYET:
pp->urb_state = URB_DONE;
if((uint8_t)USB_EPTYPE_BULK == ((pp_reg->HCHCTL & HCHCTL_EPTYPE) >> 18)) {
pp->data_toggle_out ^= 1U;
}
break;
case PIPE_STALL:
pp->urb_state = URB_STALL;
break;
case PIPE_TRACERR:
if(3U == pp->err_count) {
pp->urb_state = URB_ERROR;
pp->err_count = 0U;
}
break;
case PIPE_IDLE:
case PIPE_HALTED:
case PIPE_BBERR:
case PIPE_REQOVR:
case PIPE_DTGERR:
default:
break;
}
pp_reg->HCHINTF = HCHINTF_CH;
} else {
/* no operation */
}
return 1U;
}
/*!
\brief handle the RX FIFO non-empty interrupt
\param[in] udev: pointer to USB device instance
\param[out] none
\retval operation status
*/
#if defined (__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_rxfifonoempty(usb_core_driver *udev)
{
uint32_t count = 0U, xfer_count = 0U;
__IO uint8_t pp_num = 0U;
__IO uint32_t rx_stat = 0U;
/* disable the RX status queue level interrupt */
udev->regs.gr->GINTEN &= ~GINTEN_RXFNEIE;
rx_stat = udev->regs.gr->GRSTATP;
pp_num = (uint8_t)(rx_stat & GRSTATRP_CNUM);
switch((rx_stat & GRSTATRP_RPCKST) >> 17) {
case GRXSTS_PKTSTS_IN:
count = (rx_stat & GRSTATRP_BCOUNT) >> 4;
/* read the data into the host buffer. */
if((count > 0U) && (NULL != udev->host.pipe[pp_num].xfer_buf)) {
(void)usb_rxfifo_read(&udev->regs, udev->host.pipe[pp_num].xfer_buf, (uint16_t)count);
/* manage multiple transfer packet */
udev->host.pipe[pp_num].xfer_buf += count;
udev->host.pipe[pp_num].xfer_count += count;
xfer_count = udev->host.pipe[pp_num].xfer_count;
udev->host.backup_xfercount[pp_num] = xfer_count;
if(udev->regs.pr[pp_num]->HCHLEN & HCHLEN_PCNT) {
/* re-activate the channel when more packets are expected */
uint32_t pp_ctl = udev->regs.pr[pp_num]->HCHCTL;
pp_ctl |= HCHCTL_CEN;
pp_ctl &= ~HCHCTL_CDIS;
udev->regs.pr[pp_num]->HCHCTL = pp_ctl;
}
}
break;
case GRXSTS_PKTSTS_IN_XFER_COMP:
break;
case GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
count = (rx_stat & GRSTATRP_BCOUNT) >> 4;
while(count > 0U) {
rx_stat = udev->regs.gr->GRSTATP;
count--;
}
break;
case GRXSTS_PKTSTS_CH_HALTED:
break;
default:
break;
}
/* enable the RX status queue level interrupt */
udev->regs.gr->GINTEN |= GINTEN_RXFNEIE;
return 1U;
}
/*!
\brief handle the TX FIFO empty interrupt
\param[in] udev: pointer to USB device instance
\param[in] pp_mode: pipe mode
\param[out] none
\retval operation status
*/
#if defined (__ICCARM__) /*!< IAR compiler */
#pragma optimize = none
#endif /* __ICCARM */
static uint32_t usbh_int_txfifoempty(usb_core_driver *udev, usb_pipe_mode pp_mode)
{
uint8_t pp_num = 0U;
uint16_t word_count = 0U, len = 0U;
__IO uint32_t *txfiforeg = 0U, txfifostate = 0U;
if(PIPE_NON_PERIOD == pp_mode) {
txfiforeg = &udev->regs.gr->HNPTFQSTAT;
} else if(PIPE_PERIOD == pp_mode) {
txfiforeg = &udev->regs.hr->HPTFQSTAT;
} else {
return 0U;
}
txfifostate = *txfiforeg;
pp_num = (uint8_t)((txfifostate & TFQSTAT_CNUM) >> 27);
word_count = (uint16_t)(udev->host.pipe[pp_num].xfer_len + 3U) / 4U;
while(((txfifostate & TFQSTAT_TXFS) >= word_count) && (0U != udev->host.pipe[pp_num].xfer_len)) {
len = (uint16_t)(txfifostate & TFQSTAT_TXFS) * 4U;
if(len > udev->host.pipe[pp_num].xfer_len) {
/* last packet */
len = (uint16_t)udev->host.pipe[pp_num].xfer_len;
if(PIPE_NON_PERIOD == pp_mode) {
udev->regs.gr->GINTEN &= ~GINTEN_NPTXFEIE;
} else {
udev->regs.gr->GINTEN &= ~GINTEN_PTXFEIE;
}
}
word_count = (uint16_t)((udev->host.pipe[pp_num].xfer_len + 3U) / 4U);
usb_txfifo_write(&udev->regs, udev->host.pipe[pp_num].xfer_buf, pp_num, len);
udev->host.pipe[pp_num].xfer_buf += len;
udev->host.pipe[pp_num].xfer_len -= len;
udev->host.pipe[pp_num].xfer_count += len;
txfifostate = *txfiforeg;
}
return 1U;
}
@@ -0,0 +1,116 @@
/*!
\file usbh_hid_core.h
\brief header file for the usbh_hid_core.c
\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 USBH_HID_CORE_H
#define USBH_HID_CORE_H
#include "usb_hid.h"
#include "usbh_enum.h"
#include "usbh_transc.h"
#define HID_MIN_POLL 10U /*!< HID minimum polling */
#define HID_REPORT_SIZE 16U /*!< HID report size */
#define HID_QUEUE_SIZE 10U /*!< HID queue size */
#define USB_HID_DESC_SIZE 9U /*!< HID descriptor size */
/* states for HID state machine */
typedef enum {
HID_INIT = 0U, /*!< HID init state */
HID_IDLE, /*!< HID idle state */
HID_SEND_DATA, /*!< HID send data state */
HID_BUSY, /*!< HID busy state */
HID_GET_DATA, /*!< HID get data state */
HID_SYNC, /*!< HID synchronous state */
HID_POLL, /*!< HID polling state */
HID_ERROR /*!< HID error state */
} hid_state;
/* state types of HID control request */
typedef enum {
HID_REQ_INIT = 0U, /*!< HID init request */
HID_REQ_IDLE, /*!< HID idle request */
HID_REQ_GET_REPORT_DESC, /*!< get report descriptor request */
HID_REQ_GET_HID_DESC, /*!< get HID descriptor request */
HID_REQ_SET_IDLE, /*!< set idle request */
HID_REQ_SET_PROTOCOL, /*!< set protocol request */
HID_REQ_SET_REPORT /*!< set report request */
} hid_ctlstate;
/* HID types */
typedef enum {
HID_MOUSE = 0x01U, /*!< HID mouse type */
HID_KEYBOARD = 0x02U, /*!< HID keyboard type */
HID_UNKNOWN = 0xFFU /*!< unknown type */
} hid_type;
typedef struct {
uint8_t *buf; /*!< data FIFO buff pointer */
uint16_t head; /*!< data FIFO header */
uint16_t tail; /*!< data FIFO tail */
uint16_t size; /*!< data FIFO size */
uint8_t lock; /*!< data FIFO lock */
} data_fifo;
/* structure for HID process */
typedef struct _hid_process {
uint8_t pipe_in; /*!< pipe IN */
uint8_t pipe_out; /*!< pipe OUT */
uint8_t ep_addr; /*!< endpoint address */
uint8_t ep_in; /*!< endpoint IN */
uint8_t ep_out; /*!< endpoint OUT */
uint8_t *pdata; /*!< HID data pointer */
__IO uint8_t data_ready; /*!< HID data ready */
uint16_t len; /*!< HID data length */
uint16_t poll; /*!< HID polling */
__IO uint32_t timer; /*!< HID timer */
usb_desc_hid hid_desc; /*!< HID descriptor */
hid_state state; /*!< HID state structure */
hid_ctlstate ctl_state; /*!< control request state structure */
usbh_status (*init)(usb_core_driver *udev, usbh_host *uhost);
usbh_status (*decode)(uint8_t *data);
} usbh_hid_handler;
extern usbh_class usbh_hid;
/* function declarations */
/* set HID report */
usbh_status usbh_set_report(usb_core_driver *udev, \
usbh_host *uhost, \
uint8_t report_type, \
uint8_t report_ID, \
uint8_t report_len, \
uint8_t *report_buf);
#endif /* USBH_HID_CORE_H */
@@ -0,0 +1,96 @@
/*!
\file usbh_standard_hid.h
\brief header file for usbh_standard_hid.c
\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 USBH_STANDARD_HID_H
#define USBH_STANDARD_HID_H
#include "usbh_hid_core.h"
//#define AZERTY_KEYBOARD
#define QWERTY_KEYBOARD
#define MOUSE_BUTTON_1 0x01U /*!< mouse button 1 */
#define MOUSE_BUTTON_2 0x02U /*!< mouse button 2 */
#define MOUSE_BUTTON_3 0x04U /*!< mouse button 3 */
#define KBD_LEFT_CTRL 0x01U /*!< keyboard left ctrl key */
#define KBD_LEFT_SHIFT 0x02U /*!< keyboard left shift key */
#define KBD_LEFT_ALT 0x04U /*!< keyboard left alt key */
#define KBD_LEFT_GUI 0x08U /*!< keyboard left gui key */
#define KBD_RIGHT_CTRL 0x10U /*!< keyboard right ctrl key */
#define KBD_RIGHT_SHIFT 0x20U /*!< keyboard right shift key */
#define KBD_RIGHT_ALT 0x40U /*!< keyboard right alt key */
#define KBD_RIGHT_GUI 0x80U /*!< keyboard right gui key */
#define KBD_PRESSED_MAX_NUM 6U /*!< keyboard pressed maximum number */
typedef struct _mouse_report_data {
uint8_t x; /*!< X coordinate value */
uint8_t y; /*!< Y coordinate value */
uint8_t buttons[3]; /*!< button buff */
} mouse_report_data;
typedef struct {
uint8_t state; /*!< keyboard state */
uint8_t lctrl; /*!< keyboard left ctrl */
uint8_t lshift; /*!< keyboard left shift */
uint8_t lalt; /*!< keyboard left alt */
uint8_t lgui; /*!< keyboard left gui */
uint8_t rctrl; /*!< keyboard right ctrl */
uint8_t rshift; /*!< keyboard right shift */
uint8_t ralt; /*!< keyboard right alt */
uint8_t rgui; /*!< keyboard right gui */
uint8_t keys[6]; /*!< keyboard keys buff */
} hid_keybd_info;
/* function declarations */
/* initialize mouse */
void usr_mouse_init(void);
/* process mouse data */
void usr_mouse_process_data(mouse_report_data *data);
/* initialize mouse function */
usbh_status usbh_hid_mouse_init(usb_core_driver *udev, usbh_host *uhost);
/* decode mouse information */
usbh_status usbh_hid_mouse_decode(uint8_t *data);
/* initialize keyboard */
void usr_keybrd_init(void);
/* process keyboard data */
void usr_keybrd_process_data(uint8_t pbuf);
/* initialize the keyboard function */
usbh_status usbh_hid_keybrd_init(usb_core_driver *udev, usbh_host *uhost);
/* decode keyboard information */
usbh_status usbh_hid_keybrd_decode(uint8_t *data);
#endif /* USBH_STANDARD_HID_H */
@@ -0,0 +1,582 @@
/*!
\file usbh_hid_core.c
\brief USB host 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 <string.h>
#include "usbh_pipe.h"
#include "usbh_hid_core.h"
#include "usbh_standard_hid.h"
/* local function prototypes ('static') */
static void usbh_hiddesc_parse(usb_desc_hid *hid_desc, uint8_t *buf);
static void usbh_hid_itf_deinit(usbh_host *uhost);
static usbh_status usbh_hid_itf_init(usbh_host *uhost);
static usbh_status usbh_hid_class_req(usbh_host *uhost);
static usbh_status usbh_hid_handle(usbh_host *uhost);
static usbh_status usbh_hid_reportdesc_get(usbh_host *uhost, uint16_t len);
static usbh_status usbh_hid_sof(usbh_host *uhost);
static usbh_status usbh_hid_desc_get(usbh_host *uhost, uint16_t len);
static usbh_status usbh_set_idle(usbh_host *uhost, uint8_t duration, uint8_t report_ID);
static usbh_status usbh_set_protocol(usbh_host *uhost, uint8_t protocol);
usbh_class usbh_hid = {
USB_HID_CLASS,
usbh_hid_itf_init,
usbh_hid_itf_deinit,
usbh_hid_class_req,
usbh_hid_handle,
usbh_hid_sof
};
/*!
\brief get report
\param[in] uhost: pointer to USB host
\param[in] report_type: duration for HID set idle request
\param[in] report_ID: targeted report ID for HID set idle request
\param[in] report_len: length of data report to be send
\param[in] report_buf: report buffer
\param[out] none
\retval operation status
*/
usbh_status usbh_get_report(usbh_host *uhost, \
uint8_t report_type, \
uint8_t report_ID, \
uint8_t report_len, \
uint8_t *report_buf)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_ITF | USB_REQTYPE_CLASS,
.bRequest = GET_REPORT,
.wValue = (report_type << 8) | report_ID,
.wIndex = 0U,
.wLength = report_len
};
usbh_ctlstate_config(uhost, report_buf, report_len);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set report
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[in] report_type: duration for HID set idle request
\param[in] report_ID: targeted report ID for HID set idle request
\param[in] report_len: length of data report to be send
\param[in] report_buf: report buffer
\param[out] none
\retval operation status
*/
usbh_status usbh_set_report(usb_core_driver *udev, \
usbh_host *uhost, \
uint8_t report_type, \
uint8_t report_ID, \
uint8_t report_len, \
uint8_t *report_buf)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_CLASS,
.bRequest = SET_REPORT,
.wValue = (report_type << 8) | report_ID,
.wIndex = 0U,
.wLength = report_len
};
usbh_ctlstate_config(uhost, report_buf, report_len);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief deinitialize the host pipes used for the HID class
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
void usbh_hid_itf_deinit(usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
if(0x00U != hid->pipe_in) {
usb_pipe_halt(uhost->data, hid->pipe_in);
usbh_pipe_free(uhost->data, hid->pipe_in);
/* reset the pipe as free */
hid->pipe_in = 0U;
}
if(0x00U != hid->pipe_out) {
usb_pipe_halt(uhost->data, hid->pipe_out);
usbh_pipe_free(uhost->data, hid->pipe_out);
/* reset the channel as free */
hid->pipe_out = 0U;
}
}
/*!
\brief return device type
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval hid_type
*/
hid_type usbh_hid_device_type_get(usb_core_driver *udev, usbh_host *uhost)
{
hid_type type = HID_UNKNOWN;
uint8_t interface_protocol;
if(HOST_CLASS_HANDLER == uhost->cur_state) {
interface_protocol = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].itf_desc.bInterfaceProtocol;
if(USB_HID_PROTOCOL_KEYBOARD == interface_protocol) {
type = HID_KEYBOARD;
} else {
if(USB_HID_PROTOCOL_MOUSE == interface_protocol) {
type = HID_MOUSE;
}
}
}
return type;
}
/*!
\brief return HID device poll time
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval poll time (ms)
*/
uint8_t usbh_hid_poll_interval_get(usb_core_driver *udev, usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
if((HOST_CLASS_ENUM == uhost->cur_state) || \
(HOST_USER_INPUT == uhost->cur_state) || \
(HOST_CLASS_CHECK == uhost->cur_state) || \
(HOST_CLASS_HANDLER == uhost->cur_state)) {
return (uint8_t)(hid->poll);
} else {
return 0U;
}
}
/*!
\brief initialize the hid class
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_itf_init(usbh_host *uhost)
{
uint8_t num = 0U, ep_num = 0U, interface = 0U;
usbh_status status = USBH_BUSY;
interface = usbh_interface_find(&uhost->dev_prop, USB_HID_CLASS, USB_HID_SUBCLASS_BOOT_ITF, 0xFFU);
if(0xFFU == interface) {
uhost->usr_cb->dev_not_supported();
status = USBH_FAIL;
} else {
usbh_interface_select(&uhost->dev_prop, interface);
static usbh_hid_handler hid_handler;
memset((void *)&hid_handler, 0U, sizeof(usbh_hid_handler));
hid_handler.state = HID_ERROR;
uint8_t itf_protocol = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].itf_desc.bInterfaceProtocol;
if(USB_HID_PROTOCOL_KEYBOARD == itf_protocol) {
hid_handler.init = usbh_hid_keybrd_init;
hid_handler.decode = usbh_hid_keybrd_decode;
} else if(USB_HID_PROTOCOL_MOUSE == itf_protocol) {
hid_handler.init = usbh_hid_mouse_init;
hid_handler.decode = usbh_hid_mouse_decode;
} else {
status = USBH_FAIL;
}
hid_handler.state = HID_INIT;
hid_handler.ctl_state = HID_REQ_INIT;
hid_handler.ep_addr = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].ep_desc[0].bEndpointAddress;
hid_handler.len = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].ep_desc[0].wMaxPacketSize;
hid_handler.poll = uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].ep_desc[0].bInterval;
if(hid_handler.poll < HID_MIN_POLL) {
hid_handler.poll = HID_MIN_POLL;
}
/* check for available number of endpoints */
/* find the number of endpoints in the interface descriptor */
/* choose the lower number in order not to overrun the buffer allocated */
ep_num = USB_MIN(uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].itf_desc.bNumEndpoints, USBH_MAX_EP_NUM);
/* decode endpoint IN and OUT address from interface descriptor */
for(num = 0U; num < ep_num; num++) {
usb_desc_ep *ep_desc = &uhost->dev_prop.cfg_desc_set.itf_desc_set[uhost->dev_prop.cur_itf][0].ep_desc[num];
uint8_t ep_addr = ep_desc->bEndpointAddress;
if(ep_addr & 0x80U) {
hid_handler.ep_in = ep_addr;
hid_handler.pipe_in = usbh_pipe_allocate(uhost->data, ep_addr);
/* open channel for IN endpoint */
usbh_pipe_create(uhost->data, \
&uhost->dev_prop, \
hid_handler.pipe_in, \
USB_EPTYPE_INTR, \
hid_handler.len);
usbh_pipe_toggle_set(uhost->data, hid_handler.pipe_in, 0U);
} else {
hid_handler.ep_out = ep_addr;
hid_handler.pipe_out = usbh_pipe_allocate(uhost->data, ep_addr);
/* open channel for OUT endpoint */
usbh_pipe_create(uhost->data, \
&uhost->dev_prop, \
hid_handler.pipe_out, \
USB_EPTYPE_INTR, \
hid_handler.len);
usbh_pipe_toggle_set(uhost->data, hid_handler.pipe_out, 0U);
}
}
uhost->active_class->class_data = (void *)&hid_handler;
status = USBH_OK;
}
return status;
}
/*!
\brief handle HID class requests for HID class
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_class_req(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
usbh_status class_req_status = USBH_BUSY;
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
/* handle HID control state machine */
switch(hid->ctl_state) {
case HID_REQ_INIT:
case HID_REQ_GET_HID_DESC:
/* get HID descriptor */
if(USBH_OK == usbh_hid_desc_get(uhost, USB_HID_DESC_SIZE)) {
usbh_hiddesc_parse(&hid->hid_desc, uhost->dev_prop.data);
hid->ctl_state = HID_REQ_GET_REPORT_DESC;
}
break;
case HID_REQ_GET_REPORT_DESC:
/* get report descriptor */
if(USBH_OK == usbh_hid_reportdesc_get(uhost, hid->hid_desc.wDescriptorLength)) {
hid->ctl_state = HID_REQ_SET_IDLE;
}
break;
case HID_REQ_SET_IDLE:
class_req_status = usbh_set_idle(uhost, 0U, 0U);
/* set idle */
if(USBH_OK == class_req_status) {
hid->ctl_state = HID_REQ_SET_PROTOCOL;
} else {
if(USBH_NOT_SUPPORTED == class_req_status) {
hid->ctl_state = HID_REQ_SET_PROTOCOL;
}
}
break;
case HID_REQ_SET_PROTOCOL:
/* set protocol */
if(USBH_OK == usbh_set_protocol(uhost, 0U)) {
hid->ctl_state = HID_REQ_IDLE;
/* all requests performed */
status = USBH_OK;
}
break;
case HID_REQ_IDLE:
default:
break;
}
return status;
}
/*!
\brief manage state machine for HID data transfers
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_handle(usbh_host *uhost)
{
usbh_status status = USBH_OK;
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
switch(hid->state) {
case HID_INIT:
hid->init(uhost->data, uhost);
hid->state = HID_IDLE;
break;
case HID_IDLE:
hid->state = HID_SYNC;
status = USBH_OK;
break;
case HID_SYNC:
/* sync with start of even frame */
if(1U == usb_frame_even(uhost->data)) {
hid->state = HID_GET_DATA;
}
break;
case HID_GET_DATA:
usbh_data_recev(uhost->data, hid->pdata, hid->pipe_in, hid->len);
hid->state = HID_POLL;
hid->timer = usb_curframe_get(uhost->data);
hid->data_ready = 0U;
break;
case HID_POLL:
if(URB_DONE == usbh_urbstate_get(uhost->data, hid->pipe_in)) {
if(0U == hid->data_ready) {
hid->data_ready = 1U;
hid->decode(hid->pdata);
}
} else {
/* check IN endpoint STALL status */
if(URB_STALL == usbh_urbstate_get(uhost->data, hid->pipe_in)) {
/* issue clear feature on interrupt IN endpoint */
if(USBH_OK == (usbh_clrfeature(uhost, hid->ep_addr, hid->pipe_in))) {
/* change state to issue next IN token */
hid->state = HID_GET_DATA;
}
}
}
break;
default:
break;
}
return status;
}
/*!
\brief send get report descriptor command to the device
\param[in] uhost: pointer to USB host
\param[in] len: HID report descriptor length
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_reportdesc_get(usbh_host *uhost, uint16_t len)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_ITF | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_REPORT),
.wIndex = 0U,
.wLength = len
};
usbh_ctlstate_config(uhost, uhost->dev_prop.data, len);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief managing the SOF process
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_sof(usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
if(HID_POLL == hid->state) {
uint32_t frame_count = usb_curframe_get(uhost->data);
if((frame_count > hid->timer) && ((frame_count - hid->timer) >= hid->poll)) {
hid->state = HID_GET_DATA;
} else if((frame_count < hid->timer) && ((frame_count + 0x3FFFU - hid->timer) >= hid->poll)) {
hid->state = HID_GET_DATA;
} else {
/* no operation */
}
}
return USBH_OK;
}
/*!
\brief send the command of get HID descriptor to the device
\param[in] uhost: pointer to USB host
\param[in] len: HID descriptor length
\param[out] none
\retval operation status
*/
static usbh_status usbh_hid_desc_get(usbh_host *uhost, uint16_t len)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_ITF | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_HID),
.wIndex = 0U,
.wLength = len
};
usbh_ctlstate_config(uhost, uhost->dev_prop.data, len);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set idle state
\param[in] uhost: pointer to USB host
\param[in] duration: duration for HID set idle request
\param[in] report_ID: targeted report ID for HID set idle request
\param[out] none
\retval operation status
*/
static usbh_status usbh_set_idle(usbh_host *uhost, uint8_t duration, uint8_t report_ID)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_CLASS,
.bRequest = SET_IDLE,
.wValue = (duration << 8) | report_ID,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set protocol state
\param[in] uhost: pointer to USB host
\param[in] protocol: boot/report protocol
\param[out] none
\retval operation status
*/
static usbh_status usbh_set_protocol(usbh_host *uhost, uint8_t protocol)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_CLASS,
.bRequest = SET_PROTOCOL,
.wValue = !protocol,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief parse the HID descriptor
\param[in] hid_desc: pointer to HID descriptor
\param[in] buf: pointer to buffer where the source descriptor is available
\param[out] none
\retval none
*/
static void usbh_hiddesc_parse(usb_desc_hid *hid_desc, uint8_t *buf)
{
hid_desc->header.bLength = *(uint8_t *)(buf + 0U);
hid_desc->header.bDescriptorType = *(uint8_t *)(buf + 1U);
hid_desc->bcdHID = BYTE_SWAP(buf + 2U);
hid_desc->bCountryCode = *(uint8_t *)(buf + 4U);
hid_desc->bNumDescriptors = *(uint8_t *)(buf + 5U);
hid_desc->bDescriptorType = *(uint8_t *)(buf + 6U);
hid_desc->wDescriptorLength = BYTE_SWAP(buf + 7U);
}
@@ -0,0 +1,271 @@
/*!
\file usbh_standard_hid.c
\brief USB host HID keyboard and mouse 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 "usbh_standard_hid.h"
mouse_report_data mouse_info;
hid_keybd_info keybd_info;
__ALIGN_BEGIN uint8_t hid_mouse_info[8] __ALIGN_END = {0U};
__ALIGN_BEGIN uint32_t keybd_report_data[2] __ALIGN_END;
/* local constants */
static const uint8_t kbd_codes[] = {
0, 0, 0, 0, 31, 50, 48, 33,
19, 34, 35, 36, 24, 37, 38, 39, /* 0x00 - 0x0F */
52, 51, 25, 26, 17, 20, 32, 21,
23, 49, 18, 47, 22, 46, 2, 3, /* 0x10 - 0x1F */
4, 5, 6, 7, 8, 9, 10, 11,
43, 110, 15, 16, 61, 12, 13, 27, /* 0x20 - 0x2F */
28, 29, 42, 40, 41, 1, 53, 54,
55, 30, 112, 113, 114, 115, 116, 117, /* 0x30 - 0x3F */
118, 119, 120, 121, 122, 123, 124, 125,
126, 75, 80, 85, 76, 81, 86, 89, /* 0x40 - 0x4F */
79, 84, 83, 90, 95, 100, 105, 106,
108, 93, 98, 103, 92, 97, 102, 91, /* 0x50 - 0x5F */
96, 101, 99, 104, 45, 129, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6F */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7F */
0, 0, 0, 0, 0, 107, 0, 56,
0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0 - 0xCF */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0 - 0xDF */
58, 44, 60, 127, 64, 57, 62, 128 /* 0xE0 - 0xE7 */
};
#ifdef QWERTY_KEYBOARD
static const int8_t kbd_key[] = {
'\0', '`', '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', '\0', '\r',
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u',
'i', 'o', 'p', '[', ']', '\\',
'\0', 'a', 's', 'd', 'f', 'g', 'h', 'j',
'k', 'l', ';', '\'', '\0', '\n',
'\0', '\0', 'z', 'x', 'c', 'v', 'b', 'n',
'm', ',', '.', '/', '\0', '\0',
'\0', '\0', '\0', ' ', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\r', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '7', '4', '1',
'\0', '/', '8', '5', '2',
'0', '*', '9', '6', '3',
'.', '-', '+', '\0', '\n', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0'
};
static const int8_t kbd_key_shift[] = {
'\0', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
'_', '+', '\0', '\0', '\0', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U',
'I', 'O', 'P', '{', '}', '|', '\0', 'A', 'S', 'D', 'F', 'G',
'H', 'J', 'K', 'L', ':', '"', '\0', '\n', '\0', '\0', 'Z', 'X',
'C', 'V', 'B', 'N', 'M', '<', '>', '?', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
};
#else
static const int8_t kbd_key[] = {
'\0', '`', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
'-', '=', '\0', '\r', '\t', 'a', 'z', 'e', 'r', 't', 'y', 'u',
'i', 'o', 'p', '[', ']', '\\', '\0', 'q', 's', 'd', 'f', 'g',
'h', 'j', 'k', 'l', 'm', '\0', '\0', '\n', '\0', '\0', 'w', 'x',
'c', 'v', 'b', 'n', ',', ';', ':', '!', '\0', '\0', '\0', '\0',
'\0', ' ', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\r', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '7', '4', '1', '\0', '/',
'8', '5', '2', '0', '*', '9', '6', '3', '.', '-', '+', '\0',
'\n', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
};
static const int8_t kbd_key_shift[] = {
'\0', '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_',
'+', '\0', '\0', '\0', 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O',
'P', '{', '}', '*', '\0', 'Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K',
'L', 'M', '%', '\0', '\n', '\0', '\0', 'W', 'X', 'C', 'V', 'B', 'N',
'?', '.', '/', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
};
#endif /* QWERTY_KEYBOARD */
/*!
\brief initialize the mouse function
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
usbh_status usbh_hid_mouse_init(usb_core_driver *udev, usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
mouse_info.x = 0U;
mouse_info.y = 0U;
mouse_info.buttons[0] = 0U;
mouse_info.buttons[1] = 0U;
mouse_info.buttons[2] = 0U;
if(hid->len > sizeof(hid_mouse_info)) {
hid->len = sizeof(hid_mouse_info);
}
hid->pdata = (uint8_t *)(void *)hid_mouse_info;
usr_mouse_init();
return USBH_OK;
}
/*!
\brief decode mouse information
\param[in] data: pointer to input data
\param[out] none
\retval operation status
*/
usbh_status usbh_hid_mouse_decode(uint8_t *data)
{
mouse_info.buttons[0] = data[0] & MOUSE_BUTTON_1;
mouse_info.buttons[1] = data[0] & MOUSE_BUTTON_2;
mouse_info.buttons[2] = data[0] & MOUSE_BUTTON_3;
mouse_info.x = data[1];
mouse_info.y = data[2];
/* handle mouse data position */
usr_mouse_process_data(&mouse_info);
return USBH_FAIL;
}
/*!
\brief initialize the keyboard function
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
usbh_status usbh_hid_keybrd_init(usb_core_driver *udev, usbh_host *uhost)
{
usbh_hid_handler *hid = (usbh_hid_handler *)uhost->active_class->class_data;
keybd_info.lctrl = keybd_info.lshift = 0U;
keybd_info.lalt = keybd_info.lgui = 0U;
keybd_info.rctrl = keybd_info.rshift = 0U;
keybd_info.ralt = keybd_info.rgui = 0U;
for(uint32_t x = 0U; x < (sizeof(keybd_report_data) / sizeof(uint32_t)); x++) {
keybd_report_data[x] = 0U;
}
if(hid->len > (sizeof(keybd_report_data) / sizeof(uint32_t))) {
hid->len = (sizeof(keybd_report_data) / sizeof(uint32_t));
}
hid->pdata = (uint8_t *)(void *)keybd_report_data;
/* call user initialization*/
usr_keybrd_init();
return USBH_OK;
}
/*!
\brief get ASCII code
\param[in] info: keyboard information
\param[out] none
\retval output
*/
uint8_t usbh_hid_ascii_code_get(hid_keybd_info *info)
{
uint8_t output = 0U;
if((1U == info->lshift) || (info->rshift)) {
output = kbd_key_shift[kbd_codes[info->keys[0]]];
} else {
output = kbd_key[kbd_codes[info->keys[0]]];
}
return output;
}
/*!
\brief decode keyboard information
\param[in] udev: pointer to USB core instance
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
usbh_status usbh_hid_keybrd_decode(uint8_t *data)
{
uint8_t output = 0U;
keybd_info.lshift = data[0] & KBD_LEFT_SHIFT;
keybd_info.rshift = data[0] & KBD_RIGHT_SHIFT;
keybd_info.keys[0] = data[2];
if(keybd_info.lshift || keybd_info.rshift) {
output = kbd_key_shift[kbd_codes[keybd_info.keys[0]]];
} else {
output = kbd_key[kbd_codes[keybd_info.keys[0]]];
}
if(0U != output) {
usr_keybrd_process_data(output);
}
return USBH_OK;
}
@@ -0,0 +1,148 @@
/*!
\file usbh_msc_bbb.h
\brief header file for usbh_msc_bbb.c
\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 USBH_MSC_BBB_H
#define USBH_MSC_BBB_H
#include "msc_bbb.h"
#include "usbh_core.h"
#define USBH_MSC_BBB_CBW_TAG 0x20304050U /*!< MSC BBB CBW tag */
#define USBH_MSC_CSW_MAX_LENGTH 63U /*!< MSC CSW maximum length */
#define USBH_MSC_SEND_CSW_DISABLE 0U /*!< MSC send CSW disable */
#define USBH_MSC_SEND_CSW_ENABLE 1U /*!< MSC send CSW enable */
#define USBH_MSC_DIR_IN 0U /*!< MSC data transfer IN */
#define USBH_MSC_DIR_OUT 1U /*!< MSC data transfer OUT */
#define USBH_MSC_BOTH_DIR 2U /*!< MSC data transfer IN/OUT */
#define USBH_MSC_PAGE_LENGTH 512U /*!< MSC memory page length */
#define CBW_CB_LENGTH 16U /*!< MSC CBW CB length */
#define CBW_LENGTH 10U /*!< MSC CBW length */
#define CBW_LENGTH_TEST_UNIT_READY 0U /*!< MSC CBW test unit ready length */
#define MAX_BULK_STALL_COUNT_LIMIT 0x04U /*!< if STALL is seen on bulk
endpoint continously, this means
that device and host has phase error
hence a reset is needed */
typedef union {
msc_bbb_cbw field; /*!< MSC BBB CBW structure */
uint8_t CBWArray[31]; /*!< MSC BBB CBW array buff */
}usbh_cbw_pkt;
typedef union {
msc_bbb_csw field; /*!< MSC BBB CSW structure */
uint8_t CSWArray[13]; /*!< MSC BBB CSW array buff */
}usbh_csw_pkt;
enum usbh_msc_state {
USBH_MSC_BBB_INIT_STATE = 0U, /*!< MSC BBB init state */
USBH_MSC_BBB_RESET, /*!< MSC BBB reset state */
USBH_MSC_GET_MAX_LUN, /*!< MSC init state */
USBH_MSC_TEST_UNIT_READY, /*!< MSC test unit ready state */
USBH_MSC_READ_CAPACITY10, /*!< MSC read capacity10 state */
USBH_MSC_MODE_SENSE6, /*!< MSC sense6 mode state */
USBH_MSC_REQUEST_SENSE, /*!< MSC request sense state */
USBH_MSC_BBB_USB_TRANSFERS, /*!< MSC BBB transfers state */
USBH_MSC_DEFAULT_APPLI_STATE, /*!< MSC default application state */
USBH_MSC_CTRL_ERROR_STATE, /*!< MSC control error state */
USBH_MSC_UNRECOVERED_STATE /*!< MSC unrecovered state */
};
/* MSC BBB status types */
typedef enum {
BBB_OK = 0U, /*!< MSC BBB OK status */
BBB_FAIL, /*!< MSC BBB fail status */
BBB_PHASE_ERROR, /*!< MSC BBB phase error status */
BBB_BUSY /*!< MSC BBB busy status */
} bbb_status;
/* MSC BBB command state types */
typedef enum {
BBB_CMD_IDLE = 0U, /*!< MSC BBB command idle state */
BBB_CMD_SEND, /*!< MSC BBB command send state */
BBB_CMD_WAIT /*!< MSC BBB command wait state */
} bbb_cmd_state;
/* CSW status definitions */
typedef enum {
BBB_CSW_CMD_PASSED = 0U, /*!< MSC BBB CSW command passed status */
BBB_CSW_CMD_FAILED, /*!< MSC BBB CSW command failed status */
BBB_CSW_PHASE_ERROR /*!< MSC BBB CSW phase error status */
} bbb_csw_status;
/* MSC BBB state types */
typedef enum {
BBB_SEND_CBW = 1U, /*!< MSC BBB send CBW state */
BBB_SEND_CBW_WAIT, /*!< MSC BBB send CBW wait state */
BBB_DATA_IN, /*!< MSC BBB data IN state */
BBB_DATA_IN_WAIT, /*!< MSC BBB data IN wait state */
BBB_DATA_OUT, /*!< MSC BBB data OUT state */
BBB_DATA_OUT_WAIT, /*!< MSC BBB data OUT wait state */
BBB_RECEIVE_CSW, /*!< MSC BBB receive CSW state */
BBB_RECEIVE_CSW_WAIT, /*!< MSC BBB receive CSW wait state */
BBB_ERROR_IN, /*!< MSC BBB error IN state */
BBB_ERROR_OUT, /*!< MSC BBB error OUT state */
BBB_UNRECOVERED_ERROR /*!< MSC BBB unrecovered error state */
} bbb_state;
typedef struct {
uint8_t *pbuf; /*!< MSC BBB data buff pointer */
uint32_t data[16]; /*!< MSC BBB data buff */
bbb_state state; /*!< MSC BBB state */
bbb_state prev_state; /*!< MSC BBB previous state */
bbb_cmd_state cmd_state; /*!< MSC BBB command state */
usbh_cbw_pkt cbw; /*!< MSC CBW pocket structure */
usbh_csw_pkt csw; /*!< MSC CSW pocket structure */
} bbb_handle;
/* function declarations */
/* initialize the mass storage parameters */
void usbh_msc_bbb_init(usbh_host *uhost);
/* manage the different states of BBB transfer and updates the status to upper layer */
usbh_status usbh_msc_bbb_process(usbh_host *uhost, uint8_t lun);
/* manages the different error handling for STALL */
usbh_status usbh_msc_bbb_abort(usbh_host *uhost, uint8_t direction);
/* reset MSC BBB request structure */
usbh_status usbh_msc_bbb_reset(usbh_host *uhost);
/* decode the CSW received by the device and updates the same to upper layer */
bbb_csw_status usbh_msc_csw_decode(usbh_host *uhost);
#endif /* USBH_MSC_BBB_H */
@@ -0,0 +1,122 @@
/*!
\file usbh_core.h
\brief header file for the usbh_core.c
\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 USBH_MSC_CORE_H
#define USBH_MSC_CORE_H
#include "usb_msc.h"
#include "usbh_msc_scsi.h"
#include "usbh_msc_bbb.h"
#define MSC_MAX_SUPPORTED_LUN 2U
/* MSC state types */
typedef enum {
MSC_INIT = 0U, /*!< MSC init state */
MSC_IDLE, /*!< MSC idle state */
MSC_TEST_UNIT_READY, /*!< MSC test unit ready state */
MSC_READ_CAPACITY10, /*!< MSC read capacity10 state */
MSC_READ_INQUIRY, /*!< MSC read inquiry state */
MSC_REQUEST_SENSE, /*!< MSC request sense state */
MSC_READ, /*!< MSC read state */
MSC_WRITE, /*!< MSC write state */
MSC_UNRECOVERED_ERROR, /*!< MSC unrecovered state */
MSC_PERIODIC_CHECK /*!< MSC periodic check state */
} msc_state;
/* MSC error types */
typedef enum {
MSC_OK = 0U, /*!< MSC no error */
MSC_NOT_READY, /*!< MSC not ready */
MSC_ERROR /*!< MSC error */
} msc_error;
/* MSC request types */
typedef enum {
MSC_REQ_IDLE = 0U, /*!< MSC idle request state */
MSC_REQ_RESET, /*!< MSC reset request state */
MSC_REQ_GET_MAX_LUN, /*!< MSC get maximum LUN request state */
MSC_REQ_ERROR /*!< MSC error request state */
} msc_req_state;
/* structure for LUN */
typedef struct {
msc_state state; /*!< MSC LUN state */
msc_error error; /*!< MSC LUN error */
msc_scsi_sense sense; /*!< MSC SCSI sense */
scsi_capacity capacity; /*!< MSC SCSI capacity */
scsi_std_inquiry_data inquiry; /*!< MSC SCSI standard inquiry data */
usbh_status prev_ready_state; /*!< MSC previous ready state */
uint8_t state_changed; /*!< MSC state changed */
} msc_lun;
/* structure for MSC process */
typedef struct _msc_process {
uint8_t pipe_in; /*!< MSC pipe IN */
uint8_t pipe_out; /*!< MSC pipe OUT */
uint8_t ep_in; /*!< MSC endpoint IN */
uint8_t ep_out; /*!< MSC endpoint OUT */
uint16_t ep_size_in; /*!< MSC endpoint IN size */
uint16_t ep_size_out; /*!< MSC endpoint OUT size */
uint8_t cur_lun; /*!< MSC current LUN */
uint16_t rw_lun; /*!< MSC review LUN */
uint32_t max_lun; /*!< MSC maximum LUN */
msc_state state; /*!< MSC state */
msc_error error; /*!< MSC error */
msc_req_state req_state; /*!< MSC request state */
msc_req_state prev_req_state; /*!< MSC previous request state */
bbb_handle bbb; /*!< MSC BBB correlation parameter handle */
msc_lun unit[MSC_MAX_SUPPORTED_LUN]; /*!< MSC LUN unit buff */
uint32_t timer; /*!< MSC read/write timer */
} usbh_msc_handler;
extern usbh_class usbh_msc;
/* function declarations */
/* get MSC logic unit information */
usbh_status usbh_msc_lun_info_get(usbh_host *uhost, uint8_t lun, msc_lun *info);
/* MSC read interface */
usbh_status usbh_msc_read(usbh_host *uhost, \
uint8_t lun, \
uint32_t address, \
uint8_t *pbuf, \
uint32_t length);
/* MSC write interface */
usbh_status usbh_msc_write(usbh_host *uhost, \
uint8_t lun, \
uint32_t address, \
uint8_t *pbuf, \
uint32_t length);
#endif /* USBH_MSC_CORE_H */
@@ -0,0 +1,97 @@
/*!
\file usbh_msc_scsi.h
\brief header file for usbh_msc_scsi.c
\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 USBH_MSC_SCSI_H
#define USBH_MSC_SCSI_H
#include "msc_scsi.h"
#include "usbh_enum.h"
#define DESC_REQUEST_SENSE 00U /*!< sense request descriptor */
#define ALLOCATION_LENGTH_REQUEST_SENSE 63U /*!< sense request allocation length */
#define XFER_LEN_MODE_SENSE6 63U /*!< MSC sense6 mode transfer length */
#define MASK_MODE_SENSE_WRITE_PROTECT 0x80U /*!< sense write protect mask mode */
#define MODE_SENSE_PAGE_CONTROL_FIELD 0x00U /*!< sense page control field mode */
#define MODE_SENSE_PAGE_CODE 0x3FU /*!< sense page code mode */
#define DISK_WRITE_PROTECTED 0x01U /*!< disk write protected */
/* capacity data */
typedef struct {
uint32_t block_nbr; /*!< MSC SCSI block number */
uint16_t block_size; /*!< MSC SCSI block size */
} scsi_capacity;
/* inquiry data */
typedef struct {
uint8_t peripheral_qualifier; /*!< MSC SCSI standard peripheral qualifier */
uint8_t device_type; /*!< MSC SCSI standard inquiry device types */
uint8_t removable_media; /*!< MSC SCSI standard inquiry removable media */
uint8_t vendor_id[9]; /*!< MSC SCSI standard inquiry vendor id buff */
uint8_t product_id[17]; /*!< MSC SCSI standard inquiry product id buff */
uint8_t revision_id[5]; /*!< MSC SCSI standard inquiry revision id buff */
} scsi_std_inquiry_data;
typedef struct {
uint32_t msc_capacity; /*!< MSC capacity */
uint32_t msc_sense_key; /*!< MSC sense key */
uint16_t msc_page_len; /*!< MSC memory page length */
uint8_t msc_write_protect; /*!< MSC write protect */
}usbh_msc_parameter;
/* function declarations */
/* send 'inquiry' command to the device */
usbh_status usbh_msc_scsi_inquiry(usbh_host *uhost, uint8_t lun, scsi_std_inquiry_data *inquiry);
/* send 'test unit ready' command to the device */
usbh_status usbh_msc_test_unitready(usbh_host *uhost, uint8_t lun);
/* send the read capacity command to the device */
usbh_status usbh_msc_read_capacity10(usbh_host *uhost, uint8_t lun, scsi_capacity *capacity);
/* send the mode sense6 command to the device */
usbh_status usbh_msc_mode_sense6(usbh_host *uhost, uint8_t lun);
/* send the request sense command to the device */
usbh_status usbh_msc_request_sense(usbh_host *uhost, uint8_t lun, msc_scsi_sense *sense_data);
/* send the write10 command to the device */
usbh_status usbh_msc_write10(usbh_host *uhost, \
uint8_t lun, \
uint8_t *data_buf, \
uint32_t addr, \
uint32_t byte_num);
/* send the read10 command to the device */
usbh_status usbh_msc_read10(usbh_host *uhost, \
uint8_t lun, \
uint8_t *data_buf, \
uint32_t addr, \
uint32_t byte_num);
#endif /* USBH_MSC_SCSI_H */
@@ -0,0 +1,360 @@
/*!
\file usbh_msc_bbb.c
\brief USB MSC BBB protocol related 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 "usbh_pipe.h"
#include "usbh_transc.h"
#include "usbh_msc_core.h"
#include "usbh_msc_bbb.h"
/*!
\brief initialize the mass storage parameters
\param[in] uhost: pointer to USB host handler
\param[out] none
\retval none
*/
void usbh_msc_bbb_init(usbh_host *uhost)
{
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
msc->bbb.cbw.field.dCBWSignature = BBB_CBW_SIGNATURE;
msc->bbb.cbw.field.dCBWTag = USBH_MSC_BBB_CBW_TAG;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_SEND;
}
/*!
\brief manage the different states of BBB transfer and updates the status to upper layer
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_bbb_process(usbh_host *uhost, uint8_t lun)
{
bbb_csw_status csw_status = BBB_CSW_CMD_FAILED;
usbh_status status = USBH_BUSY;
usbh_status error = USBH_BUSY;
usb_urb_state urb_status = URB_IDLE;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.state) {
case BBB_SEND_CBW:
msc->bbb.cbw.field.bCBWLUN = lun;
msc->bbb.state = BBB_SEND_CBW_WAIT;
/* send CBW */
usbh_data_send(uhost->data, \
msc->bbb.cbw.CBWArray, \
msc->pipe_out, \
BBB_CBW_LENGTH);
break;
case BBB_SEND_CBW_WAIT:
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_out);
if(URB_DONE == urb_status) {
if(0U != msc->bbb.cbw.field.dCBWDataTransferLength) {
if(USB_TRX_IN == (msc->bbb.cbw.field.bmCBWFlags & USB_TRX_MASK)) {
msc->bbb.state = BBB_DATA_IN;
} else {
msc->bbb.state = BBB_DATA_OUT;
}
} else {
msc->bbb.state = BBB_RECEIVE_CSW;
}
} else if(URB_NOTREADY == urb_status) {
msc->bbb.state = BBB_SEND_CBW;
} else {
if(URB_STALL == urb_status) {
msc->bbb.state = BBB_ERROR_OUT;
}
}
break;
case BBB_DATA_IN:
usbh_data_recev(uhost->data, \
msc->bbb.pbuf, \
msc->pipe_in, \
msc->ep_size_in);
msc->bbb.state = BBB_DATA_IN_WAIT;
break;
case BBB_DATA_IN_WAIT:
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_in);
/* BBB DATA IN stage */
if(URB_DONE == urb_status) {
if(msc->bbb.cbw.field.dCBWDataTransferLength > msc->ep_size_in) {
msc->bbb.pbuf += msc->ep_size_in;
msc->bbb.cbw.field.dCBWDataTransferLength -= msc->ep_size_in;
} else {
msc->bbb.cbw.field.dCBWDataTransferLength = 0U;
}
if(msc->bbb.cbw.field.dCBWDataTransferLength > 0U) {
usbh_data_recev(uhost->data, \
msc->bbb.pbuf, \
msc->pipe_in, \
msc->ep_size_in);
} else {
msc->bbb.state = BBB_RECEIVE_CSW;
}
} else if(URB_STALL == urb_status) {
/* this is data stage STALL condition */
msc->bbb.state = BBB_ERROR_IN;
} else {
/* no operation */
}
break;
case BBB_DATA_OUT:
usbh_data_send(uhost->data, \
msc->bbb.pbuf, \
msc->pipe_out, \
msc->ep_size_out);
msc->bbb.state = BBB_DATA_OUT_WAIT;
break;
case BBB_DATA_OUT_WAIT:
/* BBB DATA OUT stage */
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_out);
if(URB_DONE == urb_status) {
if(msc->bbb.cbw.field.dCBWDataTransferLength > msc->ep_size_out) {
msc->bbb.pbuf += msc->ep_size_out;
msc->bbb.cbw.field.dCBWDataTransferLength -= msc->ep_size_out;
} else {
msc->bbb.cbw.field.dCBWDataTransferLength = 0U; /* reset this value and keep in same state */
}
if(msc->bbb.cbw.field.dCBWDataTransferLength > 0U) {
usbh_data_send(uhost->data, \
msc->bbb.pbuf, \
msc->pipe_out, \
msc->ep_size_out);
} else {
msc->bbb.state = BBB_RECEIVE_CSW;
}
} else if(URB_NOTREADY == urb_status) {
msc->bbb.state = BBB_DATA_OUT;
} else if(URB_STALL == urb_status) {
msc->bbb.state = BBB_ERROR_OUT;
} else {
/* no operation */
}
break;
case BBB_RECEIVE_CSW:
/* BBB CSW stage */
usbh_data_recev(uhost->data, \
msc->bbb.csw.CSWArray, \
msc->pipe_in, \
BBB_CSW_LENGTH);
msc->bbb.state = BBB_RECEIVE_CSW_WAIT;
break;
case BBB_RECEIVE_CSW_WAIT:
urb_status = usbh_urbstate_get(uhost->data, msc->pipe_in);
/* decode CSW */
if(URB_DONE == urb_status) {
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_SEND;
csw_status = usbh_msc_csw_decode(uhost);
if(BBB_CSW_CMD_PASSED == csw_status) {
status = USBH_OK;
} else {
status = USBH_FAIL;
}
} else if(URB_STALL == urb_status) {
msc->bbb.state = BBB_ERROR_IN;
} else {
/* no operation */
}
break;
case BBB_ERROR_IN:
error = usbh_msc_bbb_abort(uhost, USBH_MSC_DIR_IN);
if(USBH_OK == error) {
msc->bbb.state = BBB_RECEIVE_CSW;
} else if(USBH_UNRECOVERED_ERROR == status) {
/* this means that there is a STALL error limit, do reset recovery */
msc->bbb.state = BBB_UNRECOVERED_ERROR;
} else {
/* no operation */
}
break;
case BBB_ERROR_OUT:
status = usbh_msc_bbb_abort(uhost, USBH_MSC_DIR_OUT);
if(USBH_OK == status) {
uint8_t toggle = usbh_pipe_toggle_get(uhost->data, msc->pipe_out);
usbh_pipe_toggle_set(uhost->data, msc->pipe_out, 1U - toggle);
usbh_pipe_toggle_set(uhost->data, msc->pipe_in, 0U);
msc->bbb.state = BBB_ERROR_IN;
} else {
if(USBH_UNRECOVERED_ERROR == status) {
msc->bbb.state = BBB_UNRECOVERED_ERROR;
}
}
break;
case BBB_UNRECOVERED_ERROR:
status = usbh_msc_bbb_reset(uhost);
if(USBH_OK == status) {
msc->bbb.state = BBB_SEND_CBW;
}
break;
default:
break;
}
return status;
}
/*!
\brief manages the different error handling for stall
\param[in] uhost: pointer to USB host handler
\param[in] direction: data IN or OUT
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_bbb_abort(usbh_host *uhost, uint8_t direction)
{
usbh_status status = USBH_BUSY;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(direction) {
case USBH_MSC_DIR_IN :
/* send clrfeature command on bulk IN endpoint */
status = usbh_clrfeature(uhost, \
msc->ep_in, \
msc->pipe_in);
break;
case USBH_MSC_DIR_OUT :
/*send clrfeature command on bulk OUT endpoint */
status = usbh_clrfeature(uhost, \
msc->ep_out, \
msc->pipe_out);
break;
default:
break;
}
return status;
}
/*!
\brief reset MSC BBB transfer
\param[in] uhost: pointer to USB host handler
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_bbb_reset(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_REQTYPE_CLASS | USB_RECPTYPE_ITF,
.bRequest = BBB_RESET,
.wValue = 0U,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief decode the CSW received by the device and updates the same to upper layer
\param[in] uhost: pointer to USB host
\param[out] none
\retval on success USBH_MSC_OK, on failure USBH_MSC_FAIL
\notes
Refer to USB Mass-Storage Class: BBB (www.usb.org)
6.3.1 Valid CSW Conditions :
The host shall consider the CSW valid when:
1. dCSWSignature is equal to 53425355h
2. the CSW is 13 (Dh) bytes in length,
3. dCSWTag matches the dCBWTag from the corresponding CBW.
*/
bbb_csw_status usbh_msc_csw_decode(usbh_host *uhost)
{
bbb_csw_status status = BBB_CSW_CMD_FAILED;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
/* checking if the transfer length is different than 13 */
if(BBB_CSW_LENGTH != usbh_xfercount_get(uhost->data, msc->pipe_in)) {
status = BBB_CSW_PHASE_ERROR;
} else {
/* CSW length is correct */
/* check validity of the CSW Signature and CSWStatus */
if(BBB_CSW_SIGNATURE == msc->bbb.csw.field.dCSWSignature) {
/* check condition 1. dCSWSignature is equal to 53425355h */
if(msc->bbb.csw.field.dCSWTag == msc->bbb.cbw.field.dCBWTag) {
/* check condition 3. dCSWTag matches the dCBWTag from the corresponding CBW */
if(0U == msc->bbb.csw.field.bCSWStatus) {
status = BBB_CSW_CMD_PASSED;
} else if(1U == msc->bbb.csw.field.bCSWStatus) {
status = BBB_CSW_CMD_FAILED;
} else if(2U == msc->bbb.csw.field.bCSWStatus) {
status = BBB_CSW_PHASE_ERROR;
} else {
/* no operation */
}
}
} else {
/* if the CSW signature is not valid, we shall return the phase error to
upper layers for reset recovery */
status = BBB_CSW_PHASE_ERROR;
}
}
return status;
}
@@ -0,0 +1,551 @@
/*!
\file usbh_core.c
\brief USB MSC(mass storage device) 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 "usbh_msc_core.h"
#include "usbh_pipe.h"
#include "usbh_transc.h"
#include <string.h>
/* local function prototypes ('static') */
static void usbh_msc_itf_deinit(usbh_host *uhost);
static usbh_status usbh_msc_itf_init(usbh_host *uhost);
static usbh_status usbh_msc_req(usbh_host *uhost);
static usbh_status usbh_msc_handle(usbh_host *uhost);
static usbh_status usbh_msc_maxlun_get(usbh_host *uhost, uint8_t *maxlun);
static usbh_status usbh_msc_rdwr_process(usbh_host *uhost, uint8_t lun);
usbh_class usbh_msc = {
USB_CLASS_MSC,
usbh_msc_itf_init,
usbh_msc_itf_deinit,
usbh_msc_req,
usbh_msc_handle
};
/*!
\brief get MSC logic unit information
\param[in] uhost: pointer to USB host
\param[in] lun: logic unit number
\param[in] info: pointer to logic unit information
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_lun_info_get(usbh_host *uhost, uint8_t lun, msc_lun *info)
{
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
if(HOST_CLASS_HANDLER == uhost->cur_state) {
memcpy(info, &msc->unit[lun], sizeof(msc_lun));
return USBH_OK;
} else {
return USBH_FAIL;
}
}
/*!
\brief MSC read interface
\param[in] uhost: pointer to USB host
\param[in] lun: logic unit number
\param[in] address: address to be read
\param[in] pbuf: pointer to user buffer
\param[in] length: length to be read
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_read(usbh_host *uhost, \
uint8_t lun, \
uint32_t address, \
uint8_t *pbuf, \
uint32_t length)
{
uint32_t timeout = 0U;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
if((0U == udev->host.connect_status) || \
(HOST_CLASS_HANDLER != uhost->cur_state) || \
(MSC_IDLE != msc->unit[lun].state)) {
return USBH_FAIL;
}
msc->state = MSC_READ;
msc->unit[lun].state = MSC_READ;
msc->rw_lun = lun;
usbh_msc_read10(uhost, lun, pbuf, address, length);
timeout = uhost->control.timer;
while(USBH_BUSY == usbh_msc_rdwr_process(uhost, lun)) {
if(((uhost->control.timer - timeout) > (10000U * length)) || (0U == udev->host.connect_status)) {
msc->state = MSC_IDLE;
return USBH_FAIL;
}
}
msc->state = MSC_IDLE;
return USBH_OK;
}
/*!
\brief MSC write interface
\param[in] uhost: pointer to USB host
\param[in] lun: logic unit number
\param[in] address: address to be written
\param[in] pbuf: pointer to user buffer
\param[in] length: length to be written
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_write(usbh_host *uhost, \
uint8_t lun, \
uint32_t address, \
uint8_t *pbuf, \
uint32_t length)
{
uint32_t timeout = 0U;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
if((0U == udev->host.connect_status) || \
(HOST_CLASS_HANDLER != uhost->cur_state) || \
(MSC_IDLE != msc->unit[lun].state)) {
return USBH_FAIL;
}
msc->state = MSC_WRITE;
msc->unit[lun].state = MSC_WRITE;
msc->rw_lun = lun;
usbh_msc_write10(uhost, lun, pbuf, address, length);
timeout = uhost->control.timer;
while(USBH_BUSY == usbh_msc_rdwr_process(uhost, lun)) {
if(((uhost->control.timer - timeout) > (10000U * length)) || (0U == udev->host.connect_status)) {
msc->state = MSC_IDLE;
return USBH_FAIL;
}
}
msc->state = MSC_IDLE;
return USBH_OK;
}
/*!
\brief de-initialize interface by freeing host channels allocated to interface
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static void usbh_msc_itf_deinit(usbh_host *uhost)
{
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
if(msc->pipe_out) {
usb_pipe_halt(uhost->data, msc->pipe_out);
usbh_pipe_free(uhost->data, msc->pipe_out);
msc->pipe_out = 0U;
}
if(msc->pipe_in) {
usb_pipe_halt(uhost->data, msc->pipe_in);
usbh_pipe_free(uhost->data, msc->pipe_in);
msc->pipe_in = 0U;
}
}
/*!
\brief interface initialization for MSC class
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_itf_init(usbh_host *uhost)
{
usbh_status status = USBH_OK;
uint8_t interface = usbh_interface_find(&uhost->dev_prop, MSC_CLASS, USB_MSC_SUBCLASS_SCSI, MSC_PROTOCOL);
if(0xFFU == interface) {
uhost->usr_cb->dev_not_supported();
status = USBH_FAIL;
} else {
static usbh_msc_handler msc_handler;
memset((void *)&msc_handler, 0U, sizeof(usbh_msc_handler));
uhost->active_class->class_data = (void *)&msc_handler;
usbh_interface_select(&uhost->dev_prop, interface);
usb_desc_ep *ep_desc = &uhost->dev_prop.cfg_desc_set.itf_desc_set[interface][0].ep_desc[0];
if(ep_desc->bEndpointAddress & 0x80U) {
msc_handler.ep_in = ep_desc->bEndpointAddress;
msc_handler.ep_size_in = ep_desc->wMaxPacketSize;
} else {
msc_handler.ep_out = ep_desc->bEndpointAddress;
msc_handler.ep_size_out = ep_desc->wMaxPacketSize;
}
ep_desc = &uhost->dev_prop.cfg_desc_set.itf_desc_set[interface][0].ep_desc[1];
if(ep_desc->bEndpointAddress & 0x80U) {
msc_handler.ep_in = ep_desc->bEndpointAddress;
msc_handler.ep_size_in = ep_desc->wMaxPacketSize;
} else {
msc_handler.ep_out = ep_desc->bEndpointAddress;
msc_handler.ep_size_out = ep_desc->wMaxPacketSize;
}
msc_handler.state = MSC_INIT;
msc_handler.error = MSC_OK;
msc_handler.req_state = MSC_REQ_IDLE;
msc_handler.pipe_out = usbh_pipe_allocate(uhost->data, msc_handler.ep_out);
msc_handler.pipe_in = usbh_pipe_allocate(uhost->data, msc_handler.ep_in);
usbh_msc_bbb_init(uhost);
/* open the new channels */
usbh_pipe_create(uhost->data, \
&uhost->dev_prop, \
msc_handler.pipe_out, \
USB_EPTYPE_BULK, \
msc_handler.ep_size_out);
usbh_pipe_create(uhost->data, \
&uhost->dev_prop, \
msc_handler.pipe_in, \
USB_EPTYPE_BULK, \
msc_handler.ep_size_in);
usbh_pipe_toggle_set(uhost->data, msc_handler.pipe_out, 0U);
usbh_pipe_toggle_set(uhost->data, msc_handler.pipe_in, 0U);
}
return status;
}
/*!
\brief initialize the MSC state machine
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_req(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->req_state) {
case MSC_REQ_IDLE:
case MSC_REQ_GET_MAX_LUN:
/* issue Get_MaxLun request */
status = usbh_msc_maxlun_get(uhost, (uint8_t *)&msc->max_lun);
if(USBH_OK == status) {
msc->max_lun = ((uint8_t)msc->max_lun > MSC_MAX_SUPPORTED_LUN) ? MSC_MAX_SUPPORTED_LUN : (uint8_t)msc->max_lun + 1U;
for(uint8_t i = 0U; i < msc->max_lun; i++) {
msc->unit[i].prev_ready_state = USBH_FAIL;
msc->unit[i].state_changed = 0U;
}
} else {
if(USBH_NOT_SUPPORTED == status) {
msc->max_lun = 0U;
status = USBH_OK;
}
}
break;
case MSC_REQ_ERROR:
/* issue clearfeature request */
if(USBH_OK == usbh_clrfeature(uhost, 0x00U, uhost->control.pipe_out_num)) {
msc->req_state = msc->prev_req_state;
}
break;
default:
break;
}
return status;
}
/*!
\brief MSC state machine handler
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_handle(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
uint8_t scsi_status = USBH_BUSY;
uint8_t ready_status = USBH_BUSY;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->state) {
case MSC_INIT:
if(msc->cur_lun < msc->max_lun) {
msc->unit[msc->cur_lun].error = MSC_NOT_READY;
switch(msc->unit[msc->cur_lun].state) {
case MSC_INIT:
msc->unit[msc->cur_lun].state = MSC_READ_INQUIRY;
msc->timer = uhost->control.timer;
break;
case MSC_READ_INQUIRY:
scsi_status = usbh_msc_scsi_inquiry(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].inquiry);
if(USBH_OK == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_TEST_UNIT_READY;
} else if(USBH_FAIL == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_ERROR;
}
}
break;
case MSC_TEST_UNIT_READY:
/* issue SCSI command TestUnitReady */
ready_status = usbh_msc_test_unitready(uhost, msc->cur_lun);
if(USBH_OK == ready_status) {
if(USBH_OK != msc->unit[msc->cur_lun].prev_ready_state) {
msc->unit[msc->cur_lun].state_changed = 1U;
} else {
msc->unit[msc->cur_lun].state_changed = 0U;
}
msc->unit[msc->cur_lun].state = MSC_READ_CAPACITY10;
msc->unit[msc->cur_lun].error = MSC_OK;
msc->unit[msc->cur_lun].prev_ready_state = USBH_OK;
} else if(USBH_FAIL == ready_status) {
if(USBH_FAIL != msc->unit[msc->cur_lun].prev_ready_state) {
msc->unit[msc->cur_lun].state_changed = 1U;
} else {
msc->unit[msc->cur_lun].state_changed = 0U;
}
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
msc->unit[msc->cur_lun].error = MSC_NOT_READY;
msc->unit[msc->cur_lun].prev_ready_state = USBH_FAIL;
} else {
if(USBH_UNRECOVERED_ERROR == ready_status) {
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_ERROR;
}
}
break;
case MSC_READ_CAPACITY10:
/* issue READ_CAPACITY10 SCSI command */
scsi_status = usbh_msc_read_capacity10(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].capacity);
if(USBH_OK == scsi_status) {
if(1U == msc->unit[msc->cur_lun].state_changed) {
}
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_OK;
msc->cur_lun ++;
} else if(USBH_FAIL == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_REQUEST_SENSE;
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_ERROR;
}
}
break;
case MSC_REQUEST_SENSE:
/* issue RequestSense SCSI command for retrieving error code */
scsi_status = usbh_msc_request_sense(uhost, msc->cur_lun, &msc->unit[msc->cur_lun].sense);
if(USBH_OK == scsi_status) {
if((UNIT_ATTENTION == msc->unit[msc->cur_lun].sense.SenseKey) || (NOT_READY == msc->unit[msc->cur_lun].sense.SenseKey)) {
if((uhost->control.timer - msc->timer) < 10000U) {
msc->unit[msc->cur_lun].state = MSC_TEST_UNIT_READY;
break;
}
}
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->cur_lun++;
} else if(USBH_FAIL == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_UNRECOVERED_ERROR;
} else {
if(MSC_UNRECOVERED_ERROR == scsi_status) {
msc->unit[msc->cur_lun].state = MSC_IDLE;
msc->unit[msc->cur_lun].error = MSC_ERROR;
}
}
break;
case MSC_UNRECOVERED_ERROR:
msc->cur_lun++;
break;
default:
break;
}
} else {
msc->cur_lun = 0U;
msc->state = MSC_IDLE;
}
break;
case MSC_IDLE:
uhost->usr_cb->dev_user_app();
status = USBH_OK;
break;
default:
break;
}
return status;
}
/*!
\brief get max lun of the mass storage device
\param[in] uhost: pointer to USB host
\param[in] maxlun: pointer to max lun
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_maxlun_get(usbh_host *uhost, uint8_t *maxlun)
{
usbh_status status = USBH_BUSY;
if(CTL_IDLE == uhost->control.ctl_state) {
uhost->control.setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_REQTYPE_CLASS | USB_RECPTYPE_ITF,
.bRequest = BBB_GET_MAX_LUN,
.wValue = 0U,
.wIndex = 0U,
.wLength = 1U
};
usbh_ctlstate_config(uhost, maxlun, 1U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief get max lun of the mass storage device
\param[in] uhost: pointer to USB host
\param[in] lun: logic unit number
\param[out] none
\retval operation status
*/
static usbh_status usbh_msc_rdwr_process(usbh_host *uhost, uint8_t lun)
{
usbh_status error = USBH_BUSY;
usbh_status scsi_status = USBH_BUSY;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
/* switch msc req state machine */
switch(msc->unit[lun].state) {
case MSC_READ:
scsi_status = usbh_msc_read10(uhost, lun, NULL, 0U, 0U);
if(USBH_OK == scsi_status) {
msc->unit[lun].state = MSC_IDLE;
error = USBH_OK;
} else if(USBH_FAIL == scsi_status) {
msc->unit[lun].state = MSC_REQUEST_SENSE;
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
error = USBH_FAIL;
}
}
break;
case MSC_WRITE:
scsi_status = usbh_msc_write10(uhost, lun, NULL, 0U, 0U);
if(USBH_OK == scsi_status) {
msc->unit[lun].state = MSC_IDLE;
error = USBH_OK;
} else if(USBH_FAIL == scsi_status) {
msc->unit[lun].state = MSC_REQUEST_SENSE;
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
error = USBH_FAIL;
}
}
break;
case MSC_REQUEST_SENSE:
scsi_status = usbh_msc_request_sense(uhost, lun, &msc->unit[lun].sense);
if(USBH_OK == scsi_status) {
msc->unit[lun].state = MSC_IDLE;
msc->unit[lun].error = MSC_ERROR;
error = USBH_FAIL;
}
if(USBH_FAIL == scsi_status) {
} else {
if(USBH_UNRECOVERED_ERROR == scsi_status) {
msc->unit[lun].state = MSC_UNRECOVERED_ERROR;
error = USBH_FAIL;
}
}
break;
default:
break;
}
return error;
}
@@ -0,0 +1,232 @@
/*!
\file usbh_msc_fatfs.c
\brief USB MSC host FATFS related functions
\version 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 "diskio.h"
#include "usbh_msc_core.h"
static volatile DSTATUS state = STA_NOINIT; /* disk status */
extern usbh_host usb_host_msc;
/*!
\brief initialize the disk drive
\param[in] drv: physical drive number (0)
\param[out] none
\retval operation status
*/
DSTATUS disk_initialize(BYTE drv)
{
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
if(udev->host.connect_status) {
state &= ~STA_NOINIT;
}
return state;
}
/*!
\brief get disk status
\param[in] drv: physical drive number (0)
\param[out] none
\retval operation status
*/
DSTATUS disk_status(BYTE drv)
{
if(drv) {
return STA_NOINIT; /* supports only single drive */
}
return state;
}
/*!
\brief read sectors
\param[in] drv: physical drive number (0)
\param[in] buff: pointer to the data buffer to store read data
\param[in] sector: start sector number (LBA)
\param[in] count: sector count (1..255)
\param[out] none
\retval operation status
*/
DRESULT disk_read(BYTE drv, BYTE *buff, DWORD sector, UINT count)
{
BYTE status = USBH_OK;
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
if(drv || (!count)) {
return RES_PARERR;
}
if(state & STA_NOINIT) {
return RES_NOTRDY;
}
if(udev->host.connect_status) {
do {
status = usbh_msc_read(&usb_host_msc, drv, sector, buff, count);
if(!udev->host.connect_status) {
return RES_ERROR;
}
} while(USBH_BUSY == status);
}
if(USBH_OK == status) {
return RES_OK;
}
return RES_ERROR;
}
#if _READONLY == 0U
/*!
\brief write sectors
\param[in] drv: physical drive number (0)
\param[in] buff: pointer to the data buffer to store read data
\param[in] sector: start sector number (LBA)
\param[in] count: sector count (1..255)
\param[out] none
\retval operation status
*/
DRESULT disk_write(BYTE drv, const BYTE *buff, DWORD sector, UINT count)
{
BYTE status = USBH_OK;
usb_core_driver *udev = (usb_core_driver *)usb_host_msc.data;
if((!count) || drv) {
return RES_PARERR;
}
if(state & STA_NOINIT) {
return RES_NOTRDY;
}
if(state & STA_PROTECT) {
return RES_WRPRT;
}
if(udev->host.connect_status) {
do {
status = usbh_msc_write(&usb_host_msc, drv, sector, (BYTE *)buff, count);
if(!udev->host.connect_status) {
return RES_ERROR;
}
} while(USBH_BUSY == status);
}
if(USBH_OK == status) {
return RES_OK;
}
return RES_ERROR;
}
#endif /* _READONLY == 0 */
/*!
\brief I/O control function
\param[in] drv: physical drive number (0)
\param[in] ctrl: control code
\param[in] buff: pointer to the data buffer to store read data
\param[out] none
\retval operation status
*/
DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void *buff)
{
DRESULT res = RES_OK;
msc_lun info;
if(drv) {
return RES_PARERR;
}
res = RES_ERROR;
if(state & STA_NOINIT) {
return RES_NOTRDY;
}
switch(ctrl) {
/* make sure that no pending write process */
case CTRL_SYNC:
res = RES_OK;
break;
/* get number of sectors on the disk (dword) */
case GET_SECTOR_COUNT:
if(USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, drv, &info)) {
*(DWORD *)buff = (DWORD)info.capacity.block_nbr;
res = RES_OK;
}
break;
/* get r/w sector size (word) */
case GET_SECTOR_SIZE:
if(USBH_OK == usbh_msc_lun_info_get(&usb_host_msc, drv, &info)) {
*(WORD *)buff = (DWORD)info.capacity.block_size;
res = RES_OK;
}
break;
/* get erase block size in unit of sector (dword) */
case GET_BLOCK_SIZE:
*(DWORD *)buff = 512U;
break;
default:
res = RES_PARERR;
break;
}
return res;
}
/*!
\brief get fat time
\param[in] none
\param[out] none
\retval time value
*/
DWORD get_fattime(void)
{
return ((DWORD)(2019U - 1980U) << 25) /* year 2019 */
| ((DWORD)1U << 21) /* month 1 */
| ((DWORD)1U << 16) /* day 1 */
| ((DWORD)0U << 11) /* hour 0 */
| ((DWORD)0U << 5) /* min 0 */
| ((DWORD)0U >> 1); /* sec 0 */
}
@@ -0,0 +1,396 @@
/*!
\file usbh_msc_scsi.c
\brief USB MSC SCSI commands implementation
\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 "usbh_msc_core.h"
#include "usbh_msc_scsi.h"
/*!
\brief send 'Inquiry' command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] inquiry: pointer to the inquiry structure
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_scsi_inquiry(usbh_host *uhost, uint8_t lun, scsi_std_inquiry_data *inquiry)
{
usbh_status error = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the cbw and relevant field*/
msc->bbb.cbw.field.dCBWDataTransferLength = STANDARD_INQUIRY_DATA_LEN;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_INQUIRY;
msc->bbb.cbw.field.CBWCB[1] = (lun << 5);
msc->bbb.cbw.field.CBWCB[4] = 0x24U;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
error = USBH_BUSY;
break;
case BBB_CMD_WAIT:
error = usbh_msc_bbb_process(uhost, lun);
if(USBH_OK == error) {
memset(inquiry, 0U, sizeof(scsi_std_inquiry_data));
/* assign inquiry data */
inquiry->device_type = msc->bbb.pbuf[0] & 0x1FU;
inquiry->peripheral_qualifier = msc->bbb.pbuf[0] >> 5;
if(0x80U == ((uint32_t)msc->bbb.pbuf[1] & 0x80U)) {
inquiry->removable_media = 1U;
} else {
inquiry->removable_media = 0U;
}
memcpy(inquiry->vendor_id, &msc->bbb.pbuf[8], 8U);
memcpy(inquiry->product_id, &msc->bbb.pbuf[16], 16U);
memcpy(inquiry->revision_id, &msc->bbb.pbuf[32], 4U);
}
break;
default:
break;
}
return error;
}
/*!
\brief send 'Test unit ready' command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_test_unitready(usbh_host *uhost, uint8_t lun)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = CBW_LENGTH_TEST_UNIT_READY;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_OUT;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_TEST_UNIT_READY;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
break;
default:
break;
}
return status;
}
/*!
\brief send the read capacity command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] capacity: pointer to SCSI capacity
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_read_capacity10(usbh_host *uhost, uint8_t lun, scsi_capacity *capacity)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = READ_CAPACITY10_DATA_LEN;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_READ_CAPACITY10;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
if(USBH_OK == status) {
capacity->block_nbr = msc->bbb.pbuf[3] | \
((uint32_t)msc->bbb.pbuf[2] << 8) | \
((uint32_t)msc->bbb.pbuf[1] << 16) | \
((uint32_t)msc->bbb.pbuf[0] << 24);
capacity->block_size = (uint16_t)(msc->bbb.pbuf[7] | ((uint32_t)msc->bbb.pbuf[6] << 8));
}
break;
default:
break;
}
return status;
}
/*!
\brief send the mode sense6 command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_mode_sense6(usbh_host *uhost, uint8_t lun)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = XFER_LEN_MODE_SENSE6;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_MODE_SENSE6;
msc->bbb.cbw.field.CBWCB[2] = MODE_SENSE_PAGE_CONTROL_FIELD | MODE_SENSE_PAGE_CODE;
msc->bbb.cbw.field.CBWCB[4] = XFER_LEN_MODE_SENSE6;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
if(USBH_OK == status) {
if(msc->bbb.data[2] & MASK_MODE_SENSE_WRITE_PROTECT) {
} else {
}
}
break;
default:
break;
}
return status;
}
/*!
\brief send the Request Sense command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] sense_data: pointer to sense data
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_request_sense(usbh_host *uhost, uint8_t lun, msc_scsi_sense *sense_data)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = ALLOCATION_LENGTH_REQUEST_SENSE;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_REQUEST_SENSE;
msc->bbb.cbw.field.CBWCB[1] = (lun << 5);
msc->bbb.cbw.field.CBWCB[4] = ALLOCATION_LENGTH_REQUEST_SENSE;
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = (uint8_t *)(void *)msc->bbb.data;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
if(USBH_OK == status) {
/* get sense data */
sense_data->SenseKey = msc->bbb.pbuf[2] & 0x0FU;
sense_data->ASC = msc->bbb.pbuf[12];
sense_data->ASCQ = msc->bbb.pbuf[13];
}
break;
default:
break;
}
return status;
}
/*!
\brief send the write10 command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] data_buf: data buffer contains the data to write
\param[in] addr: address to which the data will be written
\param[in] sector_num: number of sector to be written
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_write10(usbh_host *uhost, uint8_t lun, uint8_t *data_buf, uint32_t addr, uint32_t sector_num)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
msc->bbb.cbw.field.dCBWDataTransferLength = sector_num * msc->unit[lun].capacity.block_size;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_OUT;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_WRITE10;
/* logical block address */
msc->bbb.cbw.field.CBWCB[2] = (((uint8_t *)&addr)[3]);
msc->bbb.cbw.field.CBWCB[3] = (((uint8_t *)&addr)[2]);
msc->bbb.cbw.field.CBWCB[4] = (((uint8_t *)&addr)[1]);
msc->bbb.cbw.field.CBWCB[5] = (((uint8_t *)&addr)[0]);
/* transfer length */
msc->bbb.cbw.field.CBWCB[7] = (((uint8_t *)&sector_num)[1]);
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)&sector_num)[0]);
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = data_buf;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
break;
default:
break;
}
return status;
}
/*!
\brief send the read10 command to the device
\param[in] uhost: pointer to USB host handler
\param[in] lun: logic unit number
\param[in] data_buf: data buffer contains the data to write
\param[in] addr: address to which the data will be read
\param[in] sector_num: number of sector to be read
\param[out] none
\retval operation status
*/
usbh_status usbh_msc_read10(usbh_host *uhost, uint8_t lun, uint8_t *data_buf, uint32_t addr, uint32_t sector_num)
{
usbh_status status = USBH_FAIL;
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
switch(msc->bbb.cmd_state) {
case BBB_CMD_SEND:
/* prepare the CBW and relevant field */
msc->bbb.cbw.field.dCBWDataTransferLength = sector_num * msc->unit[lun].capacity.block_size;
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
msc->bbb.cbw.field.CBWCB[0] = SCSI_READ10;
/* logical block address */
msc->bbb.cbw.field.CBWCB[2] = (((uint8_t *)&addr)[3]);
msc->bbb.cbw.field.CBWCB[3] = (((uint8_t *)&addr)[2]);
msc->bbb.cbw.field.CBWCB[4] = (((uint8_t *)&addr)[1]);
msc->bbb.cbw.field.CBWCB[5] = (((uint8_t *)&addr)[0]);
/* transfer length */
msc->bbb.cbw.field.CBWCB[7] = (((uint8_t *)&sector_num)[1]);
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)&sector_num)[0]);
msc->bbb.state = BBB_SEND_CBW;
msc->bbb.cmd_state = BBB_CMD_WAIT;
msc->bbb.pbuf = data_buf;
status = USBH_BUSY;
break;
case BBB_CMD_WAIT:
status = usbh_msc_bbb_process(uhost, lun);
break;
default:
break;
}
return status;
}
@@ -0,0 +1,262 @@
/*!
\file usbh_core.h
\brief USB host core state machine 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 USBH_CORE_H
#define USBH_CORE_H
#include "usbh_conf.h"
#include "drv_usb_host.h"
#define MSC_CLASS 0x08U /*!< USB MSC class */
#define HID_CLASS 0x03U /*!< USB HID class */
#define MSC_PROTOCOL 0x50U /*!< USB MSC protocol */
#define CBI_PROTOCOL 0x01U /*!< USB CBI protocol */
#define USBH_MAX_ERROR_COUNT 3U /*!< USBH maximum error count */
#define USBH_DEV_ADDR_DEFAULT 0U /*!< USBH device address default */
#define USBH_DEV_ADDR 1U /*!< USBH device address */
typedef enum {
USBH_OK = 0U, /*!< USB host OK status */
USBH_BUSY, /*!< USB host busy status */
USBH_FAIL, /*!< USB host fail status */
USBH_NOT_SUPPORTED, /*!< USB host not support status */
USBH_UNRECOVERED_ERROR, /*!< USB host unrecovered error status */
USBH_SPEED_UNKNOWN_ERROR, /*!< USB host speed unknown error status */
USBH_APPLY_DEINIT /*!< USB host apply deinit status */
} usbh_status;
/* USB host global operation state */
typedef enum {
HOST_DEFAULT = 0U, /*!< USB host global operation default state */
HOST_DETECT_DEV_SPEED, /*!< USB host global operation detect device speed state */
HOST_DEV_CONNECT, /*!< USB host global operation device connect state */
HOST_DEV_DETACHED, /*!< USB host global operation device detached state */
HOST_DEV_ENUM, /*!< USB host global operation device enumeration state */
HOST_PWR_FEATURE_SET, /*!< USB host global operation setting power feature state */
HOST_CLASS_CHECK, /*!< USB host global operation class check state */
HOST_CLASS_ENUM, /*!< USB host global operation class enumeration state */
HOST_CLASS_HANDLER, /*!< USB host global operation class handler state */
HOST_USER_INPUT, /*!< USB host global operation user input state */
HOST_SUSPEND, /*!< USB host global operation suspend state */
HOST_WAKEUP, /*!< USB host global operation wakeup state */
HOST_ERROR /*!< USB host global operation error state */
} usb_host_state;
/* USB host enumeration state */
typedef enum {
ENUM_DEFAULT = 0U, /*!< USB host enumeration default state */
ENUM_GET_DEV_DESC, /*!< USB host enumeration get device descriptor state */
ENUM_SET_ADDR, /*!< USB host enumeration set address state */
ENUM_GET_CFG_DESC, /*!< USB host enumeration get configuration descriptor state */
ENUM_GET_CFG_DESC_SET, /*!< USB host enumeration set configuration descriptor state */
ENUM_GET_STR_DESC, /*!< USB host enumeration get string descriptor state */
#ifdef USB_MTP
ENUM_GET_MTP_STR, /*!< USB host enumeration get MTP string state */
#endif /* USB_MTP */
ENUM_SET_CONFIGURATION, /*!< USB host enumeration set configuration state */
ENUM_DEV_CONFIGURED /*!< USB host enumeration device configured state */
} usbh_enum_state;
/* USB host control transfer state */
typedef enum {
CTL_IDLE = 0U, /*!< USB host control transfer idle state */
CTL_SETUP, /*!< USB host control transfer SETUP state */
CTL_SETUP_WAIT, /*!< USB host control transfer SETUP wait state */
CTL_DATA_IN, /*!< USB host control transfer data IN state */
CTL_DATA_IN_WAIT, /*!< USB host control transfer data IN wait state */
CTL_DATA_OUT, /*!< USB host control transfer data OUT state */
CTL_DATA_OUT_WAIT, /*!< USB host control transfer data OUT wait state */
CTL_STATUS_IN, /*!< USB host control transfer status IN state */
CTL_STATUS_IN_WAIT, /*!< USB host control transfer status IN wait state */
CTL_STATUS_OUT, /*!< USB host control transfer status OUT state */
CTL_STATUS_OUT_WAIT, /*!< USB host control transfer status OUT wait state */
CTL_ERROR, /*!< USB host control transfer error state */
CTL_FINISH /*!< USB host control transfer finish state */
} usbh_ctl_state;
/* user action state */
typedef enum {
USR_IN_NO_RESP = 0U, /*!< user action IN no response state */
USR_IN_RESP_OK = 1U /*!< user action IN response OK state */
} usbh_user_status;
/* USB host wakeup mode */
typedef enum {
NORMAL_WORK = 0U,
GENERAL_WAKEUP = 1U,
REMOTE_WAKEUP = 2
} usbh_wakeup_mode;
/* control transfer information */
typedef struct _usbh_control {
uint8_t pipe_in_num; /*!< the number of IN pipe*/
uint8_t pipe_out_num; /*!< the number of OUT pipe */
uint8_t max_len; /*!< maximum length of control transfer */
uint8_t error_count; /*!< the count of control error */
uint8_t *buf; /*!< control transfer buffer */
uint16_t ctl_len; /*!< the length of control transfer */
__IO uint32_t timer; /*!< control transfer timer */
usb_setup setup; /*!< control transfer SETUP packet */
usbh_ctl_state ctl_state; /*!< host control transfer state */
} usbh_control;
/* USB interface descriptor set */
typedef struct _usb_desc_itf_set {
usb_desc_itf itf_desc; /*!< USB interface descriptor */
usb_desc_ep ep_desc[USBH_MAX_EP_NUM]; /*!< USB endpoint descriptor */
} usb_desc_itf_set;
/* USB configure descriptor set */
typedef struct _usb_desc_cfg_set {
usb_desc_config cfg_desc; /*!< USB configuration descriptor */
usb_desc_itf_set itf_desc_set[USBH_MAX_INTERFACES_NUM][USBH_MAX_ALT_SETTING]; /*!< USB interface descriptor set*/
} usb_desc_cfg_set;
/* USB device property */
typedef struct {
uint8_t data[USBH_DATA_BUF_MAX_LEN]; /*!< if DMA is used, the data array must be located in the first position */
uint8_t cur_itf; /*!< USB device current interface */
uint8_t addr; /*!< USB device address */
uint32_t speed; /*!< USB device speed */
usb_desc_dev dev_desc; /*!< USB device descriptor */
usb_desc_cfg_set cfg_desc_set; /*!< USB interface descriptor set */
#if (1U == USBH_CFG_DESC_KEEP)
uint8_t cfgdesc_rawdata[USBH_CFGSET_MAX_LEN]; /*!< USB configuration descriptor raw data*/
#endif /* (1U == USBH_CFG_DESC_KEEP) */
} usb_dev_prop;
struct _usbh_host;
/* device class callbacks */
typedef struct {
uint8_t class_code; /*!< USB class type */
usbh_status (*class_init)(struct _usbh_host *phost);
void (*class_deinit)(struct _usbh_host *phost);
usbh_status (*class_requests)(struct _usbh_host *phost);
usbh_status (*class_machine)(struct _usbh_host *phost);
usbh_status (*class_sof)(struct _usbh_host *uhost);
void *class_data; /*!< USB class data pointer */
} usbh_class;
/* user callbacks */
typedef struct {
void (*dev_init)(void);
void (*dev_deinit)(void);
void (*dev_attach)(void);
void (*dev_reset)(void);
void (*dev_detach)(void);
void (*dev_over_currented)(void);
void (*dev_speed_detected)(uint32_t dev_speed);
void (*dev_devdesc_assigned)(void *dev_desc);
void (*dev_address_set)(void);
void (*dev_cfgdesc_assigned)(usb_desc_config *cfg_desc, \
usb_desc_itf *itf_desc, \
usb_desc_ep *ep_desc);
void (*dev_mfc_str)(void *mfc_str);
void (*dev_prod_str)(void *prod_str);
void (*dev_seral_str)(void *serial_str);
void (*dev_enumerated)(void);
usbh_user_status (*dev_user_input)(void);
int (*dev_user_app)(void);
void (*dev_not_supported)(void);
void (*dev_error)(void);
} usbh_user_cb;
/* host information */
typedef struct _usbh_host {
usb_host_state cur_state; /*!< host state machine value */
usb_host_state backup_state; /*!< backup of previous state machine value */
usbh_enum_state enum_state; /*!< enumeration state machine */
usbh_control control; /*!< USB host control state machine */
usb_dev_prop dev_prop; /*!< USB device property */
usbh_class *uclass[USBH_MAX_SUPPORTED_CLASS]; /*!< USB host supported class */
usbh_class *active_class; /*!< USB active class */
usbh_user_cb *usr_cb; /*!< USB user callback */
uint8_t class_num; /*!< USB class number */
void *data; /*!< used for... */
uint8_t suspend_flag; /*!< host suspend flag */
uint8_t dev_supp_remote_wkup; /*!< record device remote wakeup function */
usbh_wakeup_mode wakeup_mode; /*!< record wakeup mode */
} usbh_host;
/*!
\brief get USB URB state
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval URB state
*/
static inline usb_urb_state usbh_urbstate_get(usb_core_driver *udev, uint8_t pp_num)
{
return udev->host.pipe[pp_num].urb_state;
}
/*!
\brief get USB transfer data count
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval transfer data count
*/
static inline uint32_t usbh_xfercount_get(usb_core_driver *udev, uint8_t pp_num)
{
return udev->host.backup_xfercount[pp_num];
}
/* function declarations */
/* USB host stack initializations */
void usbh_init(usbh_host *uhost, usb_core_driver *udev, usb_core_enum usb_core, usbh_user_cb *user_cb);
/* USB host register device class */
usbh_status usbh_class_register(usbh_host *uhost, usbh_class *puclass);
/* de-initialize USB host */
usbh_status usbh_deinit(usbh_host *uhost);
/* USB host core main state machine process */
void usbh_core_task(usbh_host *uhost);
/* handle the error on USB host side */
void usbh_error_handler(usbh_host *uhost, usbh_status err_type);
#endif /* USBH_CORE_H */
@@ -0,0 +1,70 @@
/*!
\file usbh_enum.h
\brief USB host mode USB enumeration 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 USBH_ENUM_H
#define USBH_ENUM_H
#include "usbh_core.h"
/* function declarations */
/* configure USB control status parameters */
void usbh_ctlstate_config(usbh_host *uhost, uint8_t *buf, uint16_t len);
/* get device descriptor from the USB device */
usbh_status usbh_devdesc_get(usbh_host *uhost, uint8_t len);
/* get configuration descriptor from the USB device */
usbh_status usbh_cfgdesc_get(usbh_host *uhost, uint16_t len);
/* get string descriptor from the USB device */
usbh_status usbh_strdesc_get(usbh_host *uhost,uint8_t str_index, uint8_t *buf, uint16_t len);
/* set the address to the connected device */
usbh_status usbh_setaddress(usbh_host *uhost, uint8_t dev_addr);
/* set the configuration value to the connected device */
usbh_status usbh_setcfg(usbh_host *uhost, uint16_t config);
/* set the interface value to the connected device */
usbh_status usbh_setinterface(usbh_host *uhost, uint8_t itf_num, uint8_t alter_setting);
/* set or enable a specific device feature */
usbh_status usbh_setdevfeature(usbh_host *uhost, uint8_t feature_selector, uint16_t windex);
/* clear or disable a specific device feature */
usbh_status usbh_clrdevfeature(usbh_host *uhost, uint8_t feature_selector, uint16_t windex);
/* clear or disable a specific feature */
usbh_status usbh_clrfeature(usbh_host *uhost, uint8_t ep_addr, uint8_t pp_num);
/* get the next descriptor header */
usb_desc_header *usbh_nextdesc_get(uint8_t *pbuf, uint16_t *ptr);
/* select an interface */
usbh_status usbh_interface_select(usb_dev_prop *udev, uint8_t interface);
/* find the interface index for a specific class */
uint8_t usbh_interface_find(usb_dev_prop *udev, uint8_t main_class, uint8_t sub_class, uint8_t protocol);
/* find the interface index for a specific class interface and alternate setting number */
uint8_t usbh_interfaceindex_find(usb_dev_prop *udev, uint8_t interface_number, uint8_t alt_settings);
#endif /* USBH_ENUM_H */
@@ -0,0 +1,102 @@
/*!
\file usbh_pipe.h
\brief USB host mode pipe 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 USBH_PIPE_H
#define USBH_PIPE_H
#include "usbh_core.h"
/* host pipe maximum */
#define HP_MAX 8U
/* host pipe status */
#define HP_OK 0x0000U /*!< host pipe status */
#define HP_USED 0x8000U /*!< host pipe used status */
#define HP_ERROR 0xFFFFU /*!< host pipe error status */
#define HP_USED_MASK 0x7FFFU /*!< host pipe used mask */
/*!
\brief set toggle for a pipe
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[in] toggle: toggle (0/1)
\param[out] none
\retval none
*/
__STATIC_INLINE void usbh_pipe_toggle_set(usb_core_driver *udev, uint8_t pp_num, uint8_t toggle)
{
if(udev->host.pipe[pp_num].ep.dir) {
udev->host.pipe[pp_num].data_toggle_in = toggle;
} else {
udev->host.pipe[pp_num].data_toggle_out = toggle;
}
}
/*!
\brief get toggle flag of pipe
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval toggle flag
*/
__STATIC_INLINE uint8_t usbh_pipe_toggle_get(usb_core_driver *udev, uint8_t pp_num)
{
if(udev->host.pipe[pp_num].ep.dir) {
return udev->host.pipe[pp_num].data_toggle_in;
} else {
return udev->host.pipe[pp_num].data_toggle_out;
}
}
/* function declarations */
/* create a pipe */
uint8_t usbh_pipe_create(usb_core_driver *udev, \
usb_dev_prop *dev, \
uint8_t pp_num, \
uint8_t ep_type, \
uint16_t ep_mpl);
/* modify a pipe */
uint8_t usbh_pipe_update(usb_core_driver *udev, \
uint8_t pp_num, \
uint8_t dev_addr, \
uint32_t dev_speed, \
uint16_t ep_mpl);
/* allocate a new pipe */
uint8_t usbh_pipe_allocate(usb_core_driver *udev, uint8_t ep_addr);
/* free a pipe */
uint8_t usbh_pipe_free(usb_core_driver *udev, uint8_t pp_num);
/* delete all USB host pipe */
uint8_t usbh_pipe_delete(usb_core_driver *udev);
#endif /* USBH_PIPE_H */
@@ -0,0 +1,50 @@
/*!
\file usbh_transc.h
\brief USB host mode transactions 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 USBH_TRANSC_H
#define USBH_TRANSC_H
#include "usbh_core.h"
/* function declarations */
/* send the SETUP packet to the USB device */
usbh_status usbh_ctlsetup_send(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num);
/* send a data packet to the USB device */
usbh_status usbh_data_send(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num, uint16_t len);
/* receive a data packet from the USB device */
usbh_status usbh_data_recev(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num, uint16_t len);
/* USB control transfer handler */
usbh_status usbh_ctl_handler(usbh_host *uhost);
#endif /* USBH_TRANSC_H */
@@ -0,0 +1,659 @@
/*!
\file usbh_core.c
\brief USB host core state machine 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 "drv_usb_hw.h"
#include "usbh_pipe.h"
#include "usbh_enum.h"
#include "usbh_core.h"
#include "drv_usbh_int.h"
/* local function prototypes ('static') */
static uint8_t usb_ev_sof(usbh_host *uhost);
static uint8_t usb_ev_connect(usbh_host *uhost);
static uint8_t usb_ev_disconnect(usbh_host *uhost);
static usbh_status usbh_enum_task(usbh_host *uhost);
#if USBFS_LOW_POWER || USBHS_LOW_POWER
static void usb_hwp_suspend(usb_core_driver *udev);
static void usb_hwp_resume(usb_core_driver *udev);
#endif /* USBFS_LOW_POWER || USBHS_LOW_POWER */
usbh_ev_cb usbh_int_op = {
usb_ev_connect,
usb_ev_disconnect,
usb_ev_sof
};
usbh_ev_cb *usbh_int_fop = &usbh_int_op;
/*!
\brief USB host stack initializations
\param[in] uhost: pointer to USB host
\param[in] udev: pointer to USB device instance
\param[in] usb_core: USB core type
\param[in] user_cb: pointer to user callback
\param[out] none
\retval none
*/
void usbh_init(usbh_host *uhost, usb_core_driver *udev, usb_core_enum usb_core, usbh_user_cb *user_cb)
{
/* link driver to the stack */
udev->host.data = (void *)uhost;
uhost->data = (void *)udev;
/* host deinitialization */
usbh_deinit(uhost);
uhost->usr_cb = user_cb;
udev->host.connect_status = 0U;
for(uint8_t i = 0U; i < USBFS_MAX_TX_FIFOS; i++) {
udev->host.pipe[i].err_count = 0U;
udev->host.pipe[i].pp_status = PIPE_IDLE;
udev->host.backup_xfercount[i] = 0U;
}
udev->host.pipe[0].ep.mps = 8U;
usb_basic_init(&udev->bp, &udev->regs, usb_core);
#ifndef DUAL_ROLE_MODE_ENABLED
usb_globalint_disable(&udev->regs);
usb_core_init(udev->bp, &udev->regs);
#ifndef USE_OTG_MODE
usb_curmode_set(&udev->regs, HOST_MODE);
#endif /* USE_OTG_MODE */
usb_host_init(udev);
usb_globalint_enable(&udev->regs);
#endif /* DUAL_ROLE_MODE_ENABLED */
/* upon initialize call usr call back */
uhost->usr_cb->dev_init();
}
/*!
\brief USB host register device class
\param[in] uhost: pointer to USB host instance
\param[in] uclass: pointer to USB device class
\param[out] none
\retval operation status
*/
usbh_status usbh_class_register(usbh_host *uhost, usbh_class *uclass)
{
usbh_status status = USBH_OK;
if(NULL != uclass) {
if(uhost->class_num < USBH_MAX_SUPPORTED_CLASS) {
uhost->uclass[uhost->class_num++] = uclass;
} else {
status = USBH_FAIL;
}
} else {
status = USBH_FAIL;
}
return status;
}
/*!
\brief deinitialize USB host
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
usbh_status usbh_deinit(usbh_host *uhost)
{
usb_core_driver *udev = (usb_core_driver *)uhost->data;
/* software initialize */
uhost->cur_state = HOST_DEFAULT;
uhost->backup_state = HOST_DEFAULT;
uhost->enum_state = ENUM_DEFAULT;
uhost->control.ctl_state = CTL_IDLE;
uhost->control.max_len = USB_FS_EP0_MAX_LEN;
uhost->dev_prop.addr = USBH_DEV_ADDR_DEFAULT;
uhost->dev_prop.speed = PORT_SPEED_FULL;
uhost->dev_prop.cur_itf = 0xFFU;
usbh_pipe_free(udev, uhost->control.pipe_in_num);
usbh_pipe_free(udev, uhost->control.pipe_out_num);
return USBH_OK;
}
/*!
\brief USB host core main state machine process
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
void usbh_core_task(usbh_host *uhost)
{
volatile usbh_status status = USBH_FAIL;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
/* check for host port events */
if(((0U == udev->host.connect_status) || (0U == udev->host.port_enabled)) && (HOST_DEFAULT != uhost->cur_state)) {
if(HOST_DEV_DETACHED != uhost->cur_state) {
uhost->cur_state = HOST_DEV_DETACHED;
}
}
switch(uhost->cur_state) {
case HOST_DEFAULT:
if(udev->host.connect_status) {
uhost->cur_state = HOST_DETECT_DEV_SPEED;
usb_mdelay(100U);
usb_port_reset(udev);
uhost->usr_cb->dev_reset();
}
break;
case HOST_DETECT_DEV_SPEED:
if(udev->host.port_enabled) {
uhost->cur_state = HOST_DEV_CONNECT;
uhost->dev_prop.speed = usb_curspeed_get(udev);
uhost->usr_cb->dev_speed_detected(uhost->dev_prop.speed);
usb_mdelay(50U);
}
break;
case HOST_DEV_CONNECT:
uhost->usr_cb->dev_attach();
uhost->control.pipe_out_num = usbh_pipe_allocate(udev, 0x00U);
uhost->control.pipe_in_num = usbh_pipe_allocate(udev, 0x80U);
/* open IN control pipe */
usbh_pipe_create(udev, \
&uhost->dev_prop, \
uhost->control.pipe_in_num, \
USB_EPTYPE_CTRL, \
(uint16_t)uhost->control.max_len);
/* open OUT control pipe */
usbh_pipe_create(udev, \
&uhost->dev_prop, \
uhost->control.pipe_out_num, \
USB_EPTYPE_CTRL, \
(uint16_t)uhost->control.max_len);
uhost->cur_state = HOST_DEV_ENUM;
break;
case HOST_DEV_ENUM:
/* check for enumeration status */
if(USBH_OK == usbh_enum_task(uhost)) {
/* the function shall return USBH_OK when full enumeration is complete */
/* user callback for end of device basic enumeration */
uhost->usr_cb->dev_enumerated();
#if USBFS_LOW_POWER || USBHS_LOW_POWER
uhost->cur_state = HOST_SUSPEND;
/* judge device remote wakeup function */
if((uhost->dev_prop.cfg_desc_set.cfg_desc.bmAttributes) & (1U << 5)) {
uhost->dev_supp_remote_wkup = 1U;
} else {
uhost->dev_supp_remote_wkup = 0U;
}
#else
uhost->cur_state = HOST_PWR_FEATURE_SET;
#endif /* USBFS_LOW_POWER || USBHS_LOW_POWER */
}
break;
case HOST_PWR_FEATURE_SET:
if((uhost->dev_prop.cfg_desc_set.cfg_desc.bmAttributes) & (1U << 5)) {
if(USBH_OK == usbh_setdevfeature(uhost, FEATURE_SELECTOR_REMOTEWAKEUP, 0U)) {
uhost->cur_state = HOST_CLASS_CHECK;
}
} else {
uhost->cur_state = HOST_CLASS_CHECK;
}
break;
case HOST_CLASS_CHECK:
if(0U == uhost->class_num) {
uhost->cur_state = HOST_ERROR;
} else {
uhost->active_class = NULL;
uint8_t itf_class = uhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].itf_desc.bInterfaceClass;
for(uint8_t index = 0U; index < uhost->class_num; index++) {
if((uhost->uclass[index]->class_code == itf_class) || (0xFFU == itf_class)) {
uhost->active_class = uhost->uclass[index];
}
}
if(NULL != uhost->active_class) {
uhost->cur_state = HOST_USER_INPUT;
} else {
uhost->cur_state = HOST_ERROR;
}
}
break;
case HOST_USER_INPUT:
/* the function should return user response true to move to class state */
if(USR_IN_RESP_OK == uhost->usr_cb->dev_user_input()) {
if((USBH_OK == uhost->active_class->class_init(uhost))) {
uhost->cur_state = HOST_CLASS_ENUM;
}
}
break;
#if USBFS_LOW_POWER || USBHS_LOW_POWER
case HOST_SUSPEND:
if(uhost->dev_supp_remote_wkup) {
/* send set feature command*/
if(USBH_OK == usbh_setdevfeature(uhost, FEATURE_SELECTOR_REMOTEWAKEUP, 0U)) {
usb_hwp_suspend(udev);
usb_mdelay(20U);
uhost->suspend_flag = 1U;
uhost->usr_cb->dev_user_input();
/* MCU enter deep-sleep*/
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_DISABLE, WFI_CMD);
uhost->cur_state = HOST_WAKEUP;
}
} else {
/* host suspend */
usb_hwp_suspend(udev);
usb_mdelay(20U);
uhost->suspend_flag = 1U;
uhost->usr_cb->dev_user_input();
/* MCU enter deep-sleep */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, PMU_LOWDRIVER_DISABLE, WFI_CMD);
uhost->cur_state = HOST_WAKEUP;
}
break;
case HOST_WAKEUP:
/* judge suspend status */
if(0U == uhost->suspend_flag) {
usb_hwp_resume(udev);
usb_mdelay(500U);
if(uhost->dev_supp_remote_wkup) {
if(USBH_OK == usbh_clrdevfeature(uhost, FEATURE_SELECTOR_DEV, 0U)) {
/* user callback for initialization */
uhost->usr_cb->dev_init();
uhost->cur_state = HOST_CLASS_CHECK;
}
} else {
uhost->cur_state = HOST_CLASS_CHECK;
}
}
break;
#endif /* USBFS_LOW_POWER || USBHS_LOW_POWER */
case HOST_CLASS_ENUM:
/* process class standard control requests state machine */
status = uhost->active_class->class_requests(uhost);
if(USBH_OK == status) {
uhost->cur_state = HOST_CLASS_HANDLER;
} else {
usbh_error_handler(uhost, status);
}
break;
case HOST_CLASS_HANDLER:
/* process class state machine */
status = uhost->active_class->class_machine(uhost);
usbh_error_handler(uhost, status);
break;
case HOST_ERROR:
/* deinitialize host for new enumeration */
usbh_deinit(uhost);
uhost->usr_cb->dev_deinit();
if(NULL != uhost->active_class) {
uhost->active_class->class_deinit(uhost);
}
break;
case HOST_DEV_DETACHED:
/* manage user disconnect operations*/
uhost->usr_cb->dev_detach();
/* re-initialize host for new enumeration */
usbh_deinit(uhost);
uhost->usr_cb->dev_deinit();
if(NULL != uhost->active_class) {
uhost->active_class->class_deinit(uhost);
}
usbh_pipe_delete(udev);
uhost->cur_state = HOST_DEFAULT;
break;
default:
break;
}
}
/*!
\brief handle the error on USB host side
\param[in] uhost: pointer to USB host
\param[in] err_type: type of error or busy/OK state
\param[out] none
\retval none
*/
void usbh_error_handler(usbh_host *uhost, usbh_status err_type)
{
/* error unrecovered or not supported device speed */
if((USBH_SPEED_UNKNOWN_ERROR == err_type) || (USBH_UNRECOVERED_ERROR == err_type)) {
uhost->usr_cb->dev_error();
uhost->cur_state = HOST_ERROR;
} else if(USBH_APPLY_DEINIT == err_type) {
uhost->cur_state = HOST_ERROR;
/* user callback for initialization */
uhost->usr_cb->dev_init();
} else {
/* no operation */
}
}
/*!
\brief USB SOF event function from the interrupt
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static uint8_t usb_ev_sof(usbh_host *uhost)
{
/* update timer variable */
uhost->control.timer++;
/* this callback could be used to implement a scheduler process */
if(NULL != uhost->active_class) {
if(NULL != uhost->active_class->class_sof) {
uhost->active_class->class_sof(uhost);
}
}
return 0U;
}
/*!
\brief USB connect event function from the interrupt
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static uint8_t usb_ev_connect(usbh_host *uhost)
{
usb_core_driver *udev = (usb_core_driver *)uhost->data;
udev->host.connect_status = 1U;
return 0U;
}
/*!
\brief USB disconnect event function from the interrupt
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
static uint8_t usb_ev_disconnect(usbh_host *uhost)
{
usb_core_driver *udev = (usb_core_driver *)uhost->data;
udev->host.connect_status = 0U;
return 0U;
}
/*!
\brief handle the USB enumeration task
\param[in] uhost: pointer to host
\param[out] none
\retval none
*/
static usbh_status usbh_enum_task(usbh_host *uhost)
{
uint8_t str_buf[512];
usbh_status status = USBH_BUSY;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
static uint8_t index_mfc_str = 0U, index_prod_str = 0U, index_serial_str = 0U;
switch(uhost->enum_state) {
case ENUM_DEFAULT:
/* get device descriptor for only 1st 8 bytes : to get ep0 max packet size */
if(USBH_OK == usbh_devdesc_get(uhost, 8U)) {
uhost->control.max_len = uhost->dev_prop.dev_desc.bMaxPacketSize0;
/* modify control channels configuration for maximum packet size */
usbh_pipe_update(udev, \
uhost->control.pipe_out_num, \
0U, 0U, \
(uint16_t)uhost->control.max_len);
usbh_pipe_update(udev, \
uhost->control.pipe_in_num, \
0U, 0U, \
(uint16_t)uhost->control.max_len);
uhost->enum_state = ENUM_GET_DEV_DESC;
}
break;
case ENUM_GET_DEV_DESC:
/* get full device descriptor */
if(USBH_OK == usbh_devdesc_get(uhost, USB_DEV_DESC_LEN)) {
uhost->usr_cb->dev_devdesc_assigned(&uhost->dev_prop.dev_desc);
index_mfc_str = uhost->dev_prop.dev_desc.iManufacturer;
index_prod_str = uhost->dev_prop.dev_desc.iProduct;
index_serial_str = uhost->dev_prop.dev_desc.iSerialNumber;
uhost->enum_state = ENUM_SET_ADDR;
}
break;
case ENUM_SET_ADDR:
/* set address */
if(USBH_OK == usbh_setaddress(uhost, USBH_DEV_ADDR)) {
usb_mdelay(2U);
uhost->dev_prop.addr = USBH_DEV_ADDR;
/* user callback for device address assigned */
uhost->usr_cb->dev_address_set();
/* modify control channels to update device address */
usbh_pipe_update(udev, \
uhost->control.pipe_in_num, \
uhost->dev_prop.addr, \
0U, 0U);
usbh_pipe_update(udev, \
uhost->control.pipe_out_num, \
uhost->dev_prop.addr, \
0U, 0U);
uhost->enum_state = ENUM_GET_CFG_DESC;
}
break;
case ENUM_GET_CFG_DESC:
/* get standard configuration descriptor */
if(USBH_OK == usbh_cfgdesc_get(uhost, USB_CFG_DESC_LEN)) {
uhost->enum_state = ENUM_GET_CFG_DESC_SET;
}
break;
case ENUM_GET_CFG_DESC_SET:
/* get full configure descriptor (config, interface, endpoints) */
if(USBH_OK == usbh_cfgdesc_get(uhost, uhost->dev_prop.cfg_desc_set.cfg_desc.wTotalLength)) {
/* user callback for configuration descriptors available */
uhost->usr_cb->dev_cfgdesc_assigned(&uhost->dev_prop.cfg_desc_set.cfg_desc, \
&uhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].itf_desc, \
&uhost->dev_prop.cfg_desc_set.itf_desc_set[0][0].ep_desc[0]);
uhost->enum_state = ENUM_GET_STR_DESC;
}
break;
case ENUM_GET_STR_DESC:
if(index_mfc_str) {
if(USBH_OK == usbh_strdesc_get(uhost, \
uhost->dev_prop.dev_desc.iManufacturer, \
str_buf, \
0xFFU)) {
/* user callback for manufacturing string */
uhost->usr_cb->dev_mfc_str(str_buf);
index_mfc_str = 0U;
}
} else {
if(index_prod_str) {
/* check that product string is available */
if(USBH_OK == usbh_strdesc_get(uhost, \
uhost->dev_prop.dev_desc.iProduct, \
str_buf, \
0xFFU)) {
uhost->usr_cb->dev_prod_str(str_buf);
index_prod_str = 0U;
}
} else {
if(index_serial_str) {
if(USBH_OK == usbh_strdesc_get(uhost, \
uhost->dev_prop.dev_desc.iSerialNumber, \
str_buf, \
0xFFU)) {
uhost->usr_cb->dev_seral_str(str_buf);
uhost->enum_state = ENUM_SET_CONFIGURATION;
index_serial_str = 0U;
}
} else {
uhost->enum_state = ENUM_SET_CONFIGURATION;
}
}
}
break;
case ENUM_SET_CONFIGURATION:
if(USBH_OK == usbh_setcfg(uhost, (uint16_t)uhost->dev_prop.cfg_desc_set.cfg_desc.bConfigurationValue)) {
uhost->enum_state = ENUM_DEV_CONFIGURED;
}
break;
case ENUM_DEV_CONFIGURED:
status = USBH_OK;
break;
default:
break;
}
return status;
}
#if USBFS_LOW_POWER || USBHS_LOW_POWER
/*!
\brief handles the USB resume from suspend mode
\param[in] udev: pointer to selected USB device
\param[out] none
\retval none
*/
static void usb_hwp_resume(usb_core_driver *udev)
{
__IO uint32_t hprt = 0U;
/* switch-on the clocks */
*udev->regs.PWRCLKCTL &= ~PWRCLKCTL_SUCLK;
*udev->regs.PWRCLKCTL &= ~PWRCLKCTL_SHCLK;
hprt = usb_port_read(udev);
hprt &= ~HPCS_PSP;
hprt |= HPCS_PREM;
*udev->regs.HPCS = hprt;
usb_mdelay(20U);
hprt &= ~HPCS_PREM;
*udev->regs.HPCS = hprt;
}
/*!
\brief handles the USB enter to suspend mode
\param[in] udev: pointer to selected USB device
\param[out] none
\retval none
*/
static void usb_hwp_suspend(usb_core_driver *udev)
{
__IO uint32_t hprt = 0U;
hprt = usb_port_read(udev);
hprt |= HPCS_PSP;
*udev->regs.HPCS = hprt;
/* switch-off the clocks */
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SUCLK;
*udev->regs.PWRCLKCTL |= PWRCLKCTL_SHCLK;
}
#endif /* USBFS_LOW_POWER || USBHS_LOW_POWER */
@@ -0,0 +1,693 @@
/*!
\file usbh_enum.c
\brief USB host mode enumeration 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 "usbh_pipe.h"
#include "usbh_transc.h"
#include "usbh_enum.h"
/* local function prototypes ('static') */
static void usbh_devdesc_parse(usb_desc_dev *dev_desc, uint8_t *buf, uint16_t len);
static void usbh_cfgdesc_parse(usb_desc_config *cfg_desc, uint8_t *buf);
static void usbh_cfgset_parse(usb_dev_prop *udev, uint8_t *buf);
static void usbh_itfdesc_parse(usb_desc_itf *itf_desc, uint8_t *buf);
static void usbh_epdesc_parse(usb_desc_ep *ep_desc, uint8_t *buf);
static void usbh_strdesc_parse(uint8_t *psrc, uint8_t *pdest, uint16_t len);
/*!
\brief configure USB control status parameters
\param[in] uhost: pointer to USB host
\param[in] buf: control transfer data buffer pointer
\param[in] len: length of the data buffer
\param[out] none
\retval none
*/
void usbh_ctlstate_config(usbh_host *uhost, uint8_t *buf, uint16_t len)
{
/* prepare the transactions */
uhost->control.buf = buf;
uhost->control.ctl_len = len;
uhost->control.ctl_state = CTL_SETUP;
}
/*!
\brief get device descriptor from the USB device
\param[in] uhost: pointer to USB host
\param[in] len: length of the descriptor
\param[out] none
\retval operation status
*/
usbh_status usbh_devdesc_get(usbh_host *uhost, uint8_t len)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_DEV),
.wIndex = 0U,
.wLength = len
};
usbh_ctlstate_config(uhost, uhost->dev_prop.data, (uint16_t)len);
}
status = usbh_ctl_handler(uhost);
if(USBH_OK == status) {
/* commands successfully sent and response received */
usbh_devdesc_parse(&uhost->dev_prop.dev_desc, uhost->dev_prop.data, (uint16_t)len);
}
return status;
}
/*!
\brief get configuration descriptor from the USB device
\param[in] uhost: pointer to USB host
\param[in] len: length of the descriptor
\param[out] none
\retval operation status
*/
usbh_status usbh_cfgdesc_get(usbh_host *uhost, uint16_t len)
{
uint8_t *pdata = NULL;
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
#if (USBH_CFG_DESC_KEEP == 1U)
pdata = uhost->dev_prop.cfgdesc_rawdata;
#else
pdata = uhost->dev_prop.data;
#endif /* (USBH_CFG_DESC_KEEP == 1U) */
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_CONFIG),
.wIndex = 0U,
.wLength = len
};
usbh_ctlstate_config(uhost, pdata, len);
}
status = usbh_ctl_handler(uhost);
if(USBH_OK == status) {
if(len <= USB_CFG_DESC_LEN) {
usbh_cfgdesc_parse(&uhost->dev_prop.cfg_desc_set.cfg_desc, pdata);
} else {
usbh_cfgset_parse(&uhost->dev_prop, pdata);
}
}
return status;
}
/*!
\brief get string descriptor from the USB device
\param[in] uhost: pointer to USB host
\param[in] str_index: index for the string descriptor
\param[in] buf: buffer pointer to the string descriptor
\param[in] len: length of the descriptor
\param[out] none
\retval operation status
*/
usbh_status usbh_strdesc_get(usbh_host *uhost, \
uint8_t str_index, \
uint8_t *buf, \
uint16_t len)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_IN | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_GET_DESCRIPTOR,
.wValue = USBH_DESC(USB_DESCTYPE_STR) | str_index,
.wIndex = 0x0409U,
.wLength = len
};
usbh_ctlstate_config(uhost, uhost->dev_prop.data, len);
}
status = usbh_ctl_handler(uhost);
if(USBH_OK == status) {
/* commands successfully sent and response received */
usbh_strdesc_parse(uhost->dev_prop.data, buf, len);
}
return status;
}
/*!
\brief set the address to the connected device
\param[in] uhost: pointer to USB host
\param[in] dev_addr: device address to assign
\param[out] none
\retval operation status
*/
usbh_status usbh_setaddress(usbh_host *uhost, uint8_t dev_addr)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_SET_ADDRESS,
.wValue = (uint16_t)dev_addr,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set the configuration value to the connected device
\param[in] uhost: pointer to USB host
\param[in] config_index: configuration value
\param[out] none
\retval operation status
*/
usbh_status usbh_setcfg(usbh_host *uhost, uint16_t config_index)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_SET_CONFIGURATION,
.wValue = config_index,
.wIndex = 0U,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set the interface value to the connected device
\param[in] uhost: pointer to USB host
\param[in] itf_num: interface number
\param[in] set: alternated setting value
\param[out] none
\retval operation status
*/
usbh_status usbh_setinterface(usbh_host *uhost, uint8_t itf_num, uint8_t set)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_ITF | USB_REQTYPE_STRD,
.bRequest = USB_SET_INTERFACE,
.wValue = set,
.wIndex = itf_num,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief set the interface value to the connected device
\param[in] uhost: pointer to USB host
\param[in] feature_selector: feature selector
\param[in] windex: index value
\param[out] none
\retval operation status
*/
usbh_status usbh_setdevfeature(usbh_host *uhost, uint8_t feature_selector, uint16_t windex)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_SET_FEATURE,
.wValue = feature_selector,
.wIndex = windex,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief clear the interface value to the connected device
\param[in] uhost: pointer to USB host
\param[in] feature_selector: feature selector
\param[in] windex: index value
\param[out] none
\retval operation status
*/
usbh_status usbh_clrdevfeature(usbh_host *uhost, uint8_t feature_selector, uint16_t windex)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_DEV | USB_REQTYPE_STRD,
.bRequest = USB_CLEAR_FEATURE,
.wValue = feature_selector,
.wIndex = windex,
.wLength = 0U
};
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief clear or disable a specific feature
\param[in] uhost: pointer to USB host
\param[in] ep_addr: endpoint address
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
usbh_status usbh_clrfeature(usbh_host *uhost, uint8_t ep_addr, uint8_t pp_num)
{
usbh_status status = USBH_BUSY;
usbh_control *usb_ctl = &uhost->control;
usb_core_driver *udev = (usb_core_driver *)uhost->data;
if(CTL_IDLE == usb_ctl->ctl_state) {
usb_ctl->setup.req = (usb_req) {
.bmRequestType = USB_TRX_OUT | USB_RECPTYPE_EP | USB_REQTYPE_STRD,
.bRequest = USB_CLEAR_FEATURE,
.wValue = FEATURE_SELECTOR_EP,
.wIndex = ep_addr,
.wLength = 0U
};
if(EP_ID(ep_addr) == udev->host.pipe[pp_num].ep.num) {
usbh_pipe_toggle_set(udev, pp_num, 0U);
} else {
return USBH_FAIL;
}
usbh_ctlstate_config(uhost, NULL, 0U);
}
status = usbh_ctl_handler(uhost);
return status;
}
/*!
\brief get the next descriptor header
\param[in] pbuf: pointer to buffer where the configuration descriptor set is available
\param[in] ptr: data pointer inside the configuration descriptor set
\param[out] none
\retval return descriptor header
*/
usb_desc_header *usbh_nextdesc_get(uint8_t *pbuf, uint16_t *ptr)
{
usb_desc_header *pnext;
*ptr += ((usb_desc_header *)pbuf)->bLength;
pnext = (usb_desc_header *)((uint8_t *)pbuf + ((usb_desc_header *)pbuf)->bLength);
return (pnext);
}
/*!
\brief get the next descriptor header
\param[in] udev: pointer to device property
\param[in] interface: interface number
\param[out] none
\retval operation status
*/
usbh_status usbh_interface_select(usb_dev_prop *udev, uint8_t interface)
{
usbh_status status = USBH_OK;
if(interface < udev->cfg_desc_set.cfg_desc.bNumInterfaces) {
udev->cur_itf = interface;
} else {
status = USBH_FAIL;
}
return status;
}
/*!
\brief find the interface index for a specific class
\param[in] udev: pointer to device property
\param[in] main_class: class code
\param[in] sub_class: subclass code
\param[in] protocol: protocol code
\param[out] none
\retval interface index in the configuration structure
\note interface index 0xFF means interface index not found
*/
uint8_t usbh_interface_find(usb_dev_prop *udev, uint8_t main_class, uint8_t sub_class, uint8_t protocol)
{
usb_desc_itf *pif;
uint8_t if_ix = 0U;
pif = (usb_desc_itf *)0U;
while(if_ix < udev->cfg_desc_set.cfg_desc.bNumInterfaces) {
pif = &udev->cfg_desc_set.itf_desc_set[if_ix][0].itf_desc;
if(((pif->bInterfaceClass == main_class) || (0xFFU == main_class)) && \
((pif->bInterfaceSubClass == sub_class) || (0xFFU == sub_class)) && \
((pif->bInterfaceProtocol == protocol) || (0xFFU == protocol))) {
return if_ix;
}
if_ix++;
}
return 0xFFU;
}
/*!
\brief find the interface index for a specific class interface and alternate setting number
\param[in] udev: pointer to device property
\param[in] interface_number: interface number
\param[in] alt_settings: alternate setting number
\param[out] none
\retval interface index in the configuration structure
\note interface index 0xFF means interface index not found
*/
uint8_t usbh_interfaceindex_find(usb_dev_prop *udev, uint8_t interface_number, uint8_t alt_settings)
{
usb_desc_itf *pif;
uint8_t if_ix = 0U;
pif = (usb_desc_itf *)0U;
while(if_ix < USBH_MAX_INTERFACES_NUM) {
pif = &udev->cfg_desc_set.itf_desc_set[if_ix][alt_settings].itf_desc;
if((pif->bInterfaceNumber == interface_number) && (pif->bAlternateSetting == alt_settings)) {
return if_ix;
}
if_ix++;
}
return 0xFFU;
}
/*!
\brief parse the device descriptor
\param[in] dev_desc: pointer to USB device descriptor buffer
\param[in] buf: pointer to the source descriptor buffer
\param[in] len: length of the descriptor
\param[out] none
\retval none
*/
static void usbh_devdesc_parse(usb_desc_dev *dev_desc, uint8_t *buf, uint16_t len)
{
*dev_desc = (usb_desc_dev) {
.header = {
.bLength = *(uint8_t *)(buf + 0U),
.bDescriptorType = *(uint8_t *)(buf + 1U)
},
.bcdUSB = BYTE_SWAP(buf + 2U),
.bDeviceClass = *(uint8_t *)(buf + 4U),
.bDeviceSubClass = *(uint8_t *)(buf + 5U),
.bDeviceProtocol = *(uint8_t *)(buf + 6U),
.bMaxPacketSize0 = *(uint8_t *)(buf + 7U)
};
if(len > 8U) {
/* for 1st time after device connection, host may issue only 8 bytes for device descriptor length */
dev_desc->idVendor = BYTE_SWAP(buf + 8U);
dev_desc->idProduct = BYTE_SWAP(buf + 10U);
dev_desc->bcdDevice = BYTE_SWAP(buf + 12U);
dev_desc->iManufacturer = *(uint8_t *)(buf + 14U);
dev_desc->iProduct = *(uint8_t *)(buf + 15U);
dev_desc->iSerialNumber = *(uint8_t *)(buf + 16U);
dev_desc->bNumberConfigurations = *(uint8_t *)(buf + 17U);
}
}
/*!
\brief parse the configuration descriptor
\param[in] cfg_desc: pointer to USB configuration descriptor buffer
\param[in] buf: pointer to the source descriptor buffer
\param[out] none
\retval none
*/
static void usbh_cfgdesc_parse(usb_desc_config *cfg_desc, uint8_t *buf)
{
/* parse configuration descriptor */
*cfg_desc = (usb_desc_config) {
.header = {
.bLength = *(uint8_t *)(buf + 0U),
.bDescriptorType = *(uint8_t *)(buf + 1U),
},
.wTotalLength = BYTE_SWAP(buf + 2U),
.bNumInterfaces = *(uint8_t *)(buf + 4U),
.bConfigurationValue = *(uint8_t *)(buf + 5U),
.iConfiguration = *(uint8_t *)(buf + 6U),
.bmAttributes = *(uint8_t *)(buf + 7U),
.bMaxPower = *(uint8_t *)(buf + 8U)
};
}
/*!
\brief parse the configuration descriptor set
\param[in] udev: pointer to device property
\param[in] buf: pointer to the source descriptor buffer
\param[out] none
\retval none
*/
static void usbh_cfgset_parse(usb_dev_prop *udev, uint8_t *buf)
{
usb_desc_ep *ep = NULL;
usb_desc_itf_set *itf = NULL;
usb_desc_itf itf_value;
usb_desc_config *cfg = NULL;
usb_desc_header *pdesc = (usb_desc_header *)buf;
uint8_t itf_index = 0U, ep_index = 0U, alt_setting = 0U;
uint8_t pre_itf_index = 0U;
uint16_t ptr = 0U;
/* parse configuration descriptor */
usbh_cfgdesc_parse(&udev->cfg_desc_set.cfg_desc, buf);
cfg = &udev->cfg_desc_set.cfg_desc;
ptr = USB_CFG_DESC_LEN;
if(cfg->bNumInterfaces > USBH_MAX_INTERFACES_NUM) {
return;
}
while(ptr < cfg->wTotalLength) {
pdesc = usbh_nextdesc_get((uint8_t *)pdesc, &ptr);
if(USB_DESCTYPE_ITF == pdesc->bDescriptorType) {
itf_index = *(((uint8_t *)pdesc) + 2U);
if(pre_itf_index != itf_index) {
alt_setting = 0U;
}
itf = &udev->cfg_desc_set.itf_desc_set[itf_index][alt_setting];
alt_setting++;
if((*((uint8_t *)pdesc + 3U)) < 3U) {
usbh_itfdesc_parse(&itf_value, (uint8_t *)pdesc);
/* parse endpoint descriptors relative to the current interface */
if(itf_value.bNumEndpoints > USBH_MAX_EP_NUM) {
return;
}
usbh_itfdesc_parse(&itf->itf_desc, (uint8_t *)&itf_value);
/* store the previous interface index */
pre_itf_index = itf_index;
if(0U == itf_value.bNumEndpoints) {
continue;
}
for(ep_index = 0U; ep_index < itf_value.bNumEndpoints;) {
pdesc = usbh_nextdesc_get((void *)pdesc, &ptr);
if(USB_DESCTYPE_EP == pdesc->bDescriptorType) {
ep = &itf->ep_desc[ep_index];
usbh_epdesc_parse(ep, (uint8_t *)pdesc);
ep_index++;
}
}
}
}
}
}
/*!
\brief parse the interface descriptor
\param[in] itf_desc: pointer to USB interface descriptor buffer
\param[in] buf: pointer to the source descriptor buffer
\param[out] none
\retval none
*/
static void usbh_itfdesc_parse(usb_desc_itf *itf_desc, uint8_t *buf)
{
*itf_desc = (usb_desc_itf) {
.header = {
.bLength = *(uint8_t *)(buf + 0U),
.bDescriptorType = *(uint8_t *)(buf + 1U),
},
.bInterfaceNumber = *(uint8_t *)(buf + 2U),
.bAlternateSetting = *(uint8_t *)(buf + 3U),
.bNumEndpoints = *(uint8_t *)(buf + 4U),
.bInterfaceClass = *(uint8_t *)(buf + 5U),
.bInterfaceSubClass = *(uint8_t *)(buf + 6U),
.bInterfaceProtocol = *(uint8_t *)(buf + 7U),
.iInterface = *(uint8_t *)(buf + 8U)
};
}
/*!
\brief parse the endpoint descriptor
\param[in] ep_desc: pointer to USB endpoint descriptor buffer
\param[in] buf: pointer to the source descriptor buffer
\param[out] none
\retval none
*/
static void usbh_epdesc_parse(usb_desc_ep *ep_desc, uint8_t *buf)
{
*ep_desc = (usb_desc_ep) {
.header = {
.bLength = *(uint8_t *)(buf + 0U),
.bDescriptorType = *(uint8_t *)(buf + 1U)
},
.bEndpointAddress = *(uint8_t *)(buf + 2U),
.bmAttributes = *(uint8_t *)(buf + 3U),
.wMaxPacketSize = BYTE_SWAP(buf + 4U),
.bInterval = *(uint8_t *)(buf + 6U)
};
}
/*!
\brief parse the string descriptor
\param[in] psrc: source pointer containing the descriptor data
\param[in] pdest: destination address pointer
\param[in] len: length of the descriptor
\param[out] none
\retval none
*/
static void usbh_strdesc_parse(uint8_t *psrc, uint8_t *pdest, uint16_t len)
{
uint16_t str_len = 0U, index = 0U;
/* the Unicode string descriptor is not NULL-terminated. The string length is
* computed by subtracting two from the value of the first byte of the descriptor.
*/
/* check which is lower size, the size of string or the length of bytes read from the device */
if(USB_DESCTYPE_STR == psrc[1]) {
/* make sure the descriptor is string type */
/* psrc[0] contains size of descriptor, subtract 2 to get the length of string */
str_len = USB_MIN((uint16_t)psrc[0] - 2U, len);
psrc += 2U; /* adjust the offset ignoring the string length and descriptor type */
for(index = 0U; index < str_len; index += 2U) {
/* copy only the string and ignore the Unicode id, hence add the source */
*pdest = psrc[index];
pdest++;
}
*pdest = 0U; /* mark end of string */
}
}
@@ -0,0 +1,181 @@
/*!
\file usbh_pipe.c
\brief USB host mode pipe operation 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 "usbh_pipe.h"
/* local function prototypes ('static') */
static uint16_t usbh_freepipe_get(usb_core_driver *udev);
/*!
\brief create a pipe
\param[in] udev: pointer to USB core instance
\param[in] dev: USB device
\param[in] pp_num: pipe number
\param[in] ep_type: endpoint type
\param[in] ep_mpl: endpoint max packet length
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_create(usb_core_driver *udev, \
usb_dev_prop *dev, \
uint8_t pp_num, \
uint8_t ep_type, \
uint16_t ep_mpl)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
pp->dev_addr = dev->addr;
pp->dev_speed = dev->speed;
pp->ep.type = ep_type;
pp->ep.mps = ep_mpl;
if((USB_EPTYPE_BULK == pp->ep.type) || (USB_EPTYPE_CTRL == pp->ep.type)) {
pp->supp_ping = (uint8_t)(pp->dev_speed == PORT_SPEED_HIGH);
}
usb_pipe_init(udev, pp_num);
return HP_OK;
}
/*!
\brief modify a pipe
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[in] dev_addr: device address
\param[in] dev_speed: device speed
\param[in] ep_mpl: endpoint max packet length
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_update(usb_core_driver *udev, \
uint8_t pp_num, \
uint8_t dev_addr, \
uint32_t dev_speed, \
uint16_t ep_mpl)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
if((pp->dev_addr != dev_addr) && (dev_addr)) {
pp->dev_addr = dev_addr;
}
if((pp->dev_speed != dev_speed) && (dev_speed)) {
pp->dev_speed = dev_speed;
if((USB_EPTYPE_BULK == pp->ep.type) || (USB_EPTYPE_CTRL == pp->ep.type)) {
pp->supp_ping = (uint8_t)(pp->dev_speed == PORT_SPEED_HIGH);
}
}
if((pp->ep.mps != ep_mpl) && (ep_mpl)) {
pp->ep.mps = ep_mpl;
}
usb_pipe_init(udev, pp_num);
return HP_OK;
}
/*!
\brief allocate a new pipe
\param[in] udev: pointer to USB core instance
\param[in] ep_addr: endpoint address
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_allocate(usb_core_driver *udev, uint8_t ep_addr)
{
uint16_t pp_num = usbh_freepipe_get(udev);
if(HP_ERROR != pp_num) {
udev->host.pipe[pp_num].in_used = 1U;
udev->host.pipe[pp_num].ep.dir = EP_DIR(ep_addr);
udev->host.pipe[pp_num].ep.num = EP_ID(ep_addr);
}
return (uint8_t)pp_num;
}
/*!
\brief free a pipe
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_free(usb_core_driver *udev, uint8_t pp_num)
{
if(pp_num < HP_MAX) {
udev->host.pipe[pp_num].in_used = 0U;
}
return USBH_OK;
}
/*!
\brief delete all USB host pipe
\param[in] udev: pointer to USB core instance
\param[out] none
\retval operation status
*/
uint8_t usbh_pipe_delete(usb_core_driver *udev)
{
uint8_t pp_num = 0U;
for(pp_num = 2U; pp_num < HP_MAX; pp_num++) {
udev->host.pipe[pp_num] = (usb_pipe) {0};
}
return USBH_OK;
}
/*!
\brief get a free pipe number for allocation
\param[in] udev: pointer to USB core instance
\param[out] none
\retval operation status
*/
static uint16_t usbh_freepipe_get(usb_core_driver *udev)
{
uint8_t pp_num = 0U;
for(pp_num = 0U; pp_num < HP_MAX; pp_num++) {
if(0U == udev->host.pipe[pp_num].in_used) {
return (uint16_t)pp_num;
}
}
return HP_ERROR;
}
@@ -0,0 +1,369 @@
/*!
\file usbh_transc.c
\brief USB host mode transactions 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 "usbh_pipe.h"
#include "usbh_transc.h"
/* local function prototypes ('static') */
static usb_urb_state usbh_urb_wait(usbh_host *uhost, uint8_t pp_num, uint32_t wait_time);
static void usbh_setup_transc(usbh_host *uhost);
static void usbh_data_in_transc(usbh_host *uhost);
static void usbh_data_out_transc(usbh_host *uhost);
static void usbh_status_in_transc(usbh_host *uhost);
static void usbh_status_out_transc(usbh_host *uhost);
static uint32_t usbh_request_submit(usb_core_driver *udev, uint8_t pp_num);
/*!
\brief send the SETUP packet to the USB device
\param[in] udev: pointer to USB core instance
\param[in] buf: data buffer which will be sent to USB device
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
usbh_status usbh_ctlsetup_send(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
pp->DPID = PIPE_DPID_SETUP;
pp->xfer_buf = buf;
pp->xfer_len = USB_SETUP_PACKET_LEN;
return (usbh_status)usbh_request_submit(udev, pp_num);
}
/*!
\brief send a data packet to the USB device
\param[in] udev: pointer to USB core instance
\param[in] buf: data buffer which will be sent to USB device
\param[in] pp_num: pipe number
\param[in] len: length of the data to be sent
\param[out] none
\retval operation status
*/
usbh_status usbh_data_send(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num, uint16_t len)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
pp->xfer_buf = buf;
pp->xfer_len = len;
switch(pp->ep.type) {
case USB_EPTYPE_CTRL:
if(0U == len) {
pp->data_toggle_out = 1U;
}
pp->DPID = PIPE_DPID[pp->data_toggle_out];
break;
case USB_EPTYPE_INTR:
pp->DPID = PIPE_DPID[pp->data_toggle_out];
pp->data_toggle_out ^= 1U;
break;
case USB_EPTYPE_BULK:
pp->DPID = PIPE_DPID[pp->data_toggle_out];
break;
case USB_EPTYPE_ISOC:
pp->DPID = PIPE_DPID[0];
break;
default:
break;
}
usbh_request_submit(udev, pp_num);
return USBH_OK;
}
/*!
\brief receive a data packet from the USB device
\param[in] udev: pointer to USB core instance
\param[in] buf: data buffer which will be received from USB device
\param[in] pp_num: pipe number
\param[in] len: length of the data to be received
\param[out] none
\retval operation status
*/
usbh_status usbh_data_recev(usb_core_driver *udev, uint8_t *buf, uint8_t pp_num, uint16_t len)
{
usb_pipe *pp = &udev->host.pipe[pp_num];
pp->xfer_buf = buf;
pp->xfer_len = len;
switch(pp->ep.type) {
case USB_EPTYPE_CTRL:
pp->DPID = PIPE_DPID[1];
break;
case USB_EPTYPE_INTR:
pp->DPID = PIPE_DPID[pp->data_toggle_in];
/* toggle DATA PID */
pp->data_toggle_in ^= 1U;
break;
case USB_EPTYPE_BULK:
pp->DPID = PIPE_DPID[pp->data_toggle_in];
break;
case USB_EPTYPE_ISOC:
pp->DPID = PIPE_DPID[0];
break;
default:
break;
}
usbh_request_submit(udev, pp_num);
return USBH_OK;
}
/*!
\brief USB control transfer handler
\param[in] uhost: pointer to USB host
\param[out] none
\retval operation status
*/
usbh_status usbh_ctl_handler(usbh_host *uhost)
{
usbh_status status = USBH_BUSY;
switch(uhost->control.ctl_state) {
case CTL_SETUP:
usbh_setup_transc(uhost);
break;
case CTL_DATA_IN:
usbh_data_in_transc(uhost);
break;
case CTL_DATA_OUT:
usbh_data_out_transc(uhost);
break;
case CTL_STATUS_IN:
usbh_status_in_transc(uhost);
break;
case CTL_STATUS_OUT:
usbh_status_out_transc(uhost);
break;
case CTL_FINISH:
uhost->control.ctl_state = CTL_IDLE;
status = USBH_OK;
break;
case CTL_ERROR:
if(++uhost->control.error_count <= USBH_MAX_ERROR_COUNT) {
/* do the transmission again, starting from SETUP packet */
uhost->control.ctl_state = CTL_SETUP;
} else {
status = USBH_FAIL;
}
break;
default:
break;
}
return status;
}
/*!
\brief wait for USB URB(USB request block) state
\param[in] uhost: pointer to USB host
\param[in] pp_num: pipe number
\param[in] wait_time: wait time
\param[out] none
\retval USB URB state
*/
static usb_urb_state usbh_urb_wait(usbh_host *uhost, uint8_t pp_num, uint32_t wait_time)
{
uint32_t timeout = 0U;
usb_urb_state urb_status = URB_IDLE;
timeout = uhost->control.timer;
while(URB_DONE != (urb_status = usbh_urbstate_get(uhost->data, pp_num))) {
if(URB_NOTREADY == urb_status) {
break;
} else if(URB_STALL == urb_status) {
uhost->control.ctl_state = CTL_SETUP;
break;
} else if(URB_ERROR == urb_status) {
uhost->control.ctl_state = CTL_ERROR;
break;
} else if((wait_time > 0U) && ((uhost->control.timer - timeout) > wait_time)) {
/* timeout for IN transfer */
uhost->control.ctl_state = CTL_ERROR;
break;
} else {
/* no operation, just wait */
}
}
return urb_status;
}
/*!
\brief USB SETUP transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_setup_transc(usbh_host *uhost)
{
/* send a SETUP packet */
usbh_ctlsetup_send(uhost->data, \
uhost->control.setup.data, \
uhost->control.pipe_out_num);
if(URB_DONE == usbh_urb_wait(uhost, uhost->control.pipe_out_num, 0U)) {
uint8_t dir = (uhost->control.setup.req.bmRequestType & USB_TRX_MASK);
if(uhost->control.setup.req.wLength) {
if(USB_TRX_IN == dir) {
uhost->control.ctl_state = CTL_DATA_IN;
} else {
uhost->control.ctl_state = CTL_DATA_OUT;
}
} else {
if(USB_TRX_IN == dir) {
uhost->control.ctl_state = CTL_STATUS_OUT;
} else {
uhost->control.ctl_state = CTL_STATUS_IN;
}
}
}
}
/*!
\brief USB data IN transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_data_in_transc(usbh_host *uhost)
{
usbh_data_recev(uhost->data, \
uhost->control.buf, \
uhost->control.pipe_in_num, \
uhost->control.ctl_len);
if(URB_DONE == usbh_urb_wait(uhost, uhost->control.pipe_in_num, DATA_STAGE_TIMEOUT)) {
uhost->control.ctl_state = CTL_STATUS_OUT;
}
}
/*!
\brief USB data OUT transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_data_out_transc(usbh_host *uhost)
{
usbh_pipe_toggle_set(uhost->data, uhost->control.pipe_out_num, 1U);
usbh_data_send(uhost->data, \
uhost->control.buf, \
uhost->control.pipe_out_num, \
uhost->control.ctl_len);
if(URB_DONE == usbh_urb_wait(uhost, uhost->control.pipe_out_num, DATA_STAGE_TIMEOUT)) {
uhost->control.ctl_state = CTL_STATUS_IN;
}
}
/*!
\brief USB status IN transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_status_in_transc(usbh_host *uhost)
{
uint8_t pp_num = uhost->control.pipe_in_num;
usbh_data_recev(uhost->data, NULL, pp_num, 0U);
if(URB_DONE == usbh_urb_wait(uhost, pp_num, NODATA_STAGE_TIMEOUT)) {
uhost->control.ctl_state = CTL_FINISH;
}
}
/*!
\brief USB status OUT transaction
\param[in] uhost: pointer to USB host
\param[out] none
\retval none
*/
static void usbh_status_out_transc(usbh_host *uhost)
{
uint8_t pp_num = uhost->control.pipe_out_num;
usbh_data_send(uhost->data, NULL, pp_num, 0U);
if(URB_DONE == usbh_urb_wait(uhost, pp_num, NODATA_STAGE_TIMEOUT)) {
uhost->control.ctl_state = CTL_FINISH;
}
}
/*!
\brief prepare a pipe and start a transfer
\param[in] udev: pointer to USB core instance
\param[in] pp_num: pipe number
\param[out] none
\retval operation status
*/
static uint32_t usbh_request_submit(usb_core_driver *udev, uint8_t pp_num)
{
udev->host.pipe[pp_num].urb_state = URB_IDLE;
udev->host.pipe[pp_num].xfer_count = 0U;
if(1U == udev->host.pipe[pp_num].do_ping) {
(void)usb_pipe_ping(udev, (uint8_t)pp_num);
return USB_OK;
}
return (uint32_t)usb_pipe_xfer(udev, pp_num);
}
@@ -0,0 +1,180 @@
/*!
\file usb_cdc.h
\brief the header file of communication device class standard
\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_CDC_H
#define USB_CDC_H
#include "usb_ch9_std.h"
/* communications device class code */
#define USB_CLASS_CDC 0x02U
/* communications interface class control protocol codes */
#define USB_CDC_PROTOCOL_NONE 0x00U /*!< CDC none protocol */
#define USB_CDC_PROTOCOL_AT 0x01U /*!< CDC AT protocol */
#define USB_CDC_PROTOCOL_VENDOR 0xFFU /*!< CDC vendor protocol */
/* data interface class code */
#define USB_CLASS_DATA 0x0AU
#define USB_DESCTYPE_CDC_ACM 0x21U /*!< CDC descriptor type */
#define USB_DESCTYPE_CS_INTERFACE 0x24U /*!< CDC interface descriptor type */
#define USB_CDC_ACM_CONFIG_DESC_SIZE 0x43U /*!< CDC configuration descriptor size */
/* class-specific notification codes for pstn subclasses */
#define USB_CDC_NOTIFY_SERIAL_STATE 0x20U
/* class-specific request codes */
#define SEND_ENCAPSULATED_COMMAND 0x00U /*!< send encapsulated command request */
#define GET_ENCAPSULATED_RESPONSE 0x01U /*!< get encapsulated response request */
#define SET_COMM_FEATURE 0x02U /*!< set command feature request */
#define GET_COMM_FEATURE 0x03U /*!< get command feature request */
#define CLEAR_COMM_FEATURE 0x04U /*!< clear command feature request */
#define SET_AUX_LINE_STATE 0x10U /*!< set AUX line state code */
#define SET_HOOK_STATE 0x11U /*!< set hook state code */
#define PULSE_SETUP 0x12U /*!< pulse setup code */
#define SEND_PULSE 0x13U /*!< send pulse code */
#define SET_PULSE_TIME 0x14U /*!< set pulse time code */
#define RING_AUX_JACK 0x15U /*!< ring AUX jack code */
#define SET_LINE_CODING 0x20U /*!< set line coding request */
#define GET_LINE_CODING 0x21U /*!< get line coding request */
#define SET_CONTROL_LINE_STATE 0x22U /*!< set control line state request */
#define SEND_BREAK 0x23U /*!< send break request */
#define NO_CMD 0xFFU /*!< no command request */
#define SET_RINGER_PARMS 0x30U /*!< set ringer parameter */
#define GET_RINGER_PARMS 0x31U /*!< get ringer parameter */
#define SET_OPERATION_PARMS 0x32U /*!< set operation parameter */
#define GET_OPERATION_PARMS 0x33U /*!< get ringer parameter */
#define SET_LINE_PARMS 0x34U /*!< set line parameter */
#define GET_LINE_PARMS 0x35U /*!< get ringer parameter */
#define DIAL_DIGITS 0x36U /*!< dial digits */
#define SET_UNIT_PARAMETER 0x37U /*!< set unit parameter */
#define GET_UNIT_PARAMETER 0x38U /*!< get unit parameter */
#define CLEAR_UNIT_PARAMETER 0x39U /*!< clear unit parameter */
#define GET_PROFILE 0x3AU /*!< get profile */
#define SET_ETHERNET_MULTICAST_FILTERS 0x40U /*!< set ethernet muilcast filters */
#define SET_ETHERNET_POWER_MANAGEMENT_PATTERN FILTER 0x41U /*!< set ethernet power management pattern filter */
#define GET_ETHERNET_POWER_MANAGEMENT_PATTERN FILTER 0x42U /*!< get ethernet power management pattern filter */
#define SET_ETHERNET_PACKET_FILTER 0x43U /*!< set ethernet power pocket filter */
#define GET_ETHERNET_STATISTIC 0x44U /*!< get ethernet statistic */
#define SET_ATM_DATA_FORMAT 0x50U /*!< set ATM data format */
#define GET_ATM_DEVICE_STATISTICS 0x51U /*!< get ATM device statistics */
#define SET_ATM_DEFAULT_VC 0x52U /*!< set ATM default VC */
#define GET_ATM_VC_STATISTICS 0x53U /*!< get ATM VC statistics */
/* wValue for set control line state */
#define CDC_ACTIVATE_CARRIER_SIGNAL_RTS 0x0002U /*!< CDC activate carrier signal RTS */
#define CDC_DEACTIVATE_CARRIER_SIGNAL_RTS 0x0000U /*!< CDC deactivate carrier signal RTS */
#define CDC_ACTIVATE_SIGNAL_DTR 0x0001U /*!< CDC activate signal DTR */
#define CDC_DEACTIVATE_SIGNAL_DTR 0x0000U /*!< CDC deactivate signal DTR */
/* CDC subclass code */
enum usb_cdc_subclass {
USB_CDC_SUBCLASS_RESERVED = 0U, /*!< reserved */
USB_CDC_SUBCLASS_DLCM, /*!< direct line control mode */
USB_CDC_SUBCLASS_ACM, /*!< abstract control mode */
USB_CDC_SUBCLASS_TCM, /*!< telephone control mode */
USB_CDC_SUBCLASS_MCM, /*!< multichannel control model */
USB_CDC_SUBCLASS_CCM, /*!< CAPI control model */
USB_CDC_SUBCLASS_ENCM, /*!< ethernet networking control model */
USB_CDC_SUBCLASS_ANCM /*!< ATM networking control model */
};
#pragma pack(1)
/* cdc acm line coding structure */
typedef struct {
uint32_t dwDTERate; /*!< data terminal rate */
uint8_t bCharFormat; /*!< stop bits */
uint8_t bParityType; /*!< parity */
uint8_t bDataBits; /*!< data bits */
} acm_line;
/* notification structure */
typedef struct {
uint8_t bmRequestType; /*!< type of request */
uint8_t bNotification; /*!< communication interface class notifications */
uint16_t wValue; /*!< value of notification */
uint16_t wIndex; /*!< index of interface */
uint16_t wLength; /*!< length of notification data */
} acm_notification;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size. */
uint8_t bDescriptorSubtype; /*!< bDescriptorSubtype: header function descriptor */
uint16_t bcdCDC; /*!< bcdCDC: low byte of spec release number (CDC1.10) */
} usb_desc_header_func;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size. */
uint8_t bDescriptorSubtype; /*!< bDescriptorSubtype: call management function descriptor */
uint8_t bmCapabilities; /*!< bmCapabilities: D0 is reset, D1 is ignored */
uint8_t bDataInterface; /*!< bDataInterface: 1 interface used for call management */
} usb_desc_call_managment_func;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size. */
uint8_t bDescriptorSubtype; /*!< bDescriptorSubtype: abstract control management descriptor */
uint8_t bmCapabilities; /*!< bmCapabilities: D1 */
} usb_desc_acm_func;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size. */
uint8_t bDescriptorSubtype; /*!< bDescriptorSubtype: union function descriptor */
uint8_t bMasterInterface; /*!< bMasterInterface: communication class interface */
uint8_t bSlaveInterface0; /*!< bSlaveInterface0: data class interface */
} usb_desc_union_func;
#pragma pack()
typedef struct {
usb_desc_config config; /*!< configure descriptor */
usb_desc_itf cmd_itf; /*!< CDC cmd interface descriptor */
usb_desc_header_func cdc_header; /*!< CDC header function descriptor */
usb_desc_call_managment_func cdc_call_managment; /*!< CDC call management descriptor */
usb_desc_acm_func cdc_acm; /*!< CDC acm descriptor */
usb_desc_union_func cdc_union; /*!< CDC union function descriptor */
usb_desc_ep cdc_cmd_endpoint; /*!< CDC cmd endpoint descriptor */
usb_desc_itf cdc_data_interface; /*!< CDC data interface descriptor */
usb_desc_ep cdc_out_endpoint; /*!< CDC OUT endpoint descriptor */
usb_desc_ep cdc_in_endpoint; /*!< CDC IN endpoint descriptor */
} usb_cdc_desc_config_set;
#endif /* USB_CDC_H */
@@ -0,0 +1,81 @@
/*!
\file usb_hid.h
\brief definitions for the USB HID class
\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_HID_H
#define USB_HID_H
#include "usb_ch9_std.h"
#define USB_HID_CLASS 0x03U /*!< HID class code */
#define USB_DESCTYPE_HID 0x21U /*!< HID descriptor type */
#define USB_DESCTYPE_REPORT 0x22U /*!< report descriptor type */
/* HID subclass code */
#define USB_HID_SUBCLASS_BOOT_ITF 0x01U
/* HID protocol codes */
#define USB_HID_PROTOCOL_KEYBOARD 0x01U /*!< HID keyboard protocol */
#define USB_HID_PROTOCOL_MOUSE 0x02U /*!< HID mouse protocol */
#define GET_REPORT 0x01U /*!< get report request */
#define GET_IDLE 0x02U /*!< get idle request */
#define GET_PROTOCOL 0x03U /*!< get protocol request */
#define SET_REPORT 0x09U /*!< set report request */
#define SET_IDLE 0x0AU /*!< set idle request */
#define SET_PROTOCOL 0x0BU /*!< set protocol request */
#pragma pack(1)
typedef struct {
usb_desc_header header; /*!< regular descriptor header containing the descriptor's type and length */
uint16_t bcdHID; /*!< BCD encoded version that the HID descriptor and device complies to */
uint8_t bCountryCode; /*!< country code of the localized device, or zero if universal */
uint8_t bNumDescriptors; /*!< total number of HID report descriptors for the interface */
uint8_t bDescriptorType; /*!< type of HID report */
uint16_t wDescriptorLength; /*!< length of the associated HID report descriptor, in bytes */
} usb_desc_hid;
#pragma pack()
typedef struct {
usb_desc_config config; /*!< configure descriptor */
usb_desc_itf hid_itf; /*!< HID interface descriptor */
usb_desc_hid hid_vendor; /*!< HID vendor descriptor */
usb_desc_ep hid_epin; /*!< HID IN endpoint descriptor */
usb_desc_ep hid_epout; /*!< HID OUT endpoint descriptor */
}usb_hid_desc_config_set;
#endif /* USB_HID_H */
@@ -0,0 +1,69 @@
/*!
\file msc_bbb.h
\brief definitions for the USB MSC BBB(bulk/bulk/bulk) protocol
\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 MSC_BBB_H
#define MSC_BBB_H
#include "usb_ch9_std.h"
#define BBB_CBW_SIGNATURE 0x43425355U /*!< MSC BBB CBW signature */
#define BBB_CSW_SIGNATURE 0x53425355U /*!< MSC BBB CSW signature */
#define BBB_CBW_LENGTH 31U /*!< MSC BBB CBW length */
#define BBB_CSW_LENGTH 13U /*!< MSC BBB CSW length */
typedef struct {
uint32_t dCBWSignature; /*!< CBW signature */
uint32_t dCBWTag; /*!< CBW tag */
uint32_t dCBWDataTransferLength; /*!< CBW signature */
uint8_t bmCBWFlags; /*!< CBW flags */
uint8_t bCBWLUN; /*!< CBW LUN */
uint8_t bCBWCBLength; /*!< CBW length */
uint8_t CBWCB[16]; /*!< CBW CB */
} msc_bbb_cbw;
typedef struct {
uint32_t dCSWSignature; /*!< CSW signature */
uint32_t dCSWTag; /*!< CSW tag */
uint32_t dCSWDataResidue; /*!< CSW data residue */
uint8_t bCSWStatus; /*!< CSW status */
} msc_bbb_csw;
/* CSW command status */
enum msc_csw_status {
CSW_CMD_PASSED = 0U, /*!< CSW passed command status */
CSW_CMD_FAILED, /*!< CSW failed command status */
CSW_PHASE_ERROR /*!< CSW phase error status */
};
#endif /* MSC_BBB_H */
@@ -0,0 +1,117 @@
/*!
\file msc_scsi.h
\brief definitions for the USB MSC SCSI commands
\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 MSC_SCSI_H
#define MSC_SCSI_H
#include "usb_ch9_std.h"
/* SCSI commands */
#define SCSI_FORMAT_UNIT 0x04U /*!< MSC SCSI format unit */
#define SCSI_INQUIRY 0x12U /*!< MSC SCSI inquiry */
#define SCSI_MODE_SELECT6 0x15U /*!< MSC SCSI select6 mode */
#define SCSI_MODE_SELECT10 0x55U /*!< MSC SCSI select10 mode */
#define SCSI_MODE_SENSE6 0x1AU /*!< MSC SCSI sense6 mode */
#define SCSI_READ_TOC_DATA 0x43U /*!< MSC SCSI read TOC data */
#define SCSI_MODE_SENSE10 0x5AU /*!< MSC SCSI sense10 mode */
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1EU /*!< MSC SCSI allow medium removal */
#define SCSI_READ6 0x08U /*!< MSC SCSI read6 */
#define SCSI_READ10 0x28U /*!< MSC SCSI read10 */
#define SCSI_READ12 0xA8U /*!< MSC SCSI read12 */
#define SCSI_READ16 0x88U /*!< MSC SCSI read16 */
#define SCSI_READ_CAPACITY10 0x25U /*!< MSC SCSI read capacity10 */
#define SCSI_READ_CAPACITY16 0x9EU /*!< MSC SCSI read capacity16 */
#define SCSI_REQUEST_SENSE 0x03U /*!< MSC SCSI request sense */
#define SCSI_START_STOP_UNIT 0x1BU /*!< MSC SCSI start stop unit */
#define SCSI_TEST_UNIT_READY 0x00U /*!< MSC SCSI test unit ready */
#define SCSI_WRITE6 0x0AU /*!< MSC SCSI write6 */
#define SCSI_WRITE10 0x2AU /*!< MSC SCSI write10 */
#define SCSI_WRITE12 0xAAU /*!< MSC SCSI write12 */
#define SCSI_WRITE16 0x8AU /*!< MSC SCSI write16 */
#define SCSI_VERIFY10 0x2FU /*!< MSC SCSI verify10 */
#define SCSI_VERIFY12 0xAFU /*!< MSC SCSI verify12 */
#define SCSI_VERIFY16 0x8FU /*!< MSC SCSI verify16 */
#define SCSI_SEND_DIAGNOSTIC 0x1DU /*!< MSC SCSI send diagnostic */
#define SCSI_READ_FORMAT_CAPACITIES 0x23U /*!< MSC SCSI read format capacities */
#define INVALID_CDB 0x20U /*!< invalid CDB */
#define INVALID_FIELD_IN_COMMAND 0x24U /*!< invalid field in command */
#define PARAMETER_LIST_LENGTH_ERROR 0x1AU /*!< parameter list length error */
#define INVALID_FIELD_IN_PARAMETER_LIST 0x26U /*!< invalid field in parameter list */
#define ADDRESS_OUT_OF_RANGE 0x21U /*!< address out of range */
#define MEDIUM_NOT_PRESENT 0x3AU /*!< medium not present */
#define MEDIUM_HAVE_CHANGED 0x28U /*!< medium have changed */
#define WRITE_PROTECTED 0x27U /*!< write protected */
#define UNRECOVERED_READ_ERROR 0x11U /*!< unrecovered read error */
#define WRITE_FAULT 0x03U /*!< write fault */
#define READ_FORMAT_CAPACITY_DATA_LEN 0x0CU /*!< read format capacity data length */
#define READ_CAPACITY10_DATA_LEN 0x08U /*!< read capacity10 data length */
#define MODE_SENSE10_DATA_LEN 0x08U /*!< sense10 mode data length */
#define MODE_SENSE6_DATA_LEN 0x04U /*!< sense6 mode data length */
#define READ_TOC_CMD_LEN 0x14U /*!< read TOC command length */
#define REQUEST_SENSE_DATA_LEN 0x12U /*!< request sense data length */
#define STANDARD_INQUIRY_DATA_LEN 0x24U /*!< standard inquiry data length */
#define BLKVFY 0x04U /*!< block verify */
enum sense_state {
NO_SENSE = 0U, /*!< MSC SCSI Sense NO_SENSE state */
RECOVERED_ERROR, /*!< MSC SCSI Sense RECOVERED_ERROR */
NOT_READY, /*!< MSC SCSI Sense NOT READY state*/
MEDIUM_ERROR, /*!< MSC SCSI Sense MEDIUM ERROR state*/
HARDWARE_ERROR, /*!< MSC SCSI Sense HARDWARE ERROR state*/
ILLEGAL_REQUEST, /*!< MSC SCSI Sense ILLEGAL REQUEST state*/
UNIT_ATTENTION, /*!< MSC SCSI Sense UNIT ATTENTION state*/
DATA_PROTECT, /*!< MSC SCSI Sense DATA PROTECT state*/
BLANK_CHECK, /*!< MSC SCSI Sense BLANK CHECK state*/
VENDOR_SPECIFIC, /*!< MSC SCSI Sense VENDOR SPECIFIC state*/
COPY_ABORTED, /*!< MSC SCSI Sense COPY ABORTED state*/
ABORTED_COMMAND, /*!< MSC SCSI Sense ABORTED COMMAND state*/
RESERVED, /*!< MSC SCSI Sense RESERVED state*/
VOLUME_OVERFLOW, /*!< MSC SCSI Sense VOLUME OVERFLOW state*/
MISCOMPARE /*!< MSC SCSI Sense MISCOMPARE state*/
};
typedef struct {
uint8_t SenseKey; /*!< MSC SCSI SenseKey */
uint32_t Information; /*!< MSC SCSI sense Information */
uint8_t ASC; /*!< MSC SCSI sense ASC */
uint8_t ASCQ; /*!< MSC SCSI sense ASCQ */
} msc_scsi_sense;
#endif /* MSC_SCSI_H */
@@ -0,0 +1,68 @@
/*!
\file usb_msc.h
\brief definitions for the USB MSC class
\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_MSC_H
#define USB_MSC_H
#include "usb_ch9_std.h"
/* mass storage device class code */
#define USB_CLASS_MSC 0x08U
/* mass storage subclass code */
#define USB_MSC_SUBCLASS_RBC 0x01U /*!< MSC RBC subclass */
#define USB_MSC_SUBCLASS_ATAPI 0x02U /*!< MSC ATAPI subclass */
#define USB_MSC_SUBCLASS_UFI 0x04U /*!< MSC UFI subclass */
#define USB_MSC_SUBCLASS_SCSI 0x06U /*!< MSC SCSI subclass */
#define USB_MSC_SUBCLASS_LOCKABLE 0x07U /*!< MSC LOCKABLE subclass */
#define USB_MSC_SUBCLASS_IEEE1667 0x08U /*!< MSC IEEE1667 subclass */
/* mass storage interface class control protocol codes */
#define USB_MSC_PROTOCOL_CBI 0x00U /*!< MSC CBI protocol */
#define USB_MSC_PROTOCOL_CBI_ALT 0x01U /*!< MSC CBI ALT protocol */
#define USB_MSC_PROTOCOL_BBB 0x50U /*!< MSC BBB protocol */
/* mass storage request codes */
#define USB_MSC_REQ_CODES_ADSC 0x00U /*!< MSC ADSC request */
#define USB_MSC_REQ_CODES_GET 0xFCU /*!< MSC GET request */
#define USB_MSC_REQ_CODES_PUT 0xFDU /*!< MSC PUT request */
#define USB_MSC_REQ_CODES_GML 0xFEU /*!< MSC GML request */
#define USB_MSC_REQ_CODES_BOMSR 0xFFU /*!< MSC BOMSR request */
#define BBB_GET_MAX_LUN 0xFEU /*!< MSC BBB get maximum LUN */
#define BBB_RESET 0xFFU /*!< MSC BBB reset */
#define SCSI_CMD_LENGTH 16U /*!< MSC SCSI command length */
#endif /* USB_MSC_H */
@@ -0,0 +1,248 @@
/*!
\file usb_ch9_std.h
\brief USB 2.0 standard defines
\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_CH9_STD_H
#define USB_CH9_STD_H
#include "usb_conf.h"
#define USB_DEV_QUALIFIER_DESC_LEN 0x0AU /*!< USB device qualifier descriptor length */
#define USB_DEV_DESC_LEN 0x12U /*!< USB device descriptor length */
#define USB_CFG_DESC_LEN 0x09U /*!< USB configuration descriptor length */
#define USB_ITF_DESC_LEN 0x09U /*!< USB interface descriptor length */
#define USB_EP_DESC_LEN 0x07U /*!< USB endpoint descriptor length */
#define USB_IAD_DESC_LEN 0x08U /*!< USB IAD descriptor length */
#define USB_OTG_DESC_LEN 0x03U /*!< USB device OTG descriptor length */
#define USB_SETUP_PACKET_LEN 0x08U /*!< USB SETUP packet length */
/* bit 7 of bmRequestType: data phase transfer direction */
#define USB_TRX_MASK 0x80U /*!< USB transfer direction mask */
#define USB_TRX_OUT 0x00U /*!< USB transfer OUT direction */
#define USB_TRX_IN 0x80U /*!< USB transfer IN direction */
/* bit 6..5 of bmRequestType: request type */
#define USB_REQTYPE_STRD 0x00U /*!< USB standard request */
#define USB_REQTYPE_CLASS 0x20U /*!< USB class request */
#define USB_REQTYPE_VENDOR 0x40U /*!< USB vendor request */
#define USB_REQTYPE_MASK 0x60U /*!< USB request mask */
#define USBD_BUS_POWERED 0x00U /*!< USB bus power supply */
#define USBD_SELF_POWERED 0x01U /*!< USB self power supply */
#define USB_STATUS_REMOTE_WAKEUP 2U /*!< USB is in remote wakeup status */
#define USB_STATUS_SELF_POWERED 1U /*!< USB is in self powered status */
/* bit 4..0 of bmRequestType: recipient type */
enum _usb_recp_type {
USB_RECPTYPE_DEV = 0x0U, /*!< USB device request type */
USB_RECPTYPE_ITF = 0x1U, /*!< USB interface request type */
USB_RECPTYPE_EP = 0x2U, /*!< USB endpoint request type */
USB_RECPTYPE_MASK = 0x3U /*!< USB request type mask */
};
/* bRequest value */
enum _usb_request {
USB_GET_STATUS = 0x0U, /*!< USB get status request */
USB_CLEAR_FEATURE = 0x1U, /*!< USB clear feature request */
USB_RESERVED2 = 0x2U,
USB_SET_FEATURE = 0x3U, /*!< USB set feature request */
USB_RESERVED4 = 0x4U,
USB_SET_ADDRESS = 0x5U, /*!< USB set address request */
USB_GET_DESCRIPTOR = 0x6U, /*!< USB get descriptor request */
USB_SET_DESCRIPTOR = 0x7U, /*!< USB set descriptor request */
USB_GET_CONFIGURATION = 0x8U, /*!< USB get configuration request */
USB_SET_CONFIGURATION = 0x9U, /*!< USB set configuration request */
USB_GET_INTERFACE = 0xAU, /*!< USB get interface request */
USB_SET_INTERFACE = 0xBU, /*!< USB set interface request */
USB_SYNCH_FRAME = 0xCU /*!< USB synchronize frame request */
};
/* descriptor types of USB specifications */
enum _usb_desctype {
USB_DESCTYPE_DEV = 0x1U, /*!< USB device descriptor type */
USB_DESCTYPE_CONFIG = 0x2U, /*!< USB configuration descriptor type */
USB_DESCTYPE_STR = 0x3U, /*!< USB string descriptor type */
USB_DESCTYPE_ITF = 0x4U, /*!< USB interface descriptor type */
USB_DESCTYPE_EP = 0x5U, /*!< USB endpoint descriptor type */
USB_DESCTYPE_DEV_QUALIFIER = 0x6U, /*!< USB device qualifier descriptor type */
USB_DESCTYPE_OTHER_SPD_CONFIG = 0x7U, /*!< USB other speed configuration descriptor type */
USB_DESCTYPE_ITF_POWER = 0x8U, /*!< USB interface power descriptor type */
USB_DESCTYPE_IAD = 0xBU, /*!< USB interface association descriptor type */
USB_DESCTYPE_BOS = 0xFU /*!< USB BOS descriptor type */
};
/* USB Endpoint Descriptor bmAttributes bit definitions */
/* bits 1..0 : transfer type */
enum _usbx_type {
USB_EP_ATTR_CTL = 0x0U, /*!< USB control transfer type */
USB_EP_ATTR_ISO = 0x1U, /*!< USB Isochronous transfer type */
USB_EP_ATTR_BULK = 0x2U, /*!< USB Bulk transfer type */
USB_EP_ATTR_INT = 0x3U /*!< USB Interrupt transfer type */
};
/* bits 3..2 : Sync type (only if ISOCHRONOUS) */
#define USB_EP_ATTR_NOSYNC 0x00U /*!< No Synchronization */
#define USB_EP_ATTR_ASYNC 0x04U /*!< Asynchronous */
#define USB_EP_ATTR_ADAPTIVE 0x08U /*!< Adaptive */
#define USB_EP_ATTR_SYNC 0x0CU /*!< Synchronous */
#define USB_EP_ATTR_SYNCTYPE 0x0CU /*!< Synchronous type */
/* bits 5..4 : usage type (only if ISOCHRONOUS) */
#define USB_EP_ATTR_DATA 0x00U /*!< Data endpoint */
#define USB_EP_ATTR_FEEDBACK 0x10U /*!< Feedback endpoint */
#define USB_EP_ATTR_IMPLICIT_FEEDBACK_DATA 0x20U /*!< Implicit feedback Data endpoint */
#define USB_EP_ATTR_USAGETYPE 0x30U /*!< Usage type */
/* endpoint max packet size bits12..11 */
#define USB_EP_MPS_ADD_0 (0x00 << 11) /*!< None(1 transaction per microframe */
#define USB_EP_MPS_ADD_1 (0x01 << 11) /*!< 1 additional(2 transaction per microframe */
#define USB_EP_MPS_ADD_2 (0x02 << 11) /*!< 2 additional(3 transaction per microframe */
#define FEATURE_SELECTOR_EP 0x00U /*!< USB endpoint feature selector */
#define FEATURE_SELECTOR_DEV 0x01U /*!< USB device feature selector */
#define FEATURE_SELECTOR_REMOTEWAKEUP 0x01U /*!< USB feature selector remote wakeup */
#define BYTE_SWAP(addr) (((uint16_t)(*((uint8_t *)(addr)))) + \
(uint16_t)(((uint16_t)(*(((uint8_t *)(addr)) + 1U))) << 8))
#define BYTE_LOW(x) ((uint8_t)((x) & 0x00FFU))
#define BYTE_HIGH(x) ((uint8_t)(((x) & 0xFF00U) >> 8))
#define USB_MIN(a, b) (((a) < (b)) ? (a) : (b))
#define USB_DEFAULT_CONFIG 0U
/* USB classes */
#define USB_CLASS_HID 0x03U /*!< USB HID class */
#define USB_CLASS_MSC 0x08U /*!< USB MSC class */
/* use the following values when USB host need to get descriptor */
#define USBH_DESC(x) (((x)<< 8) & 0xFF00U)
/* as per USB specs 9.2.6.4 :standard request with data request timeout: 5sec
standard request with no data stage timeout : 50ms */
#define DATA_STAGE_TIMEOUT 5000U /*!< USB data stage timeout*/
#define NODATA_STAGE_TIMEOUT 50U /*!< USB no data stage timeout*/
#pragma pack(1)
/* USB standard device request structure */
typedef struct _usb_req {
uint8_t bmRequestType; /*!< type of request */
uint8_t bRequest; /*!< request of SETUP packet */
uint16_t wValue; /*!< value of SETUP packet */
uint16_t wIndex; /*!< index of SETUP packet */
uint16_t wLength; /*!< length of SETUP packet */
} usb_req;
/* USB SETUP packet define */
typedef union _usb_setup {
uint8_t data[8]; /*!< the data of USB SETUP packet */
usb_req req; /*!< USB standard device request */
} usb_setup;
/* USB descriptor defines */
typedef struct _usb_desc_header {
uint8_t bLength; /*!< size of the descriptor */
uint8_t bDescriptorType; /*!< type of the descriptor */
} usb_desc_header;
typedef struct _usb_desc_dev {
usb_desc_header header; /*!< descriptor header, including type and size */
uint16_t bcdUSB; /*!< BCD of the supported USB specification */
uint8_t bDeviceClass; /*!< USB device class */
uint8_t bDeviceSubClass; /*!< USB device subclass */
uint8_t bDeviceProtocol; /*!< USB device protocol */
uint8_t bMaxPacketSize0; /*!< size of the control (address 0) endpoint's bank in bytes */
uint16_t idVendor; /*!< vendor ID for the USB product */
uint16_t idProduct; /*!< unique product ID for the USB product */
uint16_t bcdDevice; /*!< product release (version) number */
uint8_t iManufacturer; /*!< string index for the manufacturer's name */
uint8_t iProduct; /*!< string index for the product name/details */
uint8_t iSerialNumber; /*!< string index for the product's globally unique hexadecimal serial number */
uint8_t bNumberConfigurations; /*!< total number of configurations supported by the device */
} usb_desc_dev;
typedef struct _usb_desc_config {
usb_desc_header header; /*!< descriptor header, including type and size */
uint16_t wTotalLength; /*!< size of the configuration descriptor header,and all sub descriptors inside the configuration */
uint8_t bNumInterfaces; /*!< total number of interfaces in the configuration */
uint8_t bConfigurationValue; /*!< configuration index of the current configuration */
uint8_t iConfiguration; /*!< index of a string descriptor describing the configuration */
uint8_t bmAttributes; /*!< configuration attributes */
uint8_t bMaxPower; /*!< maximum power consumption of the device while in the current configuration */
} usb_desc_config;
typedef struct _usb_desc_itf {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bInterfaceNumber; /*!< index of the interface in the current configuration */
uint8_t bAlternateSetting; /*!< alternate setting for the interface number */
uint8_t bNumEndpoints; /*!< total number of endpoints in the interface */
uint8_t bInterfaceClass; /*!< interface class ID */
uint8_t bInterfaceSubClass; /*!< interface subclass ID */
uint8_t bInterfaceProtocol; /*!< interface protocol ID */
uint8_t iInterface; /*!< index of the string descriptor describing the interface */
} usb_desc_itf;
typedef struct _usb_desc_ep {
usb_desc_header header; /*!< descriptor header, including type and size. */
uint8_t bEndpointAddress; /*!< logical address of the endpoint */
uint8_t bmAttributes; /*!< endpoint attributes */
uint16_t wMaxPacketSize; /*!< size of the endpoint bank, in bytes */
uint8_t bInterval; /*!< polling interval in milliseconds for the endpoint if it is an INTERRUPT or ISOCHRONOUS type */
} usb_desc_ep;
typedef struct _usb_desc_LANGID {
usb_desc_header header; /*!< descriptor header, including type and size. */
uint16_t wLANGID; /*!< LANGID code */
} usb_desc_LANGID;
typedef struct _usb_desc_str {
usb_desc_header header; /*!< descriptor header, including type and size. */
uint16_t unicode_string[128]; /*!< unicode string data */
} usb_desc_str;
#pragma pack()
/* compute string descriptor length */
#define USB_STRING_LEN(unicode_chars) (sizeof(usb_desc_header) + ((unicode_chars) << 1))
#endif /* USB_CH9_STD_H */