Sync betaflight to Gitea
This commit is contained in:
+316
@@ -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 */
|
||||
Executable
+48
@@ -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 */
|
||||
+951
@@ -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;
|
||||
}
|
||||
Executable
+167
@@ -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;
|
||||
}
|
||||
}
|
||||
+65
@@ -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 */
|
||||
+522
@@ -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;
|
||||
}
|
||||
+173
@@ -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 */
|
||||
+82
@@ -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 */
|
||||
+721
@@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
+241
@@ -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);
|
||||
}
|
||||
Executable
+66
@@ -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 */
|
||||
Executable
+67
@@ -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 */
|
||||
Executable
+473
@@ -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;
|
||||
}
|
||||
Executable
+463
@@ -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;
|
||||
}
|
||||
+103
@@ -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 */
|
||||
+663
@@ -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);
|
||||
}
|
||||
+100
@@ -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 */
|
||||
Executable
+57
@@ -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 */
|
||||
+72
@@ -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 */
|
||||
Executable
+53
@@ -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 */
|
||||
+293
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+399
@@ -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;
|
||||
}
|
||||
+815
@@ -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;
|
||||
//}
|
||||
|
||||
Executable
+78
@@ -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 */
|
||||
Executable
+302
@@ -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;
|
||||
}
|
||||
+102
@@ -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 */
|
||||
+98
@@ -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 */
|
||||
+56
@@ -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 */
|
||||
+320
@@ -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 */
|
||||
}
|
||||
+816
@@ -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;
|
||||
}
|
||||
+264
@@ -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;
|
||||
}
|
||||
+347
@@ -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 */
|
||||
+197
@@ -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 */
|
||||
+115
@@ -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 */
|
||||
+62
@@ -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 */
|
||||
+653
@@ -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 */
|
||||
+50
@@ -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 */
|
||||
+53
@@ -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 */
|
||||
+371
@@ -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);
|
||||
}
|
||||
+660
@@ -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);
|
||||
}
|
||||
+472
@@ -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);
|
||||
}
|
||||
+591
@@ -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;
|
||||
}
|
||||
+643
@@ -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;
|
||||
}
|
||||
+116
@@ -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 */
|
||||
Executable
+96
@@ -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 */
|
||||
+582
@@ -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);
|
||||
}
|
||||
Executable
+271
@@ -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;
|
||||
}
|
||||
+148
@@ -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 */
|
||||
+122
@@ -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 */
|
||||
+97
@@ -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 */
|
||||
+360
@@ -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;
|
||||
}
|
||||
+551
@@ -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;
|
||||
}
|
||||
+232
@@ -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 */
|
||||
}
|
||||
+396
@@ -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 *)§or_num)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)§or_num)[0]);
|
||||
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
msc->bbb.pbuf = data_buf;
|
||||
|
||||
status = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
status = usbh_msc_bbb_process(uhost, lun);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief send the read10 command to the device
|
||||
\param[in] uhost: pointer to USB host handler
|
||||
\param[in] lun: logic unit number
|
||||
\param[in] data_buf: data buffer contains the data to write
|
||||
\param[in] addr: address to which the data will be read
|
||||
\param[in] sector_num: number of sector to be read
|
||||
\param[out] none
|
||||
\retval operation status
|
||||
*/
|
||||
usbh_status usbh_msc_read10(usbh_host *uhost, uint8_t lun, uint8_t *data_buf, uint32_t addr, uint32_t sector_num)
|
||||
{
|
||||
usbh_status status = USBH_FAIL;
|
||||
usbh_msc_handler *msc = (usbh_msc_handler *)uhost->active_class->class_data;
|
||||
|
||||
switch(msc->bbb.cmd_state) {
|
||||
case BBB_CMD_SEND:
|
||||
/* prepare the CBW and relevant field */
|
||||
msc->bbb.cbw.field.dCBWDataTransferLength = sector_num * msc->unit[lun].capacity.block_size;
|
||||
msc->bbb.cbw.field.bmCBWFlags = USB_TRX_IN;
|
||||
msc->bbb.cbw.field.bCBWCBLength = CBW_LENGTH;
|
||||
|
||||
memset(msc->bbb.cbw.field.CBWCB, 0U, CBW_CB_LENGTH);
|
||||
|
||||
msc->bbb.cbw.field.CBWCB[0] = SCSI_READ10;
|
||||
|
||||
/* logical block address */
|
||||
msc->bbb.cbw.field.CBWCB[2] = (((uint8_t *)&addr)[3]);
|
||||
msc->bbb.cbw.field.CBWCB[3] = (((uint8_t *)&addr)[2]);
|
||||
msc->bbb.cbw.field.CBWCB[4] = (((uint8_t *)&addr)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[5] = (((uint8_t *)&addr)[0]);
|
||||
|
||||
/* transfer length */
|
||||
msc->bbb.cbw.field.CBWCB[7] = (((uint8_t *)§or_num)[1]);
|
||||
msc->bbb.cbw.field.CBWCB[8] = (((uint8_t *)§or_num)[0]);
|
||||
|
||||
msc->bbb.state = BBB_SEND_CBW;
|
||||
msc->bbb.cmd_state = BBB_CMD_WAIT;
|
||||
msc->bbb.pbuf = data_buf;
|
||||
|
||||
status = USBH_BUSY;
|
||||
break;
|
||||
|
||||
case BBB_CMD_WAIT:
|
||||
status = usbh_msc_bbb_process(uhost, lun);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
+262
@@ -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 */
|
||||
+70
@@ -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 */
|
||||
+102
@@ -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 */
|
||||
+50
@@ -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 */
|
||||
+659
@@ -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 */
|
||||
+693
@@ -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 */
|
||||
}
|
||||
}
|
||||
+181
@@ -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;
|
||||
}
|
||||
+369
@@ -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 */
|
||||
+117
@@ -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 */
|
||||
+248
@@ -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 */
|
||||
Reference in New Issue
Block a user