first commit

This commit is contained in:
YXZhu
2022-08-06 13:35:32 +08:00
commit 2240710d39
1025 changed files with 472118 additions and 0 deletions
@@ -0,0 +1,260 @@
/*!
\file audio_core.h
\brief the header file of USB audio device class core functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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)
/* audio_freq * data_size (2 bytes) * num_channels (stereo: 2) */
#define DEFAULT_OUT_BIT_RESOLUTION 16U
#define DEFAULT_OUT_CHANNEL_NBR 2U /* mono = 1, stereo = 2 */
#define AUDIO_OUT_PACKET (uint32_t)(((USBD_AUDIO_FREQ_16K * \
(DEFAULT_OUT_BIT_RESOLUTION / 8U) *\
DEFAULT_OUT_CHANNEL_NBR) / 1000U))
/* 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 4U
/* total size of the audio transfer buffer */
#define OUT_BUF_MARGIN 4U
#define TOTAL_OUT_BUF_SIZE ((uint32_t)((AUDIO_OUT_PACKET + OUT_BUF_MARGIN) * OUT_PACKET_NUM))
#define AUDIO_CONFIG_DESC_SET_LEN 109U
#define AUDIO_INTERFACE_DESC_SIZE 9U
#define USB_AUDIO_DESC_SIZ 0x09U
#define AUDIO_STANDARD_EP_DESC_SIZE 0x09U
#define AUDIO_STREAMING_EP_DESC_SIZE 0x07U
/* audio interface class code */
#define USB_CLASS_AUDIO 0x01U
/* audio interface subclass codes */
#define AUDIO_SUBCLASS_CONTROL 0x01U
#define AUDIO_SUBCLASS_AUDIOSTREAMING 0x02U
#define AUDIO_SUBCLASS_MIDISTREAMING 0x03U
/* audio interface protocol codes */
#define AUDIO_PROTOCOL_UNDEFINED 0x00U
#define AUDIO_STREAMING_GENERAL 0x01U
#define AUDIO_STREAMING_FORMAT_TYPE 0x02U
/* audio class-specific descriptor types */
#define AUDIO_DESCTYPE_UNDEFINED 0x20U
#define AUDIO_DESCTYPE_DEVICE 0x21U
#define AUDIO_DESCTYPE_CONFIGURATION 0x22U
#define AUDIO_DESCTYPE_STRING 0x23U
#define AUDIO_DESCTYPE_INTERFACE 0x24U
#define AUDIO_DESCTYPE_ENDPOINT 0x25U
/* audio control interface descriptor subtypes */
#define AUDIO_CONTROL_HEADER 0x01U
#define AUDIO_CONTROL_INPUT_TERMINAL 0x02U
#define AUDIO_CONTROL_OUTPUT_TERMINAL 0x03U
#define AUDIO_CONTROL_MIXER_UNIT 0x04U
#define AUDIO_CONTROL_SELECTOR_UNIT 0x05U
#define AUDIO_CONTROL_FEATURE_UNIT 0x06U
#define AUDIO_CONTROL_PROCESSING_UNIT 0x07U
#define AUDIO_CONTROL_EXTENSION_UNIT 0x08U
#define AUDIO_INPUT_TERMINAL_DESC_SIZE 0x0CU
#define AUDIO_OUTPUT_TERMINAL_DESC_SIZE 0x09U
#define AUDIO_STREAMING_INTERFACE_DESC_SIZE 0x07U
#define AUDIO_CONTROL_MUTE 0x0001U
#define AUDIO_FORMAT_TYPE_I 0x01U
#define AUDIO_FORMAT_TYPE_III 0x03U
#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01U
#define AUDIO_ENDPOINT_GENERAL 0x01U
#define AUDIO_REQ_GET_CUR 0x81U
#define AUDIO_REQ_SET_CUR 0x01U
#define AUDIO_OUT_STREAMING_CTRL 0x02U
#define PACKET_SIZE(freq) (((freq) * 2U) * 2U / 1000U)
#define AUDIO_PACKET_SIZE(frq) (uint8_t)(PACKET_SIZE(frq) & 0xFFU), \
(uint8_t)((PACKET_SIZE(frq) >> 8U) & 0xFFU)
#define SAMPLE_FREQ(frq) (uint8_t)(frq), (uint8_t)((frq) >> 8U), \
(uint8_t)((frq) >> 16U)
#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 */
uint8_t baInterfaceNr; /*!< interface number of the streaming interfaces */
} usb_desc_AC_itf;
typedef struct
{
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< AS_GENERAL 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 sub-frame */
uint8_t bBitResolution; /*!< the number of effectively used bits from the available bits in an audio sub-frame */
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; /*!< EP_GENERAL descriptor subtype */
uint8_t bmAttributes; /*!< transfer type and synchronization type */
uint8_t bLockDelayUnits; /*!< indicates the units used for the wLockDelay field */
uint16_t wLockDelay; /*!< indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry */
} usb_desc_AS_ep;
#pragma pack()
/* USB configuration descriptor structure */
typedef struct
{
usb_desc_config config;
usb_desc_itf std_itf;
usb_desc_AC_itf ac_itf;
usb_desc_input_terminal in_terminal;
usb_desc_mono_feature_unit feature_unit;
usb_desc_output_terminal out_terminal;
usb_desc_itf std_as_itf_zeroband;
usb_desc_itf std_as_itf_opera;
usb_desc_AS_itf as_itf;
usb_desc_format_type format_typeI;
usb_desc_std_ep std_endpoint;
usb_desc_AS_ep as_endpoint;
} 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 * 2U];
uint8_t* isoc_out_wrptr;
uint8_t* isoc_out_rdptr;
/* main buffer for audio control requests transfers and its relative variables */
uint8_t audioctl[64];
uint8_t audioctl_unit;
uint32_t audioctl_len;
uint32_t play_flag;
} usbd_audio_handler;
extern usb_desc audio_desc;
extern usb_class audio_class;
#endif /* __AUDIO_CORE_H */
@@ -0,0 +1,76 @@
/*!
\file audio_out_itf.h
\brief audio OUT (playback) interface header file
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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"
/* audio commands enumeration */
typedef enum
{
AUDIO_CMD_PLAY = 1U,
AUDIO_CMD_PAUSE,
AUDIO_CMD_STOP,
}audio_cmd_enum;
/* mute commands */
#define AUDIO_MUTE 0x01U
#define AUDIO_UNMUTE 0x00U
/* functions return value */
#define AUDIO_OK 0x00U
#define AUDIO_FAIL 0xFFU
/* audio machine states */
#define AUDIO_STATE_INACTIVE 0x00U
#define AUDIO_STATE_ACTIVE 0x01U
#define AUDIO_STATE_PLAYING 0x02U
#define AUDIO_STATE_PAUSED 0x03U
#define AUDIO_STATE_STOPPED 0x04U
#define AUDIO_STATE_ERROR 0x05U
typedef struct {
uint8_t (*audio_init) (uint32_t audio_freq, uint32_t volume, uint32_t options);
uint8_t (*audio_deinit) (uint32_t options);
uint8_t (*audio_cmd) (uint8_t* pbuf, uint32_t size, uint8_t cmd);
uint8_t (*audio_volume_ctl) (uint8_t vol);
uint8_t (*audio_mute_ctl) (uint8_t cmd);
uint8_t (*audio_periodic_tc) (uint8_t cmd);
uint8_t (*audio_state_get) (void);
} audio_fops_struct;
extern audio_fops_struct audio_out_fops;
#endif /* __AUDIO_OUT_ITF_H */
@@ -0,0 +1,549 @@
/*!
\file audio_core.c
\brief USB audio device class core functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_transc.h"
#include "audio_out_itf.h"
#include "audio_core.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x9574U
/* local function prototypes ('static') */
static uint8_t usbd_audio_sof (usb_dev *udev);
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_ctlx_out (usb_dev *udev);
static void audio_data_out (usb_dev *udev, uint8_t ep_num);
usb_class audio_class = {
.init = audio_init,
.deinit = audio_deinit,
.req_process = audio_req_handler,
.ctlx_out = audio_ctlx_out,
.data_out = audio_data_out
};
usbd_int_cb_struct usb_inthandler =
{
usbd_audio_sof,
};
/* note:it should use the c99 standard when compiling the below codes */
/* USB standard device descriptor */
usb_desc_dev audio_dev_desc =
{
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
.bcdUSB = 0x0200U,
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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 */
usb_desc_config_set audio_config_set =
{
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = AUDIO_CONFIG_DESC_SET_LEN,
.bNumInterfaces = 0x02U,
.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 = AUDIO_SUBCLASS_CONTROL,
.bInterfaceProtocol = AUDIO_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.ac_itf =
{
.header =
{
.bLength = sizeof(usb_desc_AC_itf),
.bDescriptorType = AUDIO_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = 0x01U,
.bcdADC = 0x0100U,
.wTotalLength = 0x0027U,
.bInCollection = 0x01U,
.baInterfaceNr = 0x01U
},
.in_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_input_terminal),
.bDescriptorType = AUDIO_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = 0x02U,
.bTerminalID = 0x01U,
.wTerminalType = 0x0101U,
.bAssocTerminal = 0x00U,
.bNrChannels = 0x01U,
.wChannelConfig = 0x0000U,
.iChannelNames = 0x00U,
.iTerminal = 0x00U
},
.feature_unit =
{
.header =
{
.bLength = sizeof(usb_desc_mono_feature_unit),
.bDescriptorType = AUDIO_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AUDIO_CONTROL_FEATURE_UNIT,
.bUnitID = AUDIO_OUT_STREAMING_CTRL,
.bSourceID = 0x01U,
.bControlSize = 0x01U,
.bmaControls0 = AUDIO_CONTROL_MUTE,
.bmaControls1 = 0x00U,
.iFeature = 0x00U
},
.out_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_output_terminal),
.bDescriptorType = AUDIO_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AUDIO_CONTROL_OUTPUT_TERMINAL,
.bTerminalID = 0x03U,
.wTerminalType = 0x0301U,
.bAssocTerminal = 0x00U,
.bSourceID = 0x02U,
.iTerminal = 0x00U
},
.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 = AUDIO_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AUDIO_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.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 = AUDIO_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AUDIO_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.as_itf =
{
.header =
{
.bLength = sizeof(usb_desc_AS_itf),
.bDescriptorType = AUDIO_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AUDIO_STREAMING_GENERAL,
.bTerminalLink = 0x01U,
.bDelay = 0x01U,
.wFormatTag = 0x0001U,
},
.format_typeI =
{
.header =
{
.bLength = sizeof(usb_desc_format_type),
.bDescriptorType = AUDIO_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AUDIO_STREAMING_FORMAT_TYPE,
.bFormatType = AUDIO_FORMAT_TYPE_III,
.bNrChannels = 0x02U,
.bSubFrameSize = 0x02U,
.bBitResolution = 0x10U,
.bSamFreqType = 0x01U,
.bSamFreq[0] = (uint8_t)USBD_AUDIO_FREQ_16K,
.bSamFreq[1] = USBD_AUDIO_FREQ_16K >> 8,
.bSamFreq[2] = USBD_AUDIO_FREQ_16K >> 16
},
.std_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_std_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = AUDIO_OUT_EP,
.bmAttributes = USB_ENDPOINT_TYPE_ISOCHRONOUS,
.wMaxPacketSize = PACKET_SIZE(USBD_AUDIO_FREQ_16K),
.bInterval = 0x01U,
.bRefresh = 0x00U,
.bSynchAddress = 0x00U
},
.as_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_AS_ep),
.bDescriptorType = AUDIO_DESCTYPE_ENDPOINT
},
.bDescriptorSubtype = AUDIO_ENDPOINT_GENERAL,
.bmAttributes = 0x00U,
.bLockDelayUnits = 0x00U,
.wLockDelay = 0x0000U,
}
};
/* USB language ID descriptor */
static usb_desc_LANGID usbd_language_id_desc =
{
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.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 usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
/* USB string descriptor */
static uint8_t* 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_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)
{
usb_desc_std_ep std_ep = audio_config_set.std_endpoint;
static usbd_audio_handler audio_handler;
memset((void *)&audio_handler, 0, sizeof(usbd_audio_handler));
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 RX endpoint */
usbd_ep_init(udev, EP_BUF_DBL, AUDIO_BUF_ADDR, &ep);
usbd_int_fops = &usb_inthandler;
audio_handler.isoc_out_rdptr = audio_handler.isoc_out_buff;
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
/* initialize the audio output hardware layer */
if (USBD_OK != audio_out_fops.audio_init(USBD_AUDIO_FREQ_16K, DEFAULT_VOLUME, 0U)) {
return USBD_FAIL;
}
udev->ep_transc[AUDIO_OUT_EP][TRANSC_OUT] = audio_class.data_out;
/* prepare out endpoint to receive audio data */
usbd_ep_recev (udev, AUDIO_OUT_EP, (uint8_t*)audio_handler.isoc_out_buff, (uint16_t)AUDIO_OUT_PACKET);
udev->class_data[USBD_AUDIO_INTERFACE] = (void *)&audio_handler;
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)
{
/* deinitialize audio endpoints */
usbd_ep_deinit(udev, AUDIO_OUT_EP);
/* deinitialize the audio output hardware layer */
if (USBD_OK != audio_out_fops.audio_deinit(0U)) {
return USBD_FAIL;
}
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;
usbd_audio_handler *audio = (usbd_audio_handler *)udev->class_data[USBD_AUDIO_INTERFACE];
switch (req->bRequest) {
case AUDIO_REQ_GET_CUR:
usb_transc_config(&udev->transc_in[0], audio->audioctl, req->wLength, 0U);
status = REQ_SUPP;
break;
case AUDIO_REQ_SET_CUR:
if (req->wLength) {
usb_transc_config(&udev->transc_out[0], audio->audioctl, req->wLength, 0U);
udev->class_core->req_cmd = AUDIO_REQ_SET_CUR;
audio->audioctl_len = req->wLength;
audio->audioctl_unit = BYTE_HIGH(req->wIndex);
status = REQ_SUPP;
}
break;
default:
break;
}
return status;
}
/*!
\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 void audio_data_out (usb_dev *udev, uint8_t ep_num)
{
usbd_audio_handler *audio = (usbd_audio_handler *)udev->class_data[USBD_AUDIO_INTERFACE];
if (AUDIO_OUT_EP == ep_num) {
/* increment the Buffer pointer or roll it back when all buffers are full */
if (audio->isoc_out_wrptr >= (audio->isoc_out_buff + (AUDIO_OUT_PACKET * OUT_PACKET_NUM))) {
/* all buffers are full: roll back */
audio->isoc_out_wrptr = audio->isoc_out_buff;
} else {
/* increment the buffer pointer */
audio->isoc_out_wrptr += AUDIO_OUT_PACKET;
}
/* prepare out endpoint to receive next audio packet */
usbd_ep_recev (udev, AUDIO_OUT_EP, (uint8_t*)(audio->isoc_out_wrptr), (uint16_t)AUDIO_OUT_PACKET);
/* trigger the start of streaming only when half buffer is full */
if ((0U == audio->play_flag) && (audio->isoc_out_wrptr >= (audio->isoc_out_buff + ((AUDIO_OUT_PACKET * OUT_PACKET_NUM) / 2U)))) {
/* enable start of streaming */
audio->play_flag = 1U;
}
}
}
/*!
\brief handles audio control request data
\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)
{
usbd_audio_handler *audio = (usbd_audio_handler *)udev->class_data[USBD_AUDIO_INTERFACE];
/* check if an audio_control request has been issued */
if (AUDIO_REQ_SET_CUR == udev->class_core->req_cmd) {
/* 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 (AUDIO_OUT_STREAMING_CTRL == audio->audioctl_unit) {
/* in this driver, to simplify code, only one unit is manage */
/* call the audio interface mute function */
audio_out_fops.audio_mute_ctl(audio->audioctl[0]);
/* reset the audioctl_cmd variable to prevent re-entering this function */
udev->class_core->req_cmd = 0U;
audio->audioctl_len = 0U;
}
}
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 usbd_audio_sof (usb_dev *udev)
{
usbd_audio_handler *audio = (usbd_audio_handler *)udev->class_data[USBD_AUDIO_INTERFACE];
/* check if there are available data in stream buffer.
in this function, a single variable (play_flag) is used to avoid software delays.
the play operation must be executed as soon as possible after the SOF detection. */
if (audio->play_flag) {
/* start playing received packet */
audio_out_fops.audio_cmd((uint8_t*)(audio->isoc_out_rdptr), /* samples buffer pointer */
AUDIO_OUT_PACKET, /* number of samples in Bytes */
AUDIO_CMD_PLAY); /* command to be processed */
/* increment the Buffer pointer or roll it back when all buffers all full */
if (audio->isoc_out_rdptr >= (audio->isoc_out_buff + (AUDIO_OUT_PACKET * OUT_PACKET_NUM))) {
/* roll back to the start of buffer */
audio->isoc_out_rdptr = audio->isoc_out_buff;
} else {
/* increment to the next sub-buffer */
audio->isoc_out_rdptr += AUDIO_OUT_PACKET;
}
/* if all available buffers have been consumed, stop playing */
if (audio->isoc_out_rdptr == audio->isoc_out_wrptr) {
/* pause the audio stream */
audio_out_fops.audio_cmd((uint8_t*)(audio->isoc_out_buff), /* samples buffer pointer */
AUDIO_OUT_PACKET, /* number of samples in Bytes */
AUDIO_CMD_PAUSE); /* command to be processed */
/* stop entering play loop */
audio->play_flag = 0U;
/* reset buffer pointers */
audio->isoc_out_rdptr = audio->isoc_out_buff;
audio->isoc_out_wrptr = audio->isoc_out_buff;
}
}
return USBD_OK;
}
@@ -0,0 +1,242 @@
/*!
\file audio_out_itf.c
\brief audio OUT (playback) interface functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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, uint32_t options);
static uint8_t deinit (uint32_t options);
static uint8_t audio_cmd (uint8_t* pbuf, uint32_t size, uint8_t cmd);
static uint8_t volume_ctl (uint8_t vol);
static uint8_t mute_ctl (uint8_t cmd);
static uint8_t periodic_tc (uint8_t cmd);
static uint8_t get_state (void);
audio_fops_struct audio_out_fops =
{
init,
deinit,
audio_cmd,
volume_ctl,
mute_ctl,
periodic_tc,
get_state
};
static uint8_t audio_state = AUDIO_STATE_INACTIVE;
/*!
\brief initialize and configures all required resources for audio play function
\param[in] audio_freq: statrt_up audio frequency
\param[in] volume: start_up volume to be set
\param[in] options: specific options passed to low layer function
\param[out] none
\retval AUDIO_OK if all operations succeed, otherwise, AUDIO_FAIL.
*/
static uint8_t init (uint32_t audio_freq, uint32_t volume, uint32_t options)
{
static uint32_t initialized = 0U;
/* check if the low layer has already been initialized */
if (0U == initialized) {
/* call low layer function */
if (0U != eval_audio_init(OUTPUT_DEVICE_AUTO, volume, audio_freq)) {
audio_state = AUDIO_STATE_ERROR;
return AUDIO_FAIL;
}
/* set the initialization flag to prevent reinitializing the interface again */
initialized = 1U;
}
/* update the audio state machine */
audio_state = AUDIO_STATE_ACTIVE;
return AUDIO_OK;
}
/*!
\brief free all resources used by low layer and stops audio-play function
\param[in] options: specific options passed to low layer function
\param[out] none
\retval AUDIO_OK if all operations succeed, otherwise, AUDIO_FAIL.
*/
static uint8_t deinit (uint32_t options)
{
/* update the audio state machine */
audio_state = AUDIO_STATE_INACTIVE;
return AUDIO_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 AUDIO_CMD_PLAY
\arg AUDIO_CMD_PAUSE
\arg AUDIO_CMD_RESUME
\arg AUDIO_CMD_STOP
\param[out] none
\retval AUDIO_OK if all operations succeed, otherwise, AUDIO_FAIL.
*/
static uint8_t audio_cmd (uint8_t* pbuf, uint32_t size, uint8_t cmd)
{
uint8_t status = AUDIO_OK;
/* check the current state */
if ((AUDIO_STATE_INACTIVE == audio_state) || (AUDIO_STATE_ERROR == audio_state)) {
audio_state = AUDIO_STATE_ERROR;
return AUDIO_FAIL;
}
switch (cmd) {
/* process the play command */
case AUDIO_CMD_PLAY:
/* if current state is active or stopped */
if ((AUDIO_STATE_ACTIVE == audio_state) || \
(AUDIO_STATE_STOPPED == audio_state) || \
(AUDIO_STATE_PLAYING == audio_state)) {
audio_mal_play((uint32_t)pbuf, (size / 2U));
audio_state = AUDIO_STATE_PLAYING;
} else if (AUDIO_STATE_PAUSED == audio_state) {
if (eval_audio_pause_resume(AUDIO_RESUME, (uint32_t)pbuf, (size / 2U))) {
audio_state = AUDIO_STATE_ERROR;
status = AUDIO_FAIL;
} else {
audio_state = AUDIO_STATE_PLAYING;
}
} else {
status = AUDIO_FAIL;
}
break;
/* process the stop command */
case AUDIO_CMD_STOP:
if (AUDIO_STATE_PLAYING != audio_state) {
/* unsupported command */
status = AUDIO_FAIL;
} else if (eval_audio_stop(CODEC_PDWN_SW)) {
audio_state = AUDIO_STATE_ERROR;
status = AUDIO_FAIL;
} else {
audio_state = AUDIO_STATE_STOPPED;
}
break;
/* process the pause command */
case AUDIO_CMD_PAUSE:
if (AUDIO_STATE_PLAYING != audio_state) {
/* unsupported command */
status = AUDIO_FAIL;
} else if (eval_audio_pause_resume(AUDIO_PAUSE, (uint32_t)pbuf, (size / 2U))) {
audio_state = AUDIO_STATE_ERROR;
status = AUDIO_FAIL;
} else {
audio_state = AUDIO_STATE_PAUSED;
}
break;
/* unsupported command */
default:
break;
}
return status;
}
/*!
\brief set the volume level
\param[in] vol: volume level to be set in % (from 0% to 100%)
\param[out] none
\retval AUDIO_OK if all operations succeed, otherwise, AUDIO_FAIL.
*/
static uint8_t volume_ctl (uint8_t vol)
{
/* call low layer volume setting function */
if (eval_audio_volume_ctl(vol)) {
audio_state = AUDIO_STATE_ERROR;
return AUDIO_FAIL;
}
return AUDIO_OK;
}
/*!
\brief mute or unmute the audio current output
\param[in] cmd: can be 0 to unmute, or 1 to mute
\param[out] none
\retval AUDIO_OK if all operations succeed, otherwise, AUDIO_FAIL.
*/
static uint8_t mute_ctl (uint8_t cmd)
{
/* call low layer mute setting function */
if (eval_audio_mute((uint32_t)cmd)) {
audio_state = AUDIO_STATE_ERROR;
return AUDIO_FAIL;
}
return AUDIO_OK;
}
/*!
\brief periodic transfer control
\param[in] cmd: command
\param[out] none
\retval AUDIO_OK if all operations succeed, otherwise, AUDIO_FAIL.
*/
static uint8_t periodic_tc (uint8_t cmd)
{
return AUDIO_OK;
}
/*!
\brief return the current state of the audio machine
\param[in] none
\param[out] none
\retval AUDIO_OK if all operations succeed, otherwise, AUDIO_FAIL.
*/
static uint8_t get_state (void)
{
return audio_state;
}
@@ -0,0 +1,163 @@
/*!
\file cdc_acm_core.h
\brief the header file of cdc acm driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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"
/* communications device class code */
#define USB_CLASS_CDC 0x02U
/* CDC subclass code */
#define USB_CDC_SUBCLASS_DLCM 0x01U
#define USB_CDC_SUBCLASS_ACM 0x02U
/* communications interface class control protocol codes */
#define USB_CDC_PROTOCOL_NONE 0x00U
#define USB_CDC_PROTOCOL_AT 0x01U
/* data interface class code */
#define USB_CLASS_DATA 0x0AU
#define USB_DESCTYPE_CDC_ACM 0x21U
#define USB_DESCTYPE_CS_INTERFACE 0x24U
#define USB_CDC_ACM_CONFIG_DESC_SIZE 0x43U
/* class-specific notification codes for PSTN subclasses */
#define USB_CDC_NOTIFY_SERIAL_STATE 0x20U
/* class-specific request codes */
#define SEND_ENCAPSULATED_COMMAND 0x00U
#define GET_ENCAPSULATED_RESPONSE 0x01U
#define SET_COMM_FEATURE 0x02U
#define GET_COMM_FEATURE 0x03U
#define CLEAR_COMM_FEATURE 0x04U
#define SET_LINE_CODING 0x20U
#define GET_LINE_CODING 0x21U
#define SET_CONTROL_LINE_STATE 0x22U
#define SEND_BREAK 0x23U
#define NO_CMD 0xFFU
#pragma pack(1)
/* CDC ACM line coding struct */
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;
/* header function struct */
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;
/* call managment function struct */
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;
/* acm function struct */
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;
/* union function struct */
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()
/* configuration descriptor struct */
typedef struct {
usb_desc_config config;
usb_desc_itf cmd_itf;
usb_desc_header_func cdc_header;
usb_desc_call_managment_func cdc_call_managment;
usb_desc_acm_func cdc_acm;
usb_desc_union_func cdc_union;
usb_desc_ep cdc_cmd_endpoint;
usb_desc_itf cdc_data_interface;
usb_desc_ep cdc_out_endpoint;
usb_desc_ep cdc_in_endpoint;
} usb_cdc_desc_config_set;
#define USB_CDC_RX_LEN 64U
typedef struct {
uint8_t pre_packet_send;
uint8_t packet_sent;
uint8_t packet_receive;
uint8_t data[USB_CDC_RX_LEN];
uint32_t receive_length;
acm_line line_coding;
} usb_cdc_handler;
extern usb_desc cdc_desc;
extern usb_class cdc_class;
/* function declarations */
/* receive CDC ACM data */
void cdc_acm_data_receive(usb_dev *udev);
/* send CDC ACM data */
void cdc_acm_data_send(usb_dev *udev);
/* check cdc acm is ready for data transfer */
uint8_t cdc_acm_check_ready(usb_dev *udev);
#endif /* __CDC_ACM_CORE_H */
@@ -0,0 +1,510 @@
/*!
\file cdc_acm_core.c
\brief CDC ACM driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_transc.h"
#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 */
usb_desc_dev cdc_dev_desc =
{
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV,
},
.bcdUSB = 0x0200U,
.bDeviceClass = USB_CLASS_CDC,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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 */
usb_cdc_desc_config_set cdc_config_desc =
{
.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 = CDC_ACM_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_OUT_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = CDC_ACM_DATA_PACKET_SIZE,
.bInterval = 0x00U
},
.cdc_in_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = CDC_IN_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = CDC_ACM_DATA_PACKET_SIZE,
.bInterval = 0x00U
}
};
/* USB language ID descriptor */
static usb_desc_LANGID usbd_language_id_desc =
{
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR,
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.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 usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
/* USB string descriptor set */
uint8_t* 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_handler (usb_dev *udev, usb_req *req);
static uint8_t cdc_acm_ctlx_out (usb_dev *udev);
static void cdc_acm_data_in (usb_dev *udev, uint8_t ep_num);
static void cdc_acm_data_out (usb_dev *udev, uint8_t ep_num);
usb_class cdc_class = {
.req_cmd = NO_CMD,
.init = cdc_acm_init,
.deinit = cdc_acm_deinit,
.req_process = cdc_acm_req_handler,
.ctlx_out = cdc_acm_ctlx_out,
.data_in = cdc_acm_data_in,
.data_out = cdc_acm_data_out
};
/*!
\brief receive CDC ACM data
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
void cdc_acm_data_receive(usb_dev *udev)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];
cdc->packet_receive = 0U;
cdc->pre_packet_send = 0U;
usbd_ep_recev(udev, CDC_OUT_EP, (uint8_t*)(cdc->data), USB_CDC_RX_LEN);
}
/*!
\brief send CDC ACM data
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
void cdc_acm_data_send (usb_dev *udev)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];
uint32_t data_len = cdc->receive_length;
if ((0U != data_len) && (1U == cdc->packet_sent)) {
cdc->packet_sent = 0U;
usbd_ep_send(udev, CDC_IN_EP, (uint8_t*)(cdc->data), (uint16_t)data_len);
cdc->receive_length = 0U;
}
}
/*!
\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, 5 otherwise
*/
uint8_t cdc_acm_check_ready(usb_dev *udev)
{
if (udev->class_data[CDC_COM_INTERFACE] != NULL) {
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];
if ((1U == cdc->packet_receive) && (1U == cdc->pre_packet_send)) {
return 0U;
}
}
return 5U;
}
/*!
\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)
{
static usb_cdc_handler cdc_handler;
/* initialize the data endpoints */
usbd_ep_init(udev, EP_BUF_SNG, BULK_TX_ADDR, &(cdc_config_desc.cdc_in_endpoint));
usbd_ep_init(udev, EP_BUF_SNG, BULK_RX_ADDR, &(cdc_config_desc.cdc_out_endpoint));
/* initialize the command endpoint */
usbd_ep_init(udev, EP_BUF_SNG, INT_TX_ADDR, &(cdc_config_desc.cdc_cmd_endpoint));
udev->ep_transc[EP_ID(CDC_IN_EP)][TRANSC_IN] = cdc_class.data_in;
udev->ep_transc[CDC_OUT_EP][TRANSC_OUT] = cdc_class.data_out;
/* initialize cdc handler structure */
cdc_handler.packet_receive = 0U;
cdc_handler.packet_sent = 1U;
cdc_handler.pre_packet_send = 1U;
cdc_handler.receive_length = 0U;
cdc_handler.line_coding = (acm_line){
.dwDTERate = 115200U,
.bCharFormat = 0U,
.bParityType = 0U,
.bDataBits = 0x08U
};
udev->class_data[CDC_COM_INTERFACE] = (void *)&cdc_handler;
return USBD_OK;
}
/*!
\brief de-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_deinit (usb_dev *udev, uint8_t config_index)
{
/* deinitialize the data endpoints */
usbd_ep_deinit(udev, CDC_IN_EP);
usbd_ep_deinit(udev, CDC_OUT_EP);
/* deinitialize the command endpoint */
usbd_ep_deinit(udev, CDC_CMD_EP);
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->class_data[CDC_COM_INTERFACE];
if (NO_CMD != udev->class_core->req_cmd) {
cdc->packet_receive = 1U;
cdc->pre_packet_send = 1U;
udev->class_core->req_cmd = NO_CMD;
}
return USBD_OK;
}
/*!
\brief handle CDC ACM data in transaction
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static void cdc_acm_data_in (usb_dev *udev, uint8_t ep_num)
{
usb_transc *transc = &udev->transc_in[ep_num];
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];
if (transc->xfer_count == transc->max_len) {
usbd_ep_send(udev, EP_ID(ep_num), NULL, 0U);
} else {
cdc->packet_sent = 1U;
cdc->pre_packet_send = 1U;
}
}
/*!
\brief handle CDC ACM data out transaction
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static void cdc_acm_data_out (usb_dev *udev, uint8_t ep_num)
{
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];
cdc->packet_receive = 1U;
cdc->receive_length = udev->transc_out[ep_num].xfer_count;
}
/*!
\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_handler (usb_dev *udev, usb_req *req)
{
uint8_t status = REQ_NOTSUPP, noti_buf[10] = {0U};
usb_cdc_handler *cdc = (usb_cdc_handler *)udev->class_data[CDC_COM_INTERFACE];
acm_notification *notif = (void *)noti_buf;
switch (req->bRequest) {
case SEND_ENCAPSULATED_COMMAND:
break;
case GET_ENCAPSULATED_RESPONSE:
break;
case SET_COMM_FEATURE:
break;
case GET_COMM_FEATURE:
break;
case CLEAR_COMM_FEATURE:
break;
case SET_LINE_CODING:
/* set the value of the current command to be processed */
udev->class_core->req_cmd = req->bRequest;
usb_transc_config(&udev->transc_out[0U], (uint8_t *)&cdc->line_coding, req->wLength, 0U);
status = REQ_SUPP;
break;
case GET_LINE_CODING:
usb_transc_config(&udev->transc_in[0U], (uint8_t *)&cdc->line_coding, 7U, 0U);
status = REQ_SUPP;
break;
case SET_CONTROL_LINE_STATE:
notif->bmRequestType = 0xA1U;
notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE;
notif->wIndex = 0U;
notif->wValue = 0U;
notif->wLength = 2U;
noti_buf[8] = (uint8_t)req->wValue & 3U;
noti_buf[9] = 0U;
status = REQ_SUPP;
break;
case SEND_BREAK:
break;
default:
break;
}
return status;
}
@@ -0,0 +1,179 @@
/*!
\file dfu_core.h
\brief the header file of USB DFU device class core functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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
#define USB_DFU_PROTOCL_DFU 0x02U
/* manifestation state */
#define MANIFEST_COMPLETE 0x00U
#define MANIFEST_IN_PROGRESS 0x01U
/* DFU attributes code */
#define USB_DFU_CAN_DOWNLOAD 0x01U
#define USB_DFU_CAN_UPLOAD 0x02U
#define USB_DFU_MANIFEST_TOLERANT 0x04U
#define USB_DFU_WILL_DETACH 0x08U
/* special commands with download request */
#define GET_COMMANDS 0x00U
#define SET_ADDRESS_POINTER 0x21U
#define ERASE 0x41U
/* memory operation command */
#define CMD_ERASE 0U
#define CMD_WRITE 1U
#define _BYTE1(x) (uint8_t)((x) & 0xFFU) /*!< addressing cycle 1st byte */
#define _BYTE2(x) (uint8_t)(((x) & 0xFF00U) >> 8U) /*!< addressing cycle 2nd byte */
#define _BYTE3(x) (uint8_t)(((x) & 0xFF0000U) >> 16U) /*!< addressing cycle 3rd byte */
#define SET_POLLING_TIMEOUT(x) do { \
dfu->bwPollTimeout0 = _BYTE1(x);\
dfu->bwPollTimeout1 = _BYTE2(x);\
dfu->bwPollTimeout2 = _BYTE3(x);\
} while(0)
#define FLASH_ERASE_TIMEOUT 60U
#define FLASH_WRITE_TIMEOUT 80U
/* bit detach capable = bit 3 in bmAttributes field */
#define DFU_DETACH_MASK (uint8_t)(0x10U)
#define USB_DFU_CONFIG_DESC_SIZE (18U + (9U * USBD_ITF_MAX_NUM))
#define DFU_DESC_TYPE 0x21U
/* DFU device state enumeration */
typedef enum {
STATE_APP_IDLE = 0x00U,
STATE_APP_DETACH,
STATE_DFU_IDLE,
STATE_DFU_DNLOAD_SYNC,
STATE_DFU_DNBUSY,
STATE_DFU_DNLOAD_IDLE,
STATE_DFU_MANIFEST_SYNC,
STATE_DFU_MANIFEST,
STATE_DFU_MANIFEST_WAIT_RESET,
STATE_DFU_UPLOAD_IDLE,
STATE_DFU_ERROR
} dfu_state;
/* DFU device status enumeration */
typedef enum {
STATUS_OK = 0x00U,
STATUS_ERR_TARGET,
STATUS_ERR_FILE,
STATUS_ERR_WRITE,
STATUS_ERR_ERASE,
STATUS_ERR_CHECK_ERASED,
STATUS_ERR_PROG,
STATUS_ERR_VERIFY,
STATUS_ERR_ADDRESS,
STATUS_ERR_NOTDONE,
STATUS_ERR_FIRMWARE,
STATUS_ERR_VENDOR,
STATUS_ERR_USBR,
STATUS_ERR_POR,
STATUS_ERR_UNKNOWN,
STATUS_ERR_STALLEDPKT
} dfu_status;
/* DFU class-specific requests enumeration */
typedef enum {
DFU_DETACH = 0U,
DFU_DNLOAD,
DFU_UPLOAD,
DFU_GETSTATUS,
DFU_CLRSTATUS,
DFU_GETSTATE,
DFU_ABORT,
DFU_REQ_MAX
} dfu_requests;
#pragma pack(1)
/* USB DFU function descriptor struct */
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. If */
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 struct */
typedef struct
{
usb_desc_config config;
usb_desc_itf dfu_itf;
usb_desc_dfu_func dfu_func;
} usb_dfu_desc_config_set;
/* USB DFU handler struct */
typedef struct
{
uint8_t bStatus;
uint8_t bwPollTimeout0;
uint8_t bwPollTimeout1;
uint8_t bwPollTimeout2;
uint8_t bState;
uint8_t iString;
uint8_t manifest_state;
uint16_t data_len;
uint16_t block_num;
uint32_t base_addr;
uint8_t buf[TRANSFER_SIZE];
} usbd_dfu_handler;
typedef void (*app_func) (void);
extern usb_desc dfu_desc;
extern usb_class dfu_class;
#endif /* __DFU_CORE_H */
@@ -0,0 +1,674 @@
/*!
\file dfu_core.c
\brief USB DFU device class core functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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 "systick.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);
/* 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 dfu_mode_leave (usb_dev *udev);
static uint8_t dfu_getstatus_complete (usb_dev *udev);
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 */
usb_desc_dev dfu_dev_desc =
{
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
.bcdUSB = 0x0200U,
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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 */
usb_dfu_desc_config_set dfu_config_desc =
{
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = USB_DFU_CONFIG_DESC_SIZE,
.bNumInterfaces = 0x01U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0x80U,
.bMaxPower = 0x32U
},
.dfu_itf =
{
.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 = 0x00U
},
.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 = 0x011AU,
},
};
/* USB language ID descriptor */
static usb_desc_LANGID usbd_language_id_desc =
{
.header = {
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'D', 'F', 'U'}
};
/* USBD serial string */
static usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(2U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
/* USB config string */
static usb_desc_str config_string =
{
.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'}
};
static usb_desc_str interface_string =
{
.header =
{
.bLength = USB_STRING_LEN(15U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'@', 'I', 'n', 't', 'e', 'r', 'n', 'a', 'l', 'F', 'l', 'a', 's', 'h', ' ', '/', '0', 'x', '0', '8', '0', '0',
'0', '0', '0', '0', '/', '1', '6', '*', '0', '0', '1', 'K', 'a', ',', '4', '8', '*', '0', '0', '1', 'K', 'g'}
};
uint8_t* 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_ITF] = (uint8_t *)&interface_string
};
usb_desc dfu_desc = {
.dev_desc = (uint8_t *)&dfu_dev_desc,
.config_desc = (uint8_t *)&dfu_config_desc,
.strings = usbd_dfu_strings
};
usb_class dfu_class = {
.init = dfu_init,
.deinit = dfu_deinit,
.req_process = dfu_req_handler,
.ctlx_in = dfu_ctlx_in
};
/*!
\brief initialize the USB 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 usbd_dfu_handler dfu_handler;
/* unlock the internal flash */
fmc_unlock();
systick_config();
memset((void *)&dfu_handler, 0, sizeof(usbd_dfu_handler));
dfu_handler.base_addr = APP_LOADED_ADDR;
dfu_handler.manifest_state = MANIFEST_COMPLETE;
dfu_handler.bState = STATE_DFU_IDLE;
dfu_handler.bStatus = STATUS_OK;
udev->class_data[USBD_DFU_INTERFACE] = (void *)&dfu_handler;
return USBD_OK;
}
/*!
\brief de-initialize the USB 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->class_data[USBD_DFU_INTERFACE];
/* restore device default state */
memset(udev->class_data[USBD_DFU_INTERFACE], 0, sizeof(usbd_dfu_handler));
dfu->bState = STATE_DFU_IDLE;
dfu->bStatus = STATUS_OK;
/* lock the internal flash */
fmc_lock();
return USBD_OK;
}
/*!
\brief handle the USB 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 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->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);
fmc_page_erase(dfu->base_addr);
} else {
/* no operation */
}
} else {
/* no operation */
}
} else if (dfu->block_num > 1U) { /* regular download command */
/* preform the write operation */
uint32_t idx = 0U;
/* decode the required address */
addr = (dfu->block_num - 2U) * TRANSFER_SIZE + dfu->base_addr;
if (dfu->data_len & 0x03U) { /* not an aligned data */
for (idx = dfu->data_len; idx < ((dfu->data_len & 0xFFFCU) + 4U); idx++) {
dfu->buf[idx] = 0xFFU;
}
}
/* data received are word multiple */
for (idx = 0U; idx < dfu->data_len; idx += 4U) {
fmc_word_program(addr, *(uint32_t *)(dfu->buf + idx));
addr += 4U;
}
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->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 */
delay_1ms(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->transc_out[0];
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->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->xfer_len = dfu->data_len;
transc->xfer_buf = dfu->buf;
transc->xfer_count = 0U;
} 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->class_data[USBD_DFU_INTERFACE];
if(req->wLength <= 0U) {
dfu->bState = STATE_DFU_IDLE;
return;
}
usb_transc *transc = &udev->transc_in[0];
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->xfer_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 = (uint8_t *)(addr);
/* send the status data over EP0 */
transc->xfer_buf = phy_addr;
transc->xfer_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->transc_in[0];
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->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]) {
SET_POLLING_TIMEOUT(FLASH_ERASE_TIMEOUT);
} else {
SET_POLLING_TIMEOUT(FLASH_WRITE_TIMEOUT);
}
}
} 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->xfer_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->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->transc_in[0];
usbd_dfu_handler *dfu = (usbd_dfu_handler *)udev->class_data[USBD_DFU_INTERFACE];
/* send the current state of the DFU interface to host */
transc->xfer_buf = &(dfu->bState);
transc->xfer_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->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 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->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;
/* lock the internal flash */
fmc_lock();
/* generate system reset to allow jumping to the user code */
NVIC_SystemReset();
}
}
@@ -0,0 +1,67 @@
/*!
\file custom_hid_core.h
\brief definitions for HID core
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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
#define DESC_LEN_CONFIG 41U
#define MAX_PERIPH_NUM 4U
typedef struct {
uint8_t data[2];
uint8_t reportID;
uint8_t idlestate;
uint8_t 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 custom_hid_class;
/* 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, uint16_t len);
#endif /* __CUSTOM_HID_CORE_H */
@@ -0,0 +1,66 @@
/*!
\file standard_hid_core.h
\brief definitions for HID core
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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
#define USB_HID_REPORT_DESC_LEN 0x29U
typedef struct {
uint32_t protocol;
uint32_t idle_state;
uint8_t data[HID_IN_PACKET];
__IO uint8_t prev_transfer_complete;
} 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 hid_class;
/* function declarations */
/* register HID interface operation functions */
uint8_t hid_itfop_register (usb_dev *udev, hid_fop_handler *hid_fop);
/* send HID report */
uint8_t hid_report_send (usb_dev *udev, uint8_t *report, uint16_t len);
#endif /* __STANDARD_HID_CORE_H */
@@ -0,0 +1,69 @@
/*!
\file std_hid_mouse_core.h
\brief definitions for HID mouse core
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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 __STD_HID_MOUSE_CORE_H
#define __STD_HID_MOUSE_CORE_H
#include "usbd_enum.h"
#include "usb_hid.h"
#define USB_HID_CONFIG_DESC_LEN 0x22U
#define USB_HID_REPORT_DESC_LEN 0x34U
#define MOUSE_LEFT_BUTTON 0x01U
#define MOUSE_RIGHT_BUTTON 0x02U
typedef struct {
uint32_t protocol;
uint32_t idle_state;
uint8_t data[HID_IN_PACKET];
__IO uint8_t prev_transfer_complete;
} standard_mice_handler;
typedef struct {
void (*mice_itf_config) (void);
void (*mice_itf_data_process) (usb_dev *udev);
} mice_fop_handler;
extern usb_desc hid_mouse_desc;
extern usb_class hid_class;
/* function declarations */
/* register HID interface operation functions */
uint8_t hid_itfop_register (usb_dev *udev, mice_fop_handler *hid_fop);
/* send mouse report */
uint8_t hid_report_send (usb_dev *udev, uint8_t *report, uint16_t len);
#endif /* __STD_HID_MOUSE_CORE_H */
@@ -0,0 +1,83 @@
/*!
\file usb_hid.h
\brief definitions for the USB HID class
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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
#define USB_DESCTYPE_HID 0x21U
#define USB_DESCTYPE_REPORT 0x22U
/* HID subclass code */
#define USB_HID_SUBCLASS_BOOT_ITF 0x01U
/* HID protocol codes */
#define USB_HID_PROTOCOL_KEYBOARD 0x01U
#define USB_HID_PROTOCOL_MOUSE 0x02U
#define GET_REPORT 0x01U
#define GET_IDLE 0x02U
#define GET_PROTOCOL 0x03U
#define SET_REPORT 0x09U
#define SET_IDLE 0x0AU
#define SET_PROTOCOL 0x0BU
#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;
usb_desc_itf hid_itf;
usb_desc_hid hid_vendor;
usb_desc_ep hid_epin;
usb_desc_ep hid_epout;
} usb_hid_desc_config_set;
#endif /* __USB_HID_H */
@@ -0,0 +1,503 @@
/*!
\file custom_hid_core.c
\brief custom HID class driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_transc.h"
#include "custom_hid_core.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x128AU
/* Note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
usb_desc_dev custom_hid_dev_desc =
{
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV,
},
.bcdUSB = 0x0200U,
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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_hid_desc_config_set custom_hid_config_desc =
{
.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 usb_desc_LANGID usbd_language_id_desc =
{
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.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 usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
/* USB string descriptor set */
uint8_t* 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
};
/* 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 void custom_hid_data_in (usb_dev *udev, uint8_t ep_num);
static void custom_hid_data_out (usb_dev *udev, uint8_t ep_num);
usb_class custom_hid_class = {
.req_cmd = 0xFFU,
.init = custom_hid_init,
.deinit = custom_hid_deinit,
.req_process = custom_hid_req_handler,
.data_in = custom_hid_data_in,
.data_out = custom_hid_data_out
};
const uint8_t customhid_report_descriptor[DESC_LEN_REPORT] =
{
0x06, 0x00, 0xFF, /* USAGE_PAGE (Vendor Defined: 0xFF00) */
0x09, 0x00, /* USAGE (Custom Device) */
0xa1, 0x01, /* COLLECTION (Application) */
/* led 1 */
0x85, 0x11, /* REPORT_ID (0x11) */
0x09, 0x01, /* USAGE (LED 1) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
/* led 2 */
0x85, 0x12, /* REPORT_ID (0x12) */
0x09, 0x02, /* USAGE (LED 2) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
/* led 3 */
0x85, 0x13, /* REPORT_ID (0x13) */
0x09, 0x03, /* USAGE (LED 3) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
/* led 4 */
0x85, 0x14, /* REPORT_ID (0x14) */
0x09, 0x04, /* USAGE (LED 4) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
/* wakeup key */
0x85, 0x15, /* REPORT_ID (0x15) */
0x09, 0x05, /* USAGE (Push Button) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (Data,Var,Abs,Vol) */
0x75, 0x07, /* REPORT_SIZE (7) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs,Vol) */
/* tamper key */
0x85, 0x16, /* REPORT_ID (0x16) */
0x09, 0x06, /* USAGE (Push Button) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (Data,Var,Abs,Vol) */
0x75, 0x07, /* REPORT_SIZE (7) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs,Vol) */
0xc0 /* END_COLLECTION */
};
/*!
\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->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, uint16_t len)
{
usbd_ep_send (udev, CUSTOMHID_IN_EP, report, len);
return USBD_OK;
}
/*!
\brief initialize the HID device
\param[in] pudev: 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 custom_hid_handler hid_handler;
memset((void *)&hid_handler, 0, sizeof(custom_hid_handler));
/* initialize the data endpoints */
usbd_ep_init(udev, EP_BUF_SNG, HID_TX_ADDR, &(custom_hid_config_desc.hid_epin));
usbd_ep_init(udev, EP_BUF_SNG, HID_RX_ADDR, &(custom_hid_config_desc.hid_epout));
usbd_ep_recev (udev, CUSTOMHID_OUT_EP, hid_handler.data, 2U);
udev->ep_transc[EP_ID(CUSTOMHID_IN_EP)][TRANSC_IN] = custom_hid_class.data_in;
udev->ep_transc[EP_ID(CUSTOMHID_OUT_EP)][TRANSC_OUT] = custom_hid_class.data_out;
udev->class_data[CUSTOM_HID_INTERFACE] = (void *)&hid_handler;
if (udev->user_data != NULL) {
for (uint8_t i = 0U; i < MAX_PERIPH_NUM; i++) {
if (((hid_fop_handler *)udev->user_data)->periph_config[i] != NULL) {
((hid_fop_handler *)udev->user_data)->periph_config[i]();
}
}
}
return USBD_OK;
}
/*!
\brief de-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_deinit (usb_dev *udev, uint8_t config_index)
{
/* deinitialize HID endpoints */
usbd_ep_deinit(udev, CUSTOMHID_IN_EP);
usbd_ep_deinit(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)
{
uint8_t status = REQ_NOTSUPP;
custom_hid_handler *hid = (custom_hid_handler *)udev->class_data[CUSTOM_HID_INTERFACE];
switch (req->bRequest) {
case USB_GET_DESCRIPTOR:
if (USB_DESCTYPE_REPORT == (req->wValue >> 8)) {
usb_transc_config(&udev->transc_in[0],
(uint8_t *)customhid_report_descriptor,
USB_MIN(DESC_LEN_REPORT, req->wLength),
0U);
status = REQ_SUPP;
}
break;
case GET_REPORT:
if (2U == req->wLength) {
usb_transc_config(&udev->transc_in[0], hid->data, 2U, 0U);
status = REQ_SUPP;
}
break;
case GET_IDLE:
usb_transc_config(&udev->transc_in[0], (uint8_t *)&hid->idlestate, 1U, 0U);
status = REQ_SUPP;
break;
case GET_PROTOCOL:
usb_transc_config(&udev->transc_in[0], (uint8_t *)&hid->protocol, 1U, 0U);
status = REQ_SUPP;
break;
case SET_REPORT:
hid->reportID = (uint8_t)(req->wValue);
usb_transc_config(&udev->transc_out[0], hid->data, req->wLength, 0U);
status = REQ_SUPP;
break;
case SET_IDLE:
hid->idlestate = (uint8_t)(req->wValue >> 8);
status = REQ_SUPP;
break;
case SET_PROTOCOL:
hid->protocol = (uint8_t)(req->wValue);
status = REQ_SUPP;
break;
default:
break;
}
return status;
}
/*!
\brief handle custom HID data in transaction
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval none
*/
static void custom_hid_data_in (usb_dev *udev, uint8_t ep_num)
{
return;
}
/*!
\brief handle custom HID data out transaction
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval none
*/
static void custom_hid_data_out (usb_dev *udev, uint8_t ep_num)
{
custom_hid_handler *hid = (custom_hid_handler *)udev->class_data[CUSTOM_HID_INTERFACE];
if (CUSTOMHID_OUT_EP == ep_num){
switch (hid->data[0]){
case 0x11:
if (RESET != hid->data[1]) {
/* turn on led1 */
gd_eval_led_on(LED5);
} else {
gd_eval_led_off(LED5);
}
break;
case 0x12:
if (RESET != hid->data[1]) {
gd_eval_led_on(LED2);
} else {
gd_eval_led_off(LED2);
}
break;
case 0x13:
if (RESET != hid->data[1]) {
gd_eval_led_on(LED3);
} else {
gd_eval_led_off(LED3);
}
break;
case 0x14:
if (RESET != hid->data[1]) {
gd_eval_led_on(LED4);
} else {
gd_eval_led_off(LED4);
}
break;
default:
/* turn off all leds */
gd_eval_led_off(LED5);
gd_eval_led_off(LED2);
gd_eval_led_off(LED3);
gd_eval_led_off(LED4);
break;
}
usbd_ep_recev(udev, CUSTOMHID_IN_EP, hid->data, 2U);
}
}
@@ -0,0 +1,420 @@
/*!
\file standard_hid_core.c
\brief HID class driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_transc.h"
#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 */
usb_desc_dev hid_dev_desc =
{
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
#ifdef LPM_ENABLED
.bcdUSB = 0x0201U,
#else
.bcdUSB = 0x0200U,
#endif /* LPM_ENABLED */
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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
};
#ifdef LPM_ENABLED
/* BOS descriptor */
uint8_t USBD_BOSDesc[USB_BOS_DESC_LEN] =
{
0x05,
USB_DESCTYPE_BOS,
0x0C,
0x00,
0x01, /* 1 device capability descriptor */
/* device capability*/
0x07,
USB_DEVICE_CAPABITY,
0x02,
0x06, /* LPM capability bit set */
0x00,
0x00,
0x00
};
#endif /* LPM_ENABLED */
usb_hid_desc_config_set hid_config_desc =
{
.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 = 0x40U
}
};
/* USB language ID Descriptor */
static usb_desc_LANGID usbd_language_id_desc =
{
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.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 usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
uint8_t* 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 = {
#ifdef LPM_ENABLED
.bos_desc = (uint8_t *)&USBD_BOSDesc,
#endif /* LPM_ENABLED */
.dev_desc = (uint8_t *)&hid_dev_desc,
.config_desc = (uint8_t *)&hid_config_desc,
.strings = usbd_hid_strings
};
/* 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_handler (usb_dev *udev, usb_req *req);
static void hid_data_in_handler (usb_dev *udev, uint8_t ep_num);
usb_class hid_class = {
.init = hid_init,
.deinit = hid_deinit,
.req_process = hid_req_handler,
.data_in = hid_data_in_handler
};
const uint8_t hid_report_desc[USB_HID_REPORT_DESC_LEN] =
{
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x06, /* USAGE (Keyboard) */
0xa1, 0x01, /* COLLECTION (Application) */
0x05, 0x07, /* USAGE_PAGE (Keyboard/Keypad) */
0x19, 0xe0, /* USAGE_MINIMUM (Keyboard LeftControl) */
0x29, 0xe7, /* USAGE_MAXIMUM (Keyboard Right GUI) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x95, 0x08, /* REPORT_COUNT (8) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x81, 0x03, /* INPUT (Cnst,Var,Abs) */
0x95, 0x06, /* REPORT_COUNT (6) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x25, 0xFF, /* LOGICAL_MAXIMUM (255) */
0x19, 0x00, /* USAGE_MINIMUM (Reserved (no event indicated)) */
0x29, 0x65, /* USAGE_MAXIMUM (Keyboard Application) */
0x81, 0x00, /* INPUT (Data,Ary,Abs) */
0xc0 /* END_COLLECTION */
};
/*!
\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 hid_itfop_register (usb_dev *udev, hid_fop_handler *hid_fop)
{
if (NULL != hid_fop) {
udev->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, uint16_t len)
{
standard_hid_handler *hid = (standard_hid_handler *)udev->class_data[USBD_HID_INTERFACE];
/* check if USB is configured */
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)
{
static standard_hid_handler hid_handler;
memset((void *)&hid_handler, 0, sizeof(standard_hid_handler));
/* initialize Tx endpoint */
usbd_ep_init(udev, EP_BUF_SNG, INT_TX_ADDR, &(hid_config_desc.hid_epin));
udev->ep_transc[EP_ID(HID_IN_EP)][TRANSC_IN] = hid_class.data_in;
hid_handler.prev_transfer_complete = 1U;
udev->class_data[USBD_HID_INTERFACE] = (void *)&hid_handler;
if (NULL != udev->user_data) {
((hid_fop_handler *)udev->user_data)->hid_itf_config();
}
return USBD_OK;
}
/*!
\brief de-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_deinit (usb_dev *udev, uint8_t config_index)
{
/* deinitialize HID endpoints */
usbd_ep_deinit (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_handler (usb_dev *udev, usb_req *req)
{
uint8_t status = REQ_NOTSUPP;
standard_hid_handler *hid = (standard_hid_handler *)udev->class_data[USBD_HID_INTERFACE];
switch (req->bRequest) {
case GET_REPORT:
/* no use for this driver */
break;
case GET_IDLE:
usb_transc_config(&udev->transc_in[0U], (uint8_t *)&hid->idle_state, 1U, 0U);
status = REQ_SUPP;
break;
case GET_PROTOCOL:
usb_transc_config(&udev->transc_in[0U], (uint8_t *)&hid->protocol, 1U, 0U);
status = REQ_SUPP;
break;
case SET_REPORT:
/* no use for this driver */
break;
case SET_IDLE:
hid->idle_state = (uint8_t)(req->wValue >> 8);
status = REQ_SUPP;
break;
case SET_PROTOCOL:
hid->protocol = (uint8_t)(req->wValue);
status = REQ_SUPP;
break;
case USB_GET_DESCRIPTOR:
if (USB_DESCTYPE_REPORT == (req->wValue >> 8)) {
usb_transc_config(&udev->transc_in[0U],
(uint8_t *)hid_report_desc,
USB_MIN(USB_HID_REPORT_DESC_LEN, req->wLength),
0U);
status = REQ_SUPP;
}
break;
default:
break;
}
return status;
}
/*!
\brief handle data stage in DATA IN transaction
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval none
*/
static void hid_data_in_handler (usb_dev *udev, uint8_t ep_num)
{
standard_hid_handler *hid = (standard_hid_handler *)udev->class_data[USBD_HID_INTERFACE];
if (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;
}
}
@@ -0,0 +1,425 @@
/*!
\file std_hid_mouse_core.c
\brief HID class driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_transc.h"
#include "std_hid_mouse_core.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x0381U
/* Note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
usb_desc_dev hid_dev_desc =
{
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
#ifdef LPM_ENABLED
.bcdUSB = 0x0201U,
#else
.bcdUSB = 0x0200U,
#endif /* LPM_ENABLED */
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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
};
#ifdef LPM_ENABLED
/* BOS descriptor */
uint8_t USBD_BOSDesc[USB_BOS_DESC_LEN] =
{
0x05,
USB_DESCTYPE_BOS,
0x0C,
0x00,
0x01, /* 1 device capability descriptor */
/* device capability*/
0x07,
USB_DEVICE_CAPABITY,
0x02,
0x06, /* LPM capability bit set */
0x00,
0x00,
0x00
};
#endif /* LPM_ENABLED */
usb_hid_desc_config_set hid_config_desc =
{
.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_MOUSE,
.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
}
};
/* USB language ID Descriptor */
usb_desc_LANGID usbd_language_id_desc =
{
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.header =
{
.bLength = USB_STRING_LEN(14U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'M', 'o', 'u', 's', 'e'}
};
/* USBD serial string */
static usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
static uint8_t* 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_mouse_desc = {
.dev_desc = (uint8_t *)&hid_dev_desc,
.config_desc = (uint8_t *)&hid_config_desc,
.strings = usbd_hid_strings
};
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_handler (usb_dev *udev, usb_req *req);
static void hid_data_in_handler (usb_dev *udev, uint8_t ep_num);
usb_class hid_class = {
.init = hid_init,
.deinit = hid_deinit,
.req_process = hid_req_handler,
.data_in = hid_data_in_handler
};
const uint8_t hid_report_desc[USB_HID_REPORT_DESC_LEN] =
{
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x02, /* USAGE (Mouse) */
0xa1, 0x01, /* COLLECTION (Application) */
0x09, 0x01, /* USAGE (Pointer) */
0xa1, 0x00, /* COLLECTION (Physical) */
0x05, 0x09, /* USAGE_PAGE (Button) */
0x19, 0x01, /* USAGE_MINIMUM (1) */
0x29, 0x03, /* USAGE_MAXIMUM (3) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
0x95, 0x03, /* REPORT_COUNT (3) */
0x75, 0x01, /* REPORT_SIZE (1) */
0x81, 0x02, /* INPUT (Data,Var,Abs) */
0x95, 0x01, /* REPORT_COUNT (1) */
0x75, 0x05, /* REPORT_SIZE (5) */
0x81, 0x01, /* INPUT (Cnst,Var,Abs) */
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x30, /* USAGE (X) */
0x09, 0x31, /* USAGE (Y) */
0x09, 0x38, /* USAGE (Wheel) */
0x15, 0x81, /* LOGICAL_MINIMUM (-127) */
0x25, 0x7F, /* LOGICAL_MAXIMUM (127) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x03, /* REPORT_COUNT (3) */
0x81, 0x06, /* INPUT (Data,Var,Rel) */
0xc0, /* END_COLLECTION */
0xc0 /* END_COLLECTION */
};
/*!
\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 hid_itfop_register (usb_dev *udev, mice_fop_handler *hid_fop)
{
if (NULL != hid_fop) {
udev->user_data = (void *)hid_fop;
return USBD_OK;
}
return USBD_FAIL;
}
/*!
\brief send mouse 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, uint16_t len)
{
standard_mice_handler *hid = (standard_mice_handler *)udev->class_data[USBD_HID_INTERFACE];
if(udev->cur_status == USBD_CONFIGURED){
if(1U == hid->prev_transfer_complete){
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)
{
static standard_mice_handler mice_handler;
memset((void *)&mice_handler, 0, sizeof(standard_mice_handler));
/* initialize Tx endpoint */
usbd_ep_init(udev, EP_BUF_SNG, INT_TX_ADDR, &(hid_config_desc.hid_epin));
udev->ep_transc[EP_ID(HID_IN_EP)][TRANSC_IN] = hid_class.data_in;
mice_handler.prev_transfer_complete = 1U;
udev->class_data[USBD_HID_INTERFACE] = (void *)&mice_handler;
if (NULL != udev->user_data) {
((mice_fop_handler *)(udev->user_data))->mice_itf_config();
}
return USBD_OK;
}
/*!
\brief de-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_deinit (usb_dev *udev, uint8_t config_index)
{
/* deinitialize HID endpoints */
usbd_ep_deinit (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_handler (usb_dev *udev, usb_req *req)
{
uint8_t status = REQ_NOTSUPP;
standard_mice_handler *hid = (standard_mice_handler *)udev->class_data[USBD_HID_INTERFACE];
switch (req->bRequest) {
case GET_REPORT:
/* no use for this driver */
break;
case GET_IDLE:
usb_transc_config(&udev->transc_in[0U], (uint8_t *)&hid->idle_state, 1U, 0U);
status = REQ_SUPP;
break;
case GET_PROTOCOL:
usb_transc_config(&udev->transc_in[0], (uint8_t *)&hid->protocol, 1U, 0U);
status = REQ_SUPP;
break;
case SET_REPORT:
/* no use for this driver */
break;
case SET_IDLE:
hid->idle_state = (uint8_t)(req->wValue >> 8);
status = REQ_SUPP;
break;
case SET_PROTOCOL:
hid->protocol = (uint8_t)(req->wValue);
status = REQ_SUPP;
break;
case USB_GET_DESCRIPTOR:
if (USB_DESCTYPE_REPORT == (req->wValue >> 8)) {
usb_transc_config(&udev->transc_in[0U],
(uint8_t *)hid_report_desc,
USB_MIN(USB_HID_REPORT_DESC_LEN, req->wLength),
0U);
status = REQ_SUPP;
}
break;
default:
break;
}
return status;
}
/*!
\brief handle data stage in DATA IN transaction
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint identifier
\param[out] none
\retval none
*/
static void hid_data_in_handler (usb_dev *udev, uint8_t ep_num)
{
standard_mice_handler *hid = (standard_mice_handler *)udev->class_data[USBD_HID_INTERFACE];
if(hid->data[0] == 0U){
hid->prev_transfer_complete = 1U;
}else{
hid->data[0] = 0U;
usbd_ep_send(udev, HID_IN_EP, hid->data, HID_IN_PACKET);
}
}
@@ -0,0 +1,82 @@
/*!
\file usb_iap_core.h
\brief the header file of IAP driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_DESC_LEN_IAP_REPORT 35U
#define USB_DESC_LEN_IAP_CONFIG_SET 41U
/* special commands with download request */
#define IAP_OPTION_BYTE 0x01U
#define IAP_ERASE 0x02U
#define IAP_DNLOAD 0x03U
#define IAP_LEAVE 0x04U
#define IAP_GETBIN_ADDRESS 0x05U
typedef void (*app_func) (void);
typedef struct
{
uint8_t report_buf[IAP_OUT_PACKET + 1U];
uint8_t option_byte[IAP_IN_PACKET];
/* state machine variables */
uint8_t dev_status[IAP_IN_PACKET];
uint8_t bin_addr[IAP_IN_PACKET];
uint8_t reportID;
uint8_t flag;
uint32_t protocol;
uint32_t idlestate;
uint16_t transfer_times;
uint16_t page_count;
uint16_t lps; /* last packet size */
uint32_t file_length;
uint32_t base_address;
} usbd_iap_handler;
extern usb_desc iap_desc;
extern usb_class iap_class;
/* function declarations */
/* send IAP report */
uint8_t iap_report_send(usb_dev *udev, uint8_t *report, uint16_t len);
#endif /* __USB_IAP_CORE_H */
@@ -0,0 +1,594 @@
/*!
\file usb_iap_core.c
\brief IAP driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_transc.h"
#include "usb_iap_core.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x028BU
/* Note:it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
usb_desc_dev iap_dev_desc =
{
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
.bcdUSB = 0x0200U,
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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_hid_desc_config_set iap_config_desc =
{
.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 = 0x01U,
.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 */
usb_desc_LANGID usbd_language_id_desc =
{
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'I', 'A', 'P'}
};
/* USBD serial string */
static usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(2U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
uint8_t* 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 void iap_data_out (usb_dev *udev, uint8_t ep_num);
usb_class iap_class = {
.init = iap_init,
.deinit = iap_deinit,
.req_process = iap_req_handler,
.data_out = iap_data_out
};
/* USB custom HID device report descriptor */
const uint8_t iap_report_desc[USB_DESC_LEN_IAP_REPORT] =
{
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
0x09, 0x00, /* USAGE (Custom Device) */
0xa1, 0x01, /* COLLECTION (Application) */
/* IAP command and data */
0x85, 0x01, /* REPORT_ID (0x01) */
0x09, 0x01, /* USAGE (IAP command) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0xff, /* LOGICAL_MAXIMUM (255) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x3f, /* REPORT_COUNT (63) */
0x91, 0x82, /* OUTPUT (Data,Var,Abs,Vol) */
/* device status and option byte */
0x85, 0x02, /* REPORT_ID (0x02) */
0x09, 0x02, /* USAGE (Status and option byte) */
0x15, 0x00, /* LOGICAL_MINIMUM (0) */
0x25, 0xff, /* LOGICAL_MAXIMUM (255) */
0x75, 0x08, /* REPORT_SIZE (8) */
0x95, 0x10, /* REPORT_COUNT (16) */
0x81, 0x82, /* INPUT (Data,Var,Abs,Vol) */
0xc0 /* END_COLLECTION */
};
/* IAP requests management functions */
static void iap_req_erase (usb_dev *udev);
static void iap_req_dnload (usb_dev *udev);
static void iap_req_optionbyte(usb_dev *udev);
static void iap_req_leave (usb_dev *udev);
static void iap_address_send (usb_dev *udev);
static void iap_data_write (uint8_t *data, uint32_t addr, uint32_t len);
/*!
\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 iap_init (usb_dev *udev, uint8_t config_index)
{
static usbd_iap_handler iap_handler;
/* initialize Tx endpoint */
usbd_ep_init(udev, EP_BUF_SNG, INT_TX_ADDR, &(iap_config_desc.hid_epin));
/* initialize Rx endpoint */
usbd_ep_init(udev, EP_BUF_SNG, INT_RX_ADDR, &(iap_config_desc.hid_epout));
/* unlock the internal flash */
fmc_unlock();
memset((void *)&iap_handler, 0, sizeof(usbd_iap_handler));
/* prepare receive Data */
usbd_ep_recev(udev, IAP_OUT_EP, iap_handler.report_buf, IAP_OUT_PACKET);
udev->ep_transc[EP_ID(IAP_OUT_EP)][TRANSC_OUT] = iap_class.data_out;
iap_handler.base_address = APP_LOADED_ADDR;
udev->class_data[USBD_IAP_INTERFACE] = (void *)&iap_handler;
return USBD_OK;
}
/*!
\brief de-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 iap_deinit (usb_dev *udev, uint8_t config_index)
{
/* deinitialize HID endpoints */
usbd_ep_deinit (udev, IAP_IN_EP);
usbd_ep_deinit (udev, IAP_OUT_EP);
/* lock the internal flash */
fmc_lock();
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 iap_req_handler (usb_dev *udev, usb_req *req)
{
uint8_t status = REQ_NOTSUPP;
usbd_iap_handler *iap = (usbd_iap_handler *)udev->class_data[USBD_IAP_INTERFACE];
switch (req->bRequest) {
case GET_REPORT:
/* no use for this driver */
break;
case GET_IDLE:
usb_transc_config(&udev->transc_in[0], (uint8_t *)&iap->idlestate, 1U, 0U);
status = REQ_SUPP;
break;
case GET_PROTOCOL:
usb_transc_config(&udev->transc_in[0], (uint8_t *)&iap->protocol, 1U, 0U);
status = REQ_SUPP;
break;
case SET_REPORT:
iap->reportID = (uint8_t)(req->wValue);
usb_transc_config(&udev->transc_out[0], iap->report_buf, req->wLength, 0U);
status = REQ_SUPP;
break;
case SET_IDLE:
iap->idlestate = (uint8_t)(req->wValue >> 8);
status = REQ_SUPP;
break;
case SET_PROTOCOL:
iap->protocol = (uint8_t)(req->wValue);
status = REQ_SUPP;
break;
case USB_GET_DESCRIPTOR:
if (USB_DESCTYPE_REPORT == (req->wValue >> 8)) {
usb_transc_config(&udev->transc_in[0],
(uint8_t *)iap_report_desc,
USB_MIN(USB_DESC_LEN_IAP_REPORT, req->wLength),
0U);
return REQ_SUPP;
}
break;
default:
break;
}
return status;
}
/*!
\brief handle data out stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval none
*/
static void iap_data_out (usb_dev *udev ,uint8_t ep_num)
{
usbd_iap_handler *iap = (usbd_iap_handler *)udev->class_data[USBD_IAP_INTERFACE];
if (0x01U == iap->report_buf[0]) {
switch(iap->report_buf[1]) {
case IAP_DNLOAD:
iap_req_dnload(udev);
break;
case IAP_ERASE:
iap_req_erase(udev);
break;
case IAP_OPTION_BYTE:
iap_req_optionbyte(udev);
break;
case IAP_LEAVE:
iap_req_leave(udev);
break;
case IAP_GETBIN_ADDRESS:
iap_address_send(udev);
break;
default:
break;
}
}
usbd_ep_recev(udev, IAP_OUT_EP, iap->report_buf, IAP_OUT_PACKET);
}
/*!
\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, uint16_t len)
{
usbd_ep_send (udev, IAP_IN_EP, report, len);
return USBD_OK;
}
/*!
\brief handle the IAP_DNLOAD request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_req_dnload (usb_dev *udev)
{
usbd_iap_handler *iap = (usbd_iap_handler *)udev->class_data[USBD_IAP_INTERFACE];
if (0U != iap->transfer_times) {
if (1U == iap->transfer_times) {
if (0U == iap->lps) {
iap_data_write(&iap->report_buf[2], iap->base_address, TRANSFER_SIZE);
} else {
iap_data_write(&iap->report_buf[2], iap->base_address, iap->file_length % TRANSFER_SIZE);
iap->lps = 0U;
}
iap->dev_status[0] = 0x02U;
iap->dev_status[1] = 0x02U;
iap_report_send (udev, iap->dev_status, IAP_IN_PACKET);
} else {
iap_data_write(&iap->report_buf[2], iap->base_address, TRANSFER_SIZE);
iap->base_address += TRANSFER_SIZE;
}
iap->transfer_times--;
}
}
/*!
\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 i, addr = 0U;
usbd_iap_handler *iap = (usbd_iap_handler *)udev->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;
iap->page_count = iap->report_buf[6];
/* get file length */
iap->file_length = iap->report_buf[7];
iap->file_length |= (uint32_t)iap->report_buf[8] << 8;
iap->file_length |= (uint32_t)iap->report_buf[9] << 16;
iap->file_length |= (uint32_t)iap->report_buf[10] << 24;
/* compute last packet size and transfer times */
iap->lps = iap->file_length % TRANSFER_SIZE;
if (0U == iap->lps) {
iap->transfer_times = (uint16_t)iap->file_length / TRANSFER_SIZE;
} else {
iap->transfer_times = (uint16_t)iap->file_length / TRANSFER_SIZE + 1U;
}
/* check if the address is in protected area */
if (IS_PROTECTED_AREA(iap->base_address)) {
return;
}
addr = iap->base_address;
for (i = 0U; i < iap->page_count; i ++) {
/* call the standard flash erase-page function */
fmc_page_erase(addr);
addr += PAGE_SIZE;
}
iap->dev_status[0] = 0x02U;
iap->dev_status[1] = 0x01U;
usbd_ep_send(udev, IAP_IN_EP, iap->dev_status, IAP_IN_PACKET);
}
/*!
\brief handle the IAP_OPTION_BYTE request
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void iap_req_optionbyte (usb_dev *udev)
{
uint8_t i = 0U;
uint32_t address = 0x1FFFF800U;
usbd_iap_handler *iap = (usbd_iap_handler *)udev->class_data[USBD_IAP_INTERFACE];
iap->option_byte[0] = 0x02U;
for (i = 1U; i < 17U; i++) {
iap->option_byte[i] = *(uint8_t *)address;
address++;
}
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)
{
/* lock the internal flash */
fmc_lock();
/* generate system reset to allow jumping to the user code */
NVIC_SystemReset();
}
/*!
\brief handle the IAP_SEND_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->class_data[USBD_IAP_INTERFACE];
iap->bin_addr[0] = 0x02U;
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 write data to sectors of memory
\param[in] data: data to be written
\param[in] addr: sector address/code
\param[in] len: length of data to be written (in bytes)
\param[out] none
\retval MAL_OK if all operations are OK, MAL_FAIL else
*/
static void iap_data_write (uint8_t *data, uint32_t addr, uint32_t len)
{
uint32_t index = 0U;
/* check if the address is in protected area */
if (IS_PROTECTED_AREA(addr)) {
return;
}
if (len & 0x03U) {/* not an aligned data */
for (index = len; index < ((len & 0xFFFCU) + 4U); index++) {
data[index] = 0xFFU;
}
}
/* data received are word multiple */
for (index = 0U; index < len; index += 4U) {
fmc_word_program(addr, *(uint32_t *)(data + index));
addr += 4U;
}
}
@@ -0,0 +1,132 @@
/*!
\file msc_bbb_scsi.h
\brief the header file of the msc_bbb_scsi.c
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_SCSI_H
#define __MSC_BBB_SCSI_H
#include "usbd_enum.h"
/* signature of command block wrapper (CBW) */
#define BBB_CBW_SIGNATURE 0x43425355U
#define BBB_CBW_LENGTH 31U
/* signature of command status wrapper (CSW) */
#define BBB_CSW_SIGNATURE 0x53425355U
#define BBB_CSW_LENGTH 13U
#define SCSI_CMD_LENGTH 16U
/* transport stage define */
enum msc_stage {
MSC_STAGE_CMD = 0U,
MSC_STAGE_DATA_IN,
MSC_STAGE_DATA_OUT,
MSC_STAGE_STATUS
};
/* SCSI command define */
#define INQUIRY 0x12U
#define READ_FORMAT_CAPACITIES 0x23U
#define READ_CAPACITY 0x25U
#define READ_10 0x28U
#define WRITE_10 0x2AU
#define REQUEST_SENSE 0x03U
#define TEST_UNIT_READY 0x00U
#define SCSI_MODE_SENSE6 0x1AU
#define SCSI_READ_TOC_DATA 0x43U
#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1EU
#define SCSI_START_STOP_UNIT 0x1BU
/* the array length define */
#define DATA_LEN_DISK_INQUIRY 36U
#define DATA_LEN_FORMAT_CAPACITIES 12U
#define DATA_LEN_READ_CAPACITIES 8U
#define DATA_LEN_REQUEST_SENSE 18U
#define DATA_LEN_SENSE6 8U
typedef struct {
uint32_t dCBWSignature; /*!< signature of a CBW */
uint32_t dCBWTag; /*!< a command block tag sent by the host */
uint32_t dCBWDataTransferLength; /*!< the number of bytes of data stage */
uint8_t bmCBWFlags; /*!< maybe ignored or include direction */
uint8_t bCBWLUN; /*!< the device logical unit number (LUN) */
uint8_t bCBWCBLength; /*!< the valid length of the CBWCB in bytes */
uint8_t CBWCB[16]; /*!< the command block to be executed by the device */
} msc_bbb_cbw;
typedef struct {
uint32_t dCSWSignature; /*!< signature of a CSW */
uint32_t dCSWTag; /*!< must be same as CBW */
uint32_t dCSWDataResidue; /*!< the difference between the amount of data and the actual amount */
uint8_t bCSWStatus; /*!< indicates the success or failure of the command */
} msc_bbb_csw;
typedef struct
{
/* the array to store the data from or to host in read10 or write10 command */
uint8_t bbb_data[MSC_MEDIA_PACKET_SIZE];
/* the array to store the SCSI command */
uint8_t scsi_cmd[SCSI_CMD_LENGTH];
/* the global data to store the transport stage in BBB protocol */
uint8_t bbb_transport_stage;
/* the data that need to return the host when the command is not valid */
uint8_t error_data;
uint8_t usbd_msc_maxlun;
msc_bbb_cbw bbb_cbw;
msc_bbb_csw bbb_csw;
uint32_t data_len;
uint32_t block_addr;
uint32_t block_len;
uint32_t disk_pop;
/* USB mass storage read capacities data */
uint8_t read_capa_data[DATA_LEN_READ_CAPACITIES];
} usbd_msc_handler;
/* function declarations */
/* parse the SCSI commands from the CBW */
void scsi_command_parse (usb_dev *udev);
/* report array to host */
void bbb_report_array_to_host (usb_dev *udev, uint8_t *data, uint32_t report_len);
/* process the SCSI command */
void process_scsi_command (uint8_t lun, usb_dev *udev);
#endif /* __MSC_BBB_SCSI_H */
@@ -0,0 +1,86 @@
/*!
\file msc_core.h
\brief the header file of USB MSC device class core functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_CORE_H
#define __MSC_CORE_H
#include "usbd_core.h"
#include "msc_bbb_scsi.h"
/* mass storage device class code */
#define USB_CLASS_MSC 0x08U
/* mass storage subclass code */
#define USB_MSC_SUBCLASS_RBC 0x01U
#define USB_MSC_SUBCLASS_ATAPI 0x02U
#define USB_MSC_SUBCLASS_UFI 0x04U
#define USB_MSC_SUBCLASS_SCSI 0x06U
#define USB_MSC_SUBCLASS_LOCKABLE 0x07U
#define USB_MSC_SUBCLASS_IEEE1667 0x08U
/* mass storage interface class control protocol codes */
#define USB_MSC_PROTOCOL_CBI 0x00U
#define USB_MSC_PROTOCOL_CBI_ALT 0x01U
#define USB_MSC_PROTOCOL_BBB 0x50U
/* mass storage request codes */
#define USB_MSC_REQ_CODES_ADSC 0x00U
#define USB_MSC_REQ_CODES_GET 0xFCU
#define USB_MSC_REQ_CODES_PUT 0xFDU
#define USB_MSC_REQ_CODES_GML 0xFEU
#define USB_MSC_REQ_CODES_BOMSR 0xFFU
/* mass storage class-specific request codes */
#define BBB_GET_MAX_LUN 0xFEU
#define BBB_RESET 0xFFU
#define MSC_EPIN_SIZE MSC_DATA_PACKET_SIZE
#define MSC_EPOUT_SIZE MSC_DATA_PACKET_SIZE
#define USB_MSC_CONFIG_DESC_LEN 32U
/* USB configuration descriptor structure */
typedef struct
{
usb_desc_config config;
usb_desc_itf msc_itf;
usb_desc_ep msc_epin;
usb_desc_ep msc_epout;
} usb_msc_desc_config_set;
extern usb_desc msc_desc;
extern usb_class msc_class;
#endif /* __MSC_CORE_H */
@@ -0,0 +1,60 @@
/*!
\file msc_mem.h
\brief the header file of msc_mem.c
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_MEM_H
#define __MSC_MEM_H
#include "usbd_conf.h"
#define USBD_STD_INQUIRY_LENGTH 36U
#define USBD_STD_READ_TOC_CMD_LENGTH 20U
typedef struct
{
int8_t (*mem_init) (uint8_t lun);
int8_t (*mem_ready) (uint8_t lun);
int8_t (*mem_protected) (uint8_t lun);
int8_t (*mem_read) (uint8_t lun, uint8_t *buf, uint32_t block_addr, uint16_t block_len);
int8_t (*mem_write) (uint8_t lun, uint8_t *buf, uint32_t block_addr, uint16_t block_len);
int8_t (*mem_maxlun) (void);
uint8_t *mem_toc_data;
uint8_t *mem_inquiry_data[MEM_LUN_NUM];
uint32_t mem_block_size[MEM_LUN_NUM];
uint32_t mem_block_len[MEM_LUN_NUM];
} usbd_mem_cb;
extern usbd_mem_cb *usbd_mem_fops;
#endif /* __MSC_MEM_H */
@@ -0,0 +1,420 @@
/*!
\file bbb_scsi_driver.c
\brief USB BBB and SCSI protocol driver functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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 "msc_bbb_scsi.h"
#include "msc_mem.h"
/* USB mass storage format capacities data */
uint8_t format_capa_data[DATA_LEN_FORMAT_CAPACITIES] =
{
0x00,
0x00,
0x00, /* reserved */
0x08, /* capacity list length */
0x00,
0x00,
0x00,
0x00,
0x02, /* bit0-bit1:descriptor code */
0x00,
0x00,
0x00
};
/* USB mass storage request sense data */
uint8_t request_sense_data[DATA_LEN_REQUEST_SENSE] =
{
0x70, /* bit0-bit6:error code (70h), bit7:valid */
0x00, /* reserved */
0x05, /* bit0-bit3:sense key */
0x00, 0x00, 0x00, 0x00, /* information (MSB) */
0x0A, /* additional sense length (10) */
0x00, 0x00, 0x00, 0x00, /* reserved */
0x20, /* additional sense code (mandatory) */
0x00, /* additional sense code qualifier (mandatory) */
0x00, 0x00, 0x00, 0x00 /* reserved */
};
/* USB mass storage sense 6 data */
uint8_t sense6_data[DATA_LEN_SENSE6] =
{
0x03, /* mode data length */
0x00, /* medium type */
0x00, /* device specific parameter, bit7: 0(write and read), 1(read only) */
0x00, /* block descriptor length */
0x00, /* reserved */
0x00, /* reserved */
0x00, /* reserved */
0x00 /* reserved */
};
/* local function prototypes ('static') */
static void bbb_set_csw (usb_dev *udev, uint32_t csw_tag, uint32_t csw_data_residue, uint8_t csw_status);
/*!
\brief parse the SCSI commands from the CBW
\param[in] none
\param[out] none
\retval none
*/
void scsi_command_parse (usb_dev *udev)
{
uint8_t i;
usbd_msc_handler *msc = (usbd_msc_handler *)udev->class_data[USBD_MSC_INTERFACE];
for(i = 0U; i < SCSI_CMD_LENGTH; i++){
msc->scsi_cmd[i] = msc->bbb_cbw.CBWCB[i];
}
}
/*!
\brief set the CSW structure
\param[in] csw_tag: signature that helps identify this data packet as a csw
\param[in] csw_data_residue: the device shall set this field to the value received in the cbwtag of the associated bbb_cbw
\param[in] csw_status: for data-out: the difference between the receive data and dCBWDataTransferLength
for data-in: the difference between the transmit data and dCBWDataTransferLength
\param[out] none
\retval none
*/
static void bbb_set_csw (usb_dev *udev, uint32_t csw_tag, uint32_t csw_data_residue, uint8_t csw_status)
{
usbd_msc_handler *msc = (usbd_msc_handler *)udev->class_data[USBD_MSC_INTERFACE];
msc->bbb_csw = (msc_bbb_csw) {
.dCSWSignature = BBB_CSW_SIGNATURE,
.dCSWTag = csw_tag,
.dCSWDataResidue = csw_data_residue,
.bCSWStatus = csw_status
};
}
/*!
\brief report array to host
\param[in] udev: pointer to device instance
\param[in] data: pointer to the send array
\param[in] report_len: the report length
\param[out] none
\retval none
*/
void bbb_report_array_to_host (usb_dev *udev, uint8_t *data, uint32_t report_len)
{
usbd_msc_handler *msc = (usbd_msc_handler *)udev->class_data[USBD_MSC_INTERFACE];
switch (msc->bbb_transport_stage) {
case MSC_STAGE_CMD:
msc->bbb_transport_stage = MSC_STAGE_DATA_IN;
msc->data_len = msc->bbb_cbw.dCBWDataTransferLength;
if(msc->data_len > report_len){
msc->data_len = report_len;
}
usbd_ep_send (udev, MSC_IN_EP, data, (uint16_t)msc->data_len);
break;
case MSC_STAGE_DATA_IN:
bbb_set_csw(udev, msc->bbb_cbw.dCBWTag, msc->bbb_cbw.dCBWDataTransferLength - msc->data_len, 0U);
msc->data_len = BBB_CSW_LENGTH;
msc->bbb_transport_stage = MSC_STAGE_STATUS;
usbd_ep_send (udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, (uint16_t)msc->data_len);
break;
case MSC_STAGE_STATUS:
msc->bbb_transport_stage = MSC_STAGE_CMD;
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
break;
default:
break;
}
}
/*!
\brief process the scsi command
\param[in] lun: logical block number
\param[in] udev: pointer to device instance
\param[out] none
\retval none
*/
void process_scsi_command (uint8_t lun, usb_dev *udev)
{
uint32_t l_csw_data_residue = 0U;
usbd_msc_handler *msc = (usbd_msc_handler *)udev->class_data[USBD_MSC_INTERFACE];
switch(msc->scsi_cmd[0]){
/* process the INQUIRY command */
case INQUIRY:
bbb_report_array_to_host(udev, usbd_mem_fops->mem_inquiry_data[lun], DATA_LEN_DISK_INQUIRY);
break;
/* process the TEST_UNIT_READY command */
case TEST_UNIT_READY:
if(msc->disk_pop == 1U){
gpio_bit_reset(USB_PULLUP, USB_PULLUP_PIN);
}
if (MSC_STAGE_CMD == msc->bbb_transport_stage) {
msc->bbb_transport_stage = MSC_STAGE_STATUS;
bbb_set_csw(udev, msc->bbb_cbw.dCBWTag, 0U, 0U);
usbd_ep_send (udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
} else {
msc->bbb_transport_stage = MSC_STAGE_CMD;
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
}
break;
/* process the SCSI_START_STOP_UNIT command */
case SCSI_START_STOP_UNIT:
msc->disk_pop = 1U;
if (MSC_STAGE_CMD == msc->bbb_transport_stage) {
msc->bbb_transport_stage = MSC_STAGE_STATUS;
bbb_set_csw(udev, msc->bbb_cbw.dCBWTag, 0U, 0U);
usbd_ep_send (udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
} else {
msc->bbb_transport_stage = MSC_STAGE_CMD;
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
}
break;
/* process the SCSI_ALLOW_MEDIUM_REMOVAL command */
case SCSI_ALLOW_MEDIUM_REMOVAL:
if (MSC_STAGE_CMD == msc->bbb_transport_stage) {
msc->bbb_transport_stage = MSC_STAGE_STATUS;
bbb_set_csw(udev, msc->bbb_cbw.dCBWTag, 0U, 0U);
usbd_ep_send (udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
} else {
msc->bbb_transport_stage = MSC_STAGE_CMD;
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
}
break;
/* process the READ_FORMAT_CAPACITIES command */
case READ_FORMAT_CAPACITIES:
format_capa_data[4] = (uint8_t)((usbd_mem_fops->mem_block_len[lun] - 1U) >> 24);
format_capa_data[5] = (uint8_t)((usbd_mem_fops->mem_block_len[lun] - 1U) >> 16);
format_capa_data[6] = (uint8_t)((usbd_mem_fops->mem_block_len[lun] - 1U) >> 8);
format_capa_data[7] = (uint8_t)((usbd_mem_fops->mem_block_len[lun] - 1U) >> 0);
format_capa_data[9] = (uint8_t)((usbd_mem_fops->mem_block_size[lun]) >> 16);
format_capa_data[10] = (uint8_t)((usbd_mem_fops->mem_block_size[lun]) >> 8);
format_capa_data[11] = (uint8_t)((usbd_mem_fops->mem_block_size[lun]) >> 0);
bbb_report_array_to_host(udev, format_capa_data, DATA_LEN_FORMAT_CAPACITIES);
break;
/* process the READ_CAPACITY command */
case READ_CAPACITY:
msc->read_capa_data[0] = (uint8_t)((usbd_mem_fops->mem_block_len[lun] - 1U) >> 24);
msc->read_capa_data[1] = (uint8_t)((usbd_mem_fops->mem_block_len[lun] - 1U) >> 16);
msc->read_capa_data[2] = (uint8_t)((usbd_mem_fops->mem_block_len[lun] - 1U) >> 8);
msc->read_capa_data[3] = (uint8_t)((usbd_mem_fops->mem_block_len[lun] - 1U) >> 0);
msc->read_capa_data[4] = (uint8_t)((usbd_mem_fops->mem_block_size[lun]) >> 24);
msc->read_capa_data[5] = (uint8_t)((usbd_mem_fops->mem_block_size[lun]) >> 16);
msc->read_capa_data[6] = (uint8_t)((usbd_mem_fops->mem_block_size[lun]) >> 8);
msc->read_capa_data[7] = (uint8_t)((usbd_mem_fops->mem_block_size[lun]) >> 0);
bbb_report_array_to_host(udev, msc->read_capa_data, DATA_LEN_READ_CAPACITIES);
break;
/* process the READ_10 command */
case READ_10:
/* read the first block */
if (MSC_STAGE_CMD == msc->bbb_transport_stage) {
msc->bbb_transport_stage = MSC_STAGE_DATA_IN;
msc->block_addr = ((uint32_t)msc->scsi_cmd[2] << 24) | \
((uint32_t)msc->scsi_cmd[3] << 16) | \
((uint32_t)msc->scsi_cmd[4] << 8) | \
(uint32_t)msc->scsi_cmd[5];
msc->block_len = ((uint32_t)msc->scsi_cmd[7] << 8) | (uint32_t)msc->scsi_cmd[8];
msc->block_addr *= usbd_mem_fops->mem_block_size[lun];
msc->block_len *= usbd_mem_fops->mem_block_size[lun];
usbd_mem_fops->mem_read(lun, msc->bbb_data, msc->block_addr, 1U);
usbd_ep_send (udev, MSC_IN_EP, msc->bbb_data, (uint16_t)usbd_mem_fops->mem_block_size[lun]);
msc->block_addr += usbd_mem_fops->mem_block_size[lun];
msc->block_len -= usbd_mem_fops->mem_block_size[lun];
} else if (MSC_STAGE_DATA_IN == msc->bbb_transport_stage){
/* end of the read, send bbb_csw to host */
if (0U == msc->block_len) {
msc->bbb_transport_stage = MSC_STAGE_STATUS;
l_csw_data_residue = msc->bbb_cbw.dCBWDataTransferLength - \
(((uint32_t)msc->scsi_cmd[7] << 8) | (uint32_t)msc->scsi_cmd[8]) * usbd_mem_fops->mem_block_size[lun];
bbb_set_csw(udev, msc->bbb_cbw.dCBWTag, l_csw_data_residue, 0U);
usbd_ep_send (udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
/* read the nest block */
} else {
usbd_mem_fops->mem_read(lun, msc->bbb_data, msc->block_addr, 1U);
usbd_ep_send (udev, MSC_IN_EP, msc->bbb_data, (uint16_t)usbd_mem_fops->mem_block_size[lun]);
msc->block_addr += usbd_mem_fops->mem_block_size[lun];
msc->block_len -= usbd_mem_fops->mem_block_size[lun];
}
/* enter the MSC_STAGE_CMD */
} else if (MSC_STAGE_STATUS == msc->bbb_transport_stage) {
msc->bbb_transport_stage = MSC_STAGE_CMD;
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
} else {
/* no operation */
}
break;
/* process the WRITE_10 command */
case WRITE_10:
/* write the first block */
if (MSC_STAGE_CMD == msc->bbb_transport_stage) {
msc->bbb_transport_stage = MSC_STAGE_DATA_OUT;
msc->block_addr = ((uint32_t)msc->scsi_cmd[2] << 24) | \
((uint32_t)msc->scsi_cmd[3] << 16) | \
((uint32_t)msc->scsi_cmd[4] << 8) | \
(uint32_t)msc->scsi_cmd[5];
msc->block_len = ((uint32_t)msc->scsi_cmd[7] << 8) | (uint32_t)msc->scsi_cmd[8];
msc->block_addr *= usbd_mem_fops->mem_block_size[lun];
msc->block_len *= usbd_mem_fops->mem_block_size[lun];
usbd_ep_recev (udev, MSC_OUT_EP, msc->bbb_data, (uint16_t)usbd_mem_fops->mem_block_size[lun]);
} else if (MSC_STAGE_DATA_OUT == msc->bbb_transport_stage) {
usbd_mem_fops->mem_write(lun, msc->bbb_data, msc->block_addr, 1U);
msc->block_addr += usbd_mem_fops->mem_block_size[lun];
msc->block_len -= usbd_mem_fops->mem_block_size[lun];
/* end of the write, send bbb_csw to host */
if (0U == msc->block_len) {
msc->bbb_transport_stage = MSC_STAGE_STATUS;
l_csw_data_residue = msc->bbb_cbw.dCBWDataTransferLength - \
(((uint32_t)msc->scsi_cmd[7] << 8) | (uint32_t)msc->scsi_cmd[8]) * usbd_mem_fops->mem_block_size[lun];
bbb_set_csw(udev, msc->bbb_cbw.dCBWTag, l_csw_data_residue, 0U);
usbd_ep_send (udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
/* receive the next block data */
} else {
usbd_ep_recev (udev, MSC_OUT_EP, msc->bbb_data, (uint16_t)usbd_mem_fops->mem_block_size[lun]);
}
/* enter the MSC_STAGE_CMD */
} else if (MSC_STAGE_STATUS == msc->bbb_transport_stage) {
msc->bbb_transport_stage = MSC_STAGE_CMD;
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
} else {
/* no operation */
}
break;
/* process the REQUEST_SENSE command */
case REQUEST_SENSE:
bbb_report_array_to_host(udev, request_sense_data, DATA_LEN_REQUEST_SENSE);
break;
/* process the SCSI_MODE_SENSE6 command */
case SCSI_MODE_SENSE6:
bbb_report_array_to_host(udev, sense6_data, DATA_LEN_SENSE6);
break;
/* process the SCSI_READ_TOC_DATA command */
case SCSI_READ_TOC_DATA:
bbb_report_array_to_host(udev, usbd_mem_fops->mem_toc_data, 20U);
break;
/* default process: the SCSI command is not valid */
default:
/* if the data stage is DATA_IN, return the error_data to host in MSC_STAGE_DATA_IN */
if (0x80U & msc->bbb_cbw.bmCBWFlags) {
if (MSC_STAGE_CMD == msc->bbb_transport_stage){
msc->bbb_transport_stage = MSC_STAGE_DATA_IN;
msc->data_len = 1U;
usbd_ep_send (udev, MSC_IN_EP, &msc->error_data, 1U);
} else if (MSC_STAGE_DATA_IN == msc->bbb_transport_stage){
msc->bbb_transport_stage = MSC_STAGE_STATUS;
bbb_set_csw(udev, msc->bbb_cbw.dCBWTag, msc->bbb_cbw.dCBWDataTransferLength - 1U, 1U);
usbd_ep_send (udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
} else if (MSC_STAGE_STATUS == msc->bbb_transport_stage){
msc->bbb_transport_stage = MSC_STAGE_CMD;
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
} else {
/* no operation */
}
/* if the data stage is DATA_OUT, return the bbb_csw to host immediately */
} else{
if (MSC_STAGE_CMD == msc->bbb_transport_stage) {
msc->data_len = 0U;
bbb_set_csw(udev, msc->bbb_cbw.dCBWTag, msc->bbb_cbw.dCBWDataTransferLength, 1U);
usbd_ep_send (udev, MSC_IN_EP, (uint8_t *)&msc->bbb_csw, BBB_CSW_LENGTH);
msc->bbb_transport_stage = MSC_STAGE_STATUS;
} else if (MSC_STAGE_STATUS == msc->bbb_transport_stage) {
msc->bbb_transport_stage = MSC_STAGE_CMD;
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
} else {
/* no operation */
}
}
break;
}
}
@@ -0,0 +1,338 @@
/*!
\file msc_core.c
\brief USB MSC device class core functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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 "msc_core.h"
#include "msc_mem.h"
#include "usbd_transc.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x0989U
/* note: it should use the C99 standard when compiling the below codes */
/* USB standard device descriptor */
usb_desc_dev msc_dev_desc =
{
.header = {
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
.bcdUSB = 0x0200U,
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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 */
usb_msc_desc_config_set msc_config_desc =
{
.config =
{
.header = {
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = USB_MSC_CONFIG_DESC_LEN,
.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 language ID descriptor */
usb_desc_LANGID usbd_language_id_desc =
{
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.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 usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
/* USB string descriptor */
uint8_t* usbd_msc_strings[] =
{
[STR_IDX_LANGID] = (uint8_t *)&usbd_language_id_desc,
[STR_IDX_MFC] = (uint8_t *)&manufacturer_string,
[STR_IDX_PRODUCT] = (uint8_t *)&product_string,
[STR_IDX_SERIAL] = (uint8_t *)&serial_string
};
usb_desc msc_desc = {
.dev_desc = (uint8_t *)&msc_dev_desc,
.config_desc = (uint8_t *)&msc_config_desc,
.strings = usbd_msc_strings
};
/* local function prototypes ('static') */
static uint8_t msc_init (usb_dev *udev, uint8_t config_index);
static uint8_t msc_deinit (usb_dev *udev, uint8_t config_index);
static uint8_t msc_req_handler (usb_dev *udev, usb_req *req);
static void msc_data_in_handler (usb_dev *udev, uint8_t ep_num);
static void msc_data_out_handler (usb_dev *udev, uint8_t ep_num);
usb_class msc_class = {
.init = msc_init,
.deinit = msc_deinit,
.req_process = msc_req_handler,
.data_in = msc_data_in_handler,
.data_out = msc_data_out_handler
};
/*!
\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_init (usb_dev *udev, uint8_t config_index)
{
uint8_t i = 0U;
static usbd_msc_handler msc_handler;
memset((void *)&msc_handler, 0, sizeof(usbd_msc_handler));
/* initialize Tx endpoint */
usbd_ep_init(udev, EP_BUF_SNG, BULK_TX_ADDR, &(msc_config_desc.msc_epin));
/* initialize Rx endpoint */
usbd_ep_init(udev, EP_BUF_SNG, BULK_RX_ADDR, &(msc_config_desc.msc_epout));
udev->ep_transc[EP_ID(MSC_IN_EP)][TRANSC_IN] = msc_class.data_in;
udev->ep_transc[MSC_OUT_EP][TRANSC_OUT] = msc_class.data_out;
for (i = 0U; i < MEM_LUN_NUM; i ++) {
usbd_mem_fops->mem_init(i);
}
msc_handler.bbb_transport_stage = MSC_STAGE_CMD;
/* prepare endpoint to receive first bbb_cbw */
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc_handler.bbb_cbw, BBB_CBW_LENGTH);
udev->class_data[USBD_MSC_INTERFACE] = (void *)&msc_handler;
return USBD_OK;
}
/*!
\brief de-initialize the MSC device
\param[in] udev: pointer to USB device instance
\param[in] config_index: configuration index
\param[out] none
\retval USB device operation status
*/
static uint8_t msc_deinit (usb_dev *udev, uint8_t config_index)
{
usbd_msc_handler *msc = (usbd_msc_handler *)udev->class_data[USBD_MSC_INTERFACE];
/* deinitialize MSC endpoints */
usbd_ep_deinit (udev, MSC_IN_EP);
usbd_ep_deinit (udev, MSC_OUT_EP);
msc->bbb_transport_stage = MSC_STAGE_CMD;
/* prepare endpoint to receive first bbb_cbw */
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
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_req_handler (usb_dev *udev, usb_req *req)
{
uint8_t status = REQ_NOTSUPP;
usbd_msc_handler *msc = (usbd_msc_handler *)udev->class_data[USBD_MSC_INTERFACE];
switch (req->bRequest) {
case BBB_GET_MAX_LUN:
msc->usbd_msc_maxlun = (uint8_t)usbd_mem_fops->mem_maxlun();
msc->bbb_transport_stage = MSC_STAGE_CMD;
usb_transc_config(&udev->transc_in[0], &msc->usbd_msc_maxlun, 1U, 0U);
status = REQ_SUPP;
break;
case BBB_RESET:
msc->bbb_transport_stage = MSC_STAGE_CMD;
/* prepare endpoint to receive first bbb_cbw */
usbd_ep_recev (udev, MSC_OUT_EP, (uint8_t *)&msc->bbb_cbw, BBB_CBW_LENGTH);
status = REQ_SUPP;
break;
default:
break;
}
return status;
}
/*!
\brief handle data stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: the endpoint ID
\param[out] none
\retval none
*/
static void msc_data_in_handler (usb_dev *udev, uint8_t ep_num)
{
usb_transc *transc = &udev->transc_in[ep_num];
usbd_msc_handler *msc = (usbd_msc_handler *)udev->class_data[USBD_MSC_INTERFACE];
if (transc->xfer_len) {
usbd_ep_send (udev, ep_num, transc->xfer_buf, transc->xfer_len);
} else {
if (MSC_STAGE_CMD != msc->bbb_transport_stage) {
process_scsi_command(msc->bbb_cbw.bCBWLUN, udev);
}
}
}
/*!
\brief handle data out stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: the endpoint ID
\param[out] none
\retval none
*/
static void msc_data_out_handler (usb_dev *udev, uint8_t ep_num)
{
usbd_msc_handler *msc = (usbd_msc_handler *)udev->class_data[USBD_MSC_INTERFACE];
if (MSC_STAGE_CMD == msc->bbb_transport_stage){
scsi_command_parse(udev);
process_scsi_command(msc->bbb_cbw.bCBWLUN, udev);
} else if (MSC_STAGE_DATA_OUT == msc->bbb_transport_stage) {
process_scsi_command(msc->bbb_cbw.bCBWLUN, udev);
} else {
/* no operation */
}
}
@@ -0,0 +1,73 @@
/*!
\file printer_core.h
\brief the header file of USB printer device class core functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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
#define PROTOCOL_BI_DIRECTIONAL_ITF 0x02U
#define PROTOCOL_1284_4_ITF 0x03U
#define PROTOCOL_VENDOR 0xFFU
#define DEVICE_ID_LEN 103U
#define USB_PRINTER_CONFIG_DESC_LEN 32U
/* printing device specific-class request */
#define GET_DEVICE_ID 0x00U
#define GET_PORT_STATUS 0x01U
#define SOFT_RESET 0x02U
/* USB configuration descriptor structure */
typedef struct
{
usb_desc_config config;
usb_desc_itf printer_itf;
usb_desc_ep printer_epin;
usb_desc_ep printer_epout;
} usb_printer_desc_config_set;
extern usb_desc printer_desc;
extern usb_class printer_class;
#endif /* __PRINTER_CORE_H */
@@ -0,0 +1,310 @@
/*!
\file printer_core.c
\brief USB printer device class core functions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_transc.h"
#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];
uint8_t Printer_DEVICE_ID[DEVICE_ID_LEN] =
{
0x00, 0x67,
'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 */
usb_desc_dev printer_dev_desc =
{
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV,
},
.bcdUSB = 0x0200U,
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.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 */
usb_printer_desc_config_set printer_config_desc =
{
.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 usb_desc_LANGID usbd_language_id_desc =
{
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR,
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static usb_desc_str manufacturer_string =
{
.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 usb_desc_str product_string =
{
.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 usb_desc_str serial_string =
{
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
/* USB string descriptor */
static uint8_t* 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 printer_desc = {
.dev_desc = (uint8_t *)&printer_dev_desc,
.config_desc = (uint8_t *)&printer_config_desc,
.strings = usbd_msc_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_handler (usb_dev *udev, usb_req *req);
static void printer_data_in (usb_dev *udev, uint8_t ep_num);
static void printer_data_out (usb_dev *udev, uint8_t ep_num);
usb_class printer_class = {
.init = printer_init,
.deinit = printer_deinit,
.req_process = printer_req_handler,
.data_in = printer_data_in,
.data_out = printer_data_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/Rx endpoint */
usbd_ep_init(udev, EP_BUF_SNG, BULK_TX_ADDR, &(printer_config_desc.printer_epin));
usbd_ep_init(udev, EP_BUF_SNG, BULK_RX_ADDR, &(printer_config_desc.printer_epout));
udev->ep_transc[EP_ID(PRINTER_IN_EP)][TRANSC_IN] = printer_class.data_in;
udev->ep_transc[PRINTER_OUT_EP][TRANSC_OUT] = printer_class.data_out;
/* prepare to receive data */
usbd_ep_recev(udev, PRINTER_OUT_EP, g_printer_data_buf, PRINTER_OUT_PACKET);
return USBD_OK;
}
/*!
\brief de-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_deinit (usb_dev *udev, uint8_t config_index)
{
/* deinitialize the data Tx/Rx endpoint */
usbd_ep_deinit(udev, PRINTER_IN_EP);
usbd_ep_deinit(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_handler (usb_dev *udev, usb_req *req)
{
uint8_t status = REQ_NOTSUPP;
switch (req->bRequest) {
case GET_DEVICE_ID:
usb_transc_config(&udev->transc_in[0], Printer_DEVICE_ID, DEVICE_ID_LEN, 0U);
status = REQ_SUPP;
break;
case GET_PORT_STATUS:
usb_transc_config(&udev->transc_in[0], (uint8_t *)&g_port_status, 1U, 0U);
status = REQ_SUPP;
break;
case SOFT_RESET:
usbd_ep_recev(udev, PRINTER_OUT_EP, g_printer_data_buf, PRINTER_OUT_PACKET);
status = REQ_SUPP;
break;
default:
break;
}
return status;
}
/*!
\brief handle printer data
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval none
*/
static void printer_data_in (usb_dev *udev, uint8_t ep_num)
{
}
/*!
\brief handle printer data
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval none
*/
static void printer_data_out (usb_dev *udev, uint8_t ep_num)
{
}
@@ -0,0 +1,212 @@
/*!
\file usb_ch9_std.h
\brief USB 2.0 standard defines
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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 "usbd_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_BOS_DESC_LEN 0x0CU /*!< USB BOS descriptor length */
#define USB_OTG_DESC_LEN 0x03U /*!< USB device OTG descriptor length */
#define USB_SETUP_PACKET_LEN 0x08U /*!< USB SETUP packet length */
#define USB_DEVICE_CAPABITY 0x10U /*!< USB device capabity */
/* 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 reserved2 */
USB_SET_FEATURE = 0x3U, /*!< USB set feature request */
USB_RESERVED4 = 0x4U, /*!< USB reserved4 */
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 synchronized 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_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 endpoint control attributes*/
USB_EP_ATTR_ISO = 0x1U, /*!< USB endpoint isochronous attributes*/
USB_EP_ATTR_BULK = 0x2U, /*!< USB endpoint bulk attributes*/
USB_EP_ATTR_INT = 0x3U /*!< USB endpoint interrupt attributes*/
};
/* bits 3..2 : Sync type (only if ISOCHRONOUS) */
#define USB_EP_ATTR_NOSYNC 0x00U /*!< USB endpoint no SYNC attributes*/
#define USB_EP_ATTR_ASYNC 0x04U /*!< USB endpoint ASYNC attributes*/
#define USB_EP_ATTR_ADAPTIVE 0x08U /*!< USB endpoint adaptive attributes*/
#define USB_EP_ATTR_SYNC 0x0CU /*!< USB endpoint SYNC attributes*/
#define USB_EP_ATTR_SYNCTYPE 0x0CU /*!< USB endpoint SYNC type attributes*/
/* bits 5..4 : usage type (only if ISOCHRONOUS) */
#define USB_EP_ATTR_DATA 0x00U /*!< USB endpoint data attributes*/
#define USB_EP_ATTR_FEEDBACK 0x10U /*!< USB endpoint feedback attributes*/
#define USB_EP_ATTR_IMPLICIT_FEEDBACK_DATA 0x20U /*!< USB endpoint implicit feedback attributes*/
#define USB_EP_ATTR_USAGETYPE 0x30U /*!< USB endpoint usage type attributes*/
#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 definition */
typedef union _usb_setup {
uint8_t data[8]; /*!< USB setup data */
usb_req req; /*!< USB setup request */
} usb_setup;
/* USB descriptor definition */
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 attribute */
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[64]; /*!< 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 */
@@ -0,0 +1,332 @@
/*!
\file usbd_core.h
\brief USB device driver core
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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 "usb_ch9_std.h"
/* endpoints definitions */
#define EP_IN(x) ((uint8_t)(0x80U | (x)))
#define EP_OUT(x) ((uint8_t)(x))
#define EP_DIR(x) ((uint8_t)((x) >> 7U))
#define EP_ID(x) ((uint8_t)((x) & 0x7FU))
/* USB device endpoint0 max packet size */
#define USBD_EP0_MAX_SIZE 64U
#define USBD_TRANSC_COUNT 3U
/* USB device operation status */
enum usbd_status {
USBD_UNCONNECTED = 0U, /*!< USB device unconnected status */
USBD_DEFAULT, /*!< USB device default status */
USBD_ADDRESSED, /*!< USB device addressed status */
USBD_CONFIGURED, /*!< USB device configured status */
USBD_SUSPENDED, /*!< USB device suspended status */
USBD_CONNECTED /*!< USB device connected status */
};
/* USB device operation state */
enum usbd_state {
USBD_OK = 0U, /*!< USB device OK state */
USBD_BUSY, /*!< USB device busy state */
USBD_FAIL /*!< USB device fail state */
};
/* USB device transaction type */
enum usbd_transc {
TRANSC_SETUP = 0U, /*!< SETUP transaction */
TRANSC_OUT, /*!< OUT transaction */
TRANSC_IN, /*!< IN transaction */
TRANSC_UNKNOWN /*!< unknown transaction */
};
/* USB device endpoint buffer type */
enum usbd_ep_kind {
EP_BUF_SNG = 0U, /*!< single buffer endpoint type value */
EP_BUF_DBL /*!< double buffer endpoint type value */
};
/* USB device transaction struct */
typedef struct {
uint8_t max_len; /*!< packet max length */
uint8_t ep_stall; /*!< endpoint STALL */
uint8_t *xfer_buf; /*!< transfer buffer */
uint16_t xfer_len; /*!< transfer length */
uint16_t xfer_count; /*!< transfer count */
} usb_transc;
/* USB device basic struct */
typedef struct {
uint8_t max_ep_count; /*!< endpoint max count */
uint8_t twin_buf; /*!< double buffer */
uint16_t ram_size; /*!< ram size */
} usb_basic;
/* USB descriptor */
typedef struct {
uint8_t *dev_desc; /*!< device descriptor */
uint8_t *config_desc; /*!< configure descriptor */
uint8_t *bos_desc; /*!< bos descriptor */
uint8_t **strings; /*!< strings descriptor */
} usb_desc;
/* USB power management */
typedef struct {
uint8_t power_mode; /*!< power mode */
uint8_t power_low; /*!< power low */
uint8_t esof_count; /*!< ESOF count */
uint8_t suspend_enabled; /*!< suspend enabled flag */
uint8_t remote_wakeup; /*!< remote wakeup */
uint8_t remote_wakeup_on; /*!< remote wakeup enable */
uint8_t lpm_enable; /*!< LPM enable */
} usb_pm;
/* USB LPM management */
typedef struct {
uint32_t besl; /*!< besl */
uint32_t L1_resume; /*!< L1 resume */
uint32_t L1_remote_wakeup; /*!< L1 remote wakeup */
} usb_lpm;
/* USB control information */
typedef struct {
usb_req req; /*!< USB request */
uint8_t ctl_zlp; /*!< control zero length packet */
} usb_control;
typedef struct _usb_dev usb_dev;
typedef struct _usb_handler usb_handler;
typedef void (*usb_ep_transc) (usb_dev *usbd_dev, uint8_t ep_num);
/* USB class struct */
typedef struct {
uint8_t req_cmd;
uint8_t req_altset;
uint8_t (*init) (usb_dev *udev, uint8_t config_index);
uint8_t (*deinit) (usb_dev *udev, uint8_t config_index);
uint8_t (*req_process) (usb_dev *udev, usb_req *req);
uint8_t (*ctlx_in) (usb_dev *udev);
uint8_t (*ctlx_out) (usb_dev *udev);
void (*data_in) (usb_dev *udev, uint8_t ep_num);
void (*data_out) (usb_dev *udev, uint8_t ep_num);
} usb_class;
/* USB core driver struct */
struct _usb_dev {
/* basic parameters */
uint8_t config;
uint8_t dev_addr;
__IO uint8_t cur_status;
__IO uint8_t backup_status;
usb_pm pm;
#ifdef LPM_ENABLED
usb_lpm lpm;
#endif /* LPM_ENABLED */
usb_control control;
usb_transc transc_out[EP_COUNT];
usb_transc transc_in[EP_COUNT];
usb_ep_transc ep_transc[EP_COUNT][USBD_TRANSC_COUNT];
/* device class */
usb_desc *desc;
usb_class *class_core;
usb_handler *drv_handler;
void *class_data[USBD_ITF_MAX_NUM];
void *user_data;
void *data;
};
typedef struct
{
uint8_t (*SOF) (usb_dev *udev); /*!< SOF ISR callback */
} usbd_int_cb_struct;
/* USB handler struct */
struct _usb_handler {
void (*init) (void);
void (*deinit) (void);
void (*dp_pullup) (FlagStatus status);
void (*set_addr) (usb_dev *udev);
void (*suspend) (void);
void (*suspend_leave) (void);
void (*resume) (usb_dev *udev);
void (*ep_reset) (usb_dev *udev);
void (*ep_setup) (usb_dev *udev, uint8_t buf_kind, uint32_t buf_addr, const usb_desc_ep *ep_desc);
void (*ep_disable) (usb_dev *udev, uint8_t ep_addr);
void (*ep_rx_enable) (usb_dev *udev, uint8_t ep_num);
void (*ep_write) (uint8_t *fifo, uint8_t ep_num, uint16_t bytes);
uint16_t (*ep_read) (uint8_t *fifo, uint8_t ep_num, uint8_t buf_kind);
void (*ep_stall_set) (usb_dev *udev, uint8_t ep_addr);
void (*ep_stall_clear) (usb_dev *udev, uint8_t ep_addr);
uint8_t (*ep_status_get) (usb_dev *udev, uint8_t ep_addr);
};
extern usbd_int_cb_struct *usbd_int_fops;
/* static inline function definitions */
/*!
\brief device connect
\param[in] udev: pointer to USB core instance
\param[out] none
\retval none
*/
__STATIC_INLINE void usbd_connect (usb_dev *udev)
{
udev->drv_handler->dp_pullup(SET);
udev->cur_status = (uint8_t)USBD_CONNECTED;
}
/*!
\brief device disconnect
\param[in] udev: pointer to USB core instance
\param[out] none
\retval none
*/
__STATIC_INLINE void usbd_disconnect (usb_dev *udev)
{
udev->drv_handler->dp_pullup(RESET);
udev->cur_status = (uint8_t)USBD_UNCONNECTED;
}
/*!
\brief device core register configure when stop device
\param[in] udev: pointer to USB core instance
\param[out] none
\retval none
*/
__STATIC_INLINE void usbd_core_deinit (usb_dev *udev)
{
udev->drv_handler->deinit();
}
/*!
\brief initialize endpoint
\param[in] udev: pointer to USB core instance
\param[in] buf_kind: endpoint buffer kind
\param[in] buf_addr: buffer addresss
\param[in] ep_desc: pointer to endpoint descriptor
\param[out] none
\retval none
*/
__STATIC_INLINE void usbd_ep_init (usb_dev *udev, uint8_t buf_kind, uint32_t buf_addr, const usb_desc_ep *ep_desc)
{
udev->drv_handler->ep_setup(udev, buf_kind, buf_addr, ep_desc);
}
/*!
\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
*/
__STATIC_INLINE void usbd_ep_deinit (usb_dev *udev, uint8_t ep_addr)
{
udev->drv_handler->ep_disable(udev, ep_addr);
}
/*!
\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
*/
__STATIC_INLINE void usbd_ep_stall (usb_dev *udev, uint8_t ep_addr)
{
udev->drv_handler->ep_stall_set(udev, ep_addr);
}
/*!
\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
*/
__STATIC_INLINE void usbd_ep_clear_stall (usb_dev *udev, uint8_t ep_addr)
{
udev->drv_handler->ep_stall_clear(udev, ep_addr);
}
/*!
\brief get endpoint 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
*/
__STATIC_INLINE uint8_t usbd_ep_status_get (usb_dev *udev, uint8_t ep_addr)
{
return udev->drv_handler->ep_status_get(udev, ep_addr);
}
/* function declarations */
/* initialize USBD */
void usbd_init(usb_dev *udev, usb_desc *desc, usb_class *usbc);
/* endpoint prepare to transmit data */
void usbd_ep_send (usb_dev *udev, uint8_t ep_addr, uint8_t *pbuf, uint16_t buf_len);
/* endpoint prepare to receive data */
void usbd_ep_recev (usb_dev *udev, uint8_t ep_addr, uint8_t *pbuf, uint16_t buf_len);
#endif /* __USBD_CORE_H */
@@ -0,0 +1,111 @@
/*!
\file usbd_enum.h
\brief USB enumeration definitions
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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 "usb_ch9_std.h"
#ifndef NULL
#define NULL 0U
#endif
/* request state enumeration */
typedef enum _usb_reqsta
{
REQ_SUPP = 0x0U, /* supported request */
REQ_NOTSUPP = 0x1U /* unsupported request */
} usb_reqsta;
/* string descriptor index enumeration */
enum _str_index
{
STR_IDX_LANGID = 0x0U, /* language ID string index */
STR_IDX_MFC = 0x1U, /* manufacturer string index */
STR_IDX_PRODUCT = 0x2U, /* product string index */
STR_IDX_SERIAL = 0x3U, /* serial string index */
STR_IDX_CONFIG = 0x4U, /* configuration string index */
STR_IDX_ITF = 0x5U, /* interface string index */
STR_IDX_MAX = 0x6U /* string index max value */
};
/* PWR status enumeration */
typedef enum
{
USB_PWRSTA_SELF_POWERED = 0x1U, /* USB is in self powered status */
USB_PWRSTA_REMOTE_WAKEUP = 0x2U, /* USB is in remote wakeup status */
} usb_pwrsta;
/* usb endpoint feature enumeration */
typedef enum
{
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 */
/* device unique ID */
#define DEVICE_ID1 (0x1FFFF7E8U) /* device ID1 */
#define DEVICE_ID2 (0x1FFFF7ECU) /* device ID2 */
#define DEVICE_ID3 (0x1FFFF7F0U) /* device ID3 */
#define DEVICE_ID (0x40022100U) /* device ID information */
//#define USB_SERIAL_STRING_SIZE 0x1AU
/* USB device exported macros */
#define BYTE_SWAP(addr) (((uint16_t)(*((uint8_t *)(addr)))) + \
(uint16_t)(((uint16_t)(*(((uint8_t *)(addr)) + 1U))) << 8U))
#define BYTE_LOW(x) ((uint8_t)((x) & 0x00FFU))
#define BYTE_HIGH(x) ((uint8_t)(((x) & 0xFF00U) >> 8U))
#define USB_MIN(a, b) (((a) < (b)) ? (a) : (b))
#define CTL_EP(ep) (((ep) == 0x00U) || ((ep) == 0x80U))
/* function declarations */
/* handle USB standard device request */
usb_reqsta usbd_standard_request (usb_dev *udev, usb_req *req);
/* handle device class request */
usb_reqsta usbd_class_request (usb_dev *udev, usb_req *req);
/* handle USB vendor request */
usb_reqsta usbd_vendor_request (usb_dev *udev, usb_req *req);
/* get serial string */
void serial_string_get (uint16_t *unicode_str);
#endif /* __USBD_ENUM_H */
@@ -0,0 +1,67 @@
/*!
\file usbd_pwr.h
\brief USB device power management functions prototype
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_PWR_H
#define __USBD_PWR_H
#include "usbd_core.h"
/* static inline function definitions */
/*!
\brief first operation of USB wakeup is to wakeup MCU
\param[in] udev: pointer to USB core instance
\param[out] none
\retval none
*/
__STATIC_INLINE void resume_mcu (usb_dev *udev)
{
udev->drv_handler->suspend_leave();
}
/*!
\brief set USB device to suspend mode
\param[in] udev: pointer to USB core instance
\param[out] none
\retval none
*/
__STATIC_INLINE void usbd_to_suspend (usb_dev *udev)
{
udev->drv_handler->suspend();
}
/* function declarations */
/* start to remote wakeup */
void usbd_remote_wakeup_active (usb_dev *udev);
#endif /* __USBD_PWR_H */
@@ -0,0 +1,63 @@
/*!
\file usbd_transc.h
\brief USBD transaction
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_TRANSC_H
#define __USB_TRANSC_H
#include "usbd_core.h"
/*!
\brief USB transaction configure
\param[in] udev: pointer to USB device instance
\param[in] buf: transfer data buffer
\param[in] len: transfer data length
\param[out] none
\retval none
*/
__STATIC_INLINE void usb_transc_config (usb_transc *transc, uint8_t *buf, uint16_t len, uint16_t count)
{
transc->xfer_buf = buf;
transc->xfer_len = len;
transc->xfer_count = count;
}
/* function declarations */
/* process USB SETUP transaction */
void _usb_setup_transc (usb_dev *udev, uint8_t ep_num);
/* process USB OUT transaction */
void _usb_out0_transc (usb_dev *udev, uint8_t ep_num);
/* process USB IN transaction */
void _usb_in0_transc (usb_dev *udev, uint8_t ep_num);
#endif /* __USB_TRANSC_H */
@@ -0,0 +1,129 @@
/*!
\file usbd_core.c
\brief USB device driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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 "usbd_transc.h"
#include "usbd_lld_core.h"
usbd_int_cb_struct *usbd_int_fops = NULL;
/*!
\brief configure USB device initialization
\param[in] udev: pointer to USB core instance
\param[in] core: endpoint address
\param[in] usbc: USB class
\param[in] usbha: USB handler
\param[out] none
\retval none
*/
void usbd_init (usb_dev *udev, usb_desc *desc, usb_class *usbc)
{
/* configure USBD core basic attributes */
usbd_core.basic.max_ep_count = 8U;
usbd_core.basic.twin_buf = 1U;
usbd_core.basic.ram_size = 512U;
usbd_core.dev = udev;
udev->desc = desc;
udev->class_core = usbc;
udev->drv_handler = &usbd_drv_handler;
udev->ep_transc[0][TRANSC_SETUP] = _usb_setup_transc;
udev->ep_transc[0][TRANSC_OUT] = _usb_out0_transc;
udev->ep_transc[0][TRANSC_IN] = _usb_in0_transc;
/* configure power management */
udev->pm.power_mode = (udev->desc->config_desc[7] & 0x40U) >> 5;
/* enable USB suspend */
udev->pm.suspend_enabled = 1U;
/* USB low level initialization */
udev->drv_handler->init();
/* create serial string */
serial_string_get((uint16_t *)udev->desc->strings[STR_IDX_SERIAL]);
}
/*!
\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] buf_len: buffer length
\param[out] none
\retval none
*/
void usbd_ep_recev (usb_dev *udev, uint8_t ep_addr, uint8_t *pbuf, uint16_t buf_len)
{
/* configure the transaction level parameters */
usb_transc *transc = &udev->transc_out[EP_ID(ep_addr)];
usb_transc_config(transc, pbuf, buf_len, 0U);
/* enable endpoint to receive */
udev->drv_handler->ep_rx_enable(udev, ep_addr);
}
/*!
\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] buf_len: buffer length
\param[out] none
\retval none
*/
void usbd_ep_send (usb_dev *udev, uint8_t ep_addr, uint8_t *pbuf, uint16_t buf_len)
{
uint8_t ep_num = EP_ID(ep_addr);
usb_transc *transc = &udev->transc_in[ep_num];
uint16_t len = USB_MIN(buf_len, transc->max_len);
/* configure the transaction level parameters */
udev->drv_handler->ep_write(pbuf, ep_num, len);
usb_transc_config(transc, pbuf + len, buf_len - len, len);
}
@@ -0,0 +1,735 @@
/*!
\file usbd_enum.c
\brief USB enumeration function
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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"
/* USB enumeration handle functions */
static usb_reqsta _usb_std_getstatus (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_clearfeature (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_setfeature (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_setaddress (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_getdescriptor (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_setdescriptor (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_getconfiguration (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_setconfiguration (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_reserved (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_getinterface (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_setinterface (usb_dev *udev, usb_req *req);
static usb_reqsta _usb_std_synchframe (usb_dev *udev, usb_req *req);
static uint8_t* _usb_dev_desc_get (usb_dev *udev, uint8_t index, uint16_t *len);
static uint8_t* _usb_config_desc_get (usb_dev *udev, uint8_t index, uint16_t *len);
static uint8_t* _usb_str_desc_get (usb_dev *udev, uint8_t index, uint16_t *len);
static uint8_t* _usb_bos_desc_get (usb_dev *udev, uint8_t index, uint16_t *len);
static void int_to_unicode (uint32_t value, uint8_t *pbuf, uint8_t len);
/* standard device request handler */
static usb_reqsta (*_std_dev_req[]) (usb_dev *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_dev *udev, uint8_t index, uint16_t *len) = {
[USB_DESCTYPE_DEV - 1U] = _usb_dev_desc_get,
[USB_DESCTYPE_CONFIG - 1U] = _usb_config_desc_get,
[USB_DESCTYPE_STR - 1U] = _usb_str_desc_get
};
/*!
\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 operation cur_status
*/
usb_reqsta usbd_standard_request (usb_dev *udev, usb_req *req)
{
/* call device request handle function */
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_dev *udev, usb_req *req)
{
if ((uint8_t)USBD_CONFIGURED == udev->cur_status) {
if (BYTE_LOW(req->wIndex) < USBD_ITF_MAX_NUM) {
/* call device class handle function */
return (usb_reqsta)udev->class_core->req_process(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_dev *udev, usb_req *req)
{
(void)udev;
(void)req;
/* added by user */
return REQ_NOTSUPP;
}
/*!
\brief no operation, just for reserved
\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_reserved (usb_dev *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_dev *udev, uint8_t index, uint16_t *len)
{
(void)index;
*len = udev->desc->dev_desc[0];
return udev->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_dev *udev, uint8_t index, uint16_t *len)
{
(void)index;
*len = udev->desc->config_desc[2];
return udev->desc->config_desc;
}
/*!
\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_dev *udev, uint8_t index, uint16_t *len)
{
(void)index;
*len = (uint16_t)udev->desc->bos_desc[2] | (uint16_t)((uint16_t)udev->desc->bos_desc[3] << 8);
return udev->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 string descriptor
*/
static uint8_t* _usb_str_desc_get (usb_dev *udev, uint8_t index, uint16_t *len)
{
uint8_t* desc = udev->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_dev *udev, usb_req *req)
{
uint8_t recp = BYTE_LOW(req->wIndex);
usb_reqsta req_status = REQ_NOTSUPP;
static uint8_t status[2] = {0U};
switch(req->bmRequestType & USB_RECPTYPE_MASK) {
/* handle device get status request */
case USB_RECPTYPE_DEV:
switch (udev->cur_status) {
case USBD_ADDRESSED:
case USBD_CONFIGURED:
if (udev->pm.power_mode) {
status[0] = USB_STATUS_SELF_POWERED;
} else {
status[0] = 0U;
}
if (udev->pm.remote_wakeup) {
status[0] |= USB_STATUS_REMOTE_WAKEUP;
} else {
status[0] = 0U;
}
req_status = REQ_SUPP;
break;
default:
break;
}
break;
/* handle interface get status request */
case USB_RECPTYPE_ITF:
if (((uint8_t)USBD_CONFIGURED == udev->cur_status) && (recp < USBD_ITF_MAX_NUM)) {
req_status = REQ_SUPP;
}
break;
/* handle endpoint get status request */
case USB_RECPTYPE_EP:
if ((uint8_t)USBD_CONFIGURED == udev->cur_status) {
if (0x80U == (recp & 0x80U)) {
status[0] = udev->transc_in[EP_ID(recp)].ep_stall;
} else {
status[0] = udev->transc_out[recp].ep_stall;
}
req_status = REQ_SUPP;
}
break;
default:
break;
}
if (REQ_SUPP == req_status) {
usb_transc_config(&udev->transc_in[0], status, 2U, 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_dev *udev, usb_req *req)
{
uint8_t ep = 0U;
switch (req->bmRequestType & (uint8_t)USB_RECPTYPE_MASK) {
case USB_RECPTYPE_DEV:
switch (udev->cur_status) {
case USBD_ADDRESSED:
case USBD_CONFIGURED:
/* clear device remote wakeup feature */
if ((uint16_t)USB_FEATURE_REMOTE_WAKEUP == req->wValue) {
udev->pm.remote_wakeup = 0U;
return REQ_SUPP;
}
break;
default:
break;
}
break;
case USB_RECPTYPE_EP:
/* get endpoint address */
ep = BYTE_LOW(req->wIndex);
if ((uint8_t)USBD_CONFIGURED == udev->cur_status) {
/* clear endpoint halt feature */
if (((uint16_t)USB_FEATURE_EP_HALT == req->wValue) && (!CTL_EP(ep))) {
usbd_ep_clear_stall(udev, ep);
return REQ_SUPP;
}
}
break;
case USB_RECPTYPE_ITF:
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_dev *udev, usb_req *req)
{
uint8_t ep = 0U;
switch (req->bmRequestType & (uint8_t)USB_RECPTYPE_MASK) {
case USB_RECPTYPE_DEV:
switch (udev->cur_status) {
case USBD_ADDRESSED:
case USBD_CONFIGURED:
/* set device remote wakeup feature */
if ((uint16_t)USB_FEATURE_REMOTE_WAKEUP == req->wValue) {
udev->pm.remote_wakeup = 1U;
return REQ_SUPP;
}
break;
default:
break;
}
break;
case USB_RECPTYPE_EP:
/* get endpoint address */
ep = BYTE_LOW(req->wIndex);
if ((uint8_t)USBD_CONFIGURED == udev->cur_status) {
/* set endpoint halt feature */
if (((uint8_t)USB_FEATURE_EP_HALT == req->wValue) && (!CTL_EP(ep))) {
usbd_ep_stall(udev, ep);
return REQ_SUPP;
}
}
break;
case USB_RECPTYPE_ITF:
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_dev *udev, usb_req *req)
{
if ((0U == req->wIndex) && (0U == req->wLength)) {
udev->dev_addr = (uint8_t)(req->wValue) & 0x7FU;
if (udev->cur_status != (uint8_t)USBD_CONFIGURED) {
if (udev->dev_addr) {
udev->cur_status = (uint8_t)USBD_ADDRESSED;
} else {
udev->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_dev *udev, usb_req *req)
{
uint8_t desc_type = 0U;
uint8_t desc_index = 0U;
usb_reqsta status = REQ_NOTSUPP;
usb_transc *transc = &udev->transc_in[0];
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, &transc->xfer_len);
if (64U == req->wLength) {
transc->xfer_len = 8U;
}
break;
case USB_DESCTYPE_CONFIG:
transc->xfer_buf = std_desc_get[desc_type - 1U](udev, desc_index, &transc->xfer_len);
break;
case USB_DESCTYPE_STR:
if (desc_index < STR_IDX_MAX) {
transc->xfer_buf = std_desc_get[desc_type - 1U](udev, desc_index, &transc->xfer_len);
}
break;
case USB_DESCTYPE_ITF:
case USB_DESCTYPE_EP:
case USB_DESCTYPE_DEV_QUALIFIER:
case USB_DESCTYPE_OTHER_SPD_CONFIG:
case USB_DESCTYPE_ITF_POWER:
break;
case USB_DESCTYPE_BOS:
transc->xfer_buf = _usb_bos_desc_get(udev, desc_index, &transc->xfer_len);
break;
default:
break;
}
break;
case USB_RECPTYPE_ITF:
/* get device class special descriptor */
status = (usb_reqsta)(udev->class_core->req_process(udev, req));
break;
case USB_RECPTYPE_EP:
break;
default:
break;
}
if ((transc->xfer_len) && (req->wLength)) {
transc->xfer_len = USB_MIN(transc->xfer_len, req->wLength);
if ((transc->xfer_len < udev->control.req.wLength) &&
(0U == transc->xfer_len % transc->max_len)) {
udev->control.ctl_zlp = 1U;
}
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_dev *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_dev *udev, usb_req *req)
{
(void)req;
usb_reqsta req_status = REQ_NOTSUPP;
switch (udev->cur_status) {
case USBD_ADDRESSED:
if (0U == udev->config) {
req_status = REQ_SUPP;
}
break;
case USBD_CONFIGURED:
if (udev->config) {
req_status = REQ_SUPP;
}
break;
default:
break;
}
if (REQ_SUPP == req_status) {
usb_transc_config(&udev->transc_in[0], &(udev->config), 1U, 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 operation cur_status
*/
static usb_reqsta _usb_std_setconfiguration (usb_dev *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->cur_status) {
case USBD_ADDRESSED:
if (config){
(void)udev->class_core->init(udev, config);
udev->config = config;
udev->cur_status = (uint8_t)USBD_CONFIGURED;
}
status = REQ_SUPP;
break;
case USBD_CONFIGURED:
if (0U == config) {
(void)udev->class_core->deinit(udev, config);
udev->config = config;
udev->cur_status = (uint8_t)USBD_ADDRESSED;
} else if (config != udev->config) {
/* clear old configuration */
(void)udev->class_core->deinit(udev, udev->config);
/* set new configuration */
udev->config = config;
(void)udev->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_dev *udev, usb_req *req)
{
switch (udev->cur_status) {
case USBD_DEFAULT:
break;
case USBD_ADDRESSED:
break;
case USBD_CONFIGURED:
if (BYTE_LOW(req->wIndex) < USBD_ITF_MAX_NUM) {
usb_transc_config(&udev->transc_in[0], &(udev->class_core->req_altset), 1U, 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_dev *udev, usb_req *req)
{
switch (udev->cur_status) {
case USBD_DEFAULT:
break;
case USBD_ADDRESSED:
break;
case USBD_CONFIGURED:
if (BYTE_LOW(req->wIndex) < USBD_ITF_MAX_NUM) {
udev->class_core->req_altset = (uint8_t)req->wValue;
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_dev *udev, usb_req *req)
{
(void)udev;
(void)req;
/* no handle */
return REQ_SUPP;
}
/*!
\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
*/
static void int_to_unicode (uint32_t value, uint8_t *pbuf, uint8_t len)
{
uint8_t index = 0U;
for (index = 0U; index < len; index++) {
if ((value >> 28U) < 0x0AU) {
pbuf[2U * index] = (uint8_t)((value >> 28) + '0');
} else {
pbuf[2U * index] = (uint8_t)((value >> 28) + 'A' - 10U);
}
value = value << 4U;
pbuf[2U * index + 1U] = 0U;
}
}
/*!
\brief convert hex 32bits value into unicode char
\param[in] none
\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) >> 16U);
}
}
}
@@ -0,0 +1,60 @@
/*!
\file usbd_pwr.c
\brief USB device power management driver
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_pwr.h"
/*!
\brief start to remote wakeup
\param[in] udev: pointer to USB core instance
\param[out] none
\retval none
*/
void usbd_remote_wakeup_active(usb_dev *udev)
{
resume_mcu(udev);
#ifdef LPM_ENABLED
if(1 == udev->lpm.L1_remote_wakeup){
udev->drv_handler->resume(udev);
udev->lpm.L1_resume = 1U;
}
#endif /* LPM_ENABLED */
if(1U == udev->pm.remote_wakeup){
udev->pm.remote_wakeup_on = 1U;
udev->pm.esof_count = 15U;
udev->drv_handler->resume(udev);
}
}
@@ -0,0 +1,207 @@
/*!
\file usbd_transc.c
\brief USBD transaction function
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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"
/* local function prototypes ('static') */
static inline void usb_stall_transc (usb_dev *udev);
static inline void usb_ctl_status_in (usb_dev *udev);
static inline void usb_ctl_data_in (usb_dev *udev);
static inline void usb_ctl_out (usb_dev *udev);
static inline void usb_0len_packet_send (usb_dev *udev);
/*!
\brief USB setup stage processing
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void _usb_setup_transc (usb_dev *udev, uint8_t ep_num)
{
(void)ep_num;
usb_reqsta reqstat = REQ_NOTSUPP;
uint16_t count = udev->drv_handler->ep_read((uint8_t *)(&udev->control.req), 0U, (uint8_t)EP_BUF_SNG);
if (count != USB_SETUP_PACKET_LEN) {
usb_stall_transc(udev);
return;
}
switch (udev->control.req.bmRequestType & USB_REQTYPE_MASK) {
/* standard device request */
case USB_REQTYPE_STRD:
reqstat = usbd_standard_request(udev, &udev->control.req);
break;
/* device class request */
case USB_REQTYPE_CLASS:
reqstat = usbd_class_request(udev, &udev->control.req);
break;
/* vendor defined request */
case USB_REQTYPE_VENDOR:
reqstat = usbd_vendor_request(udev, &udev->control.req);
break;
default:
break;
}
if (REQ_SUPP == reqstat) {
if (0U == udev->control.req.wLength) {
/* USB control transfer status in stage */
usb_ctl_status_in(udev);
} else {
if (udev->control.req.bmRequestType & 0x80U) {
usb_ctl_data_in(udev);
} else {
/* USB control transfer data out stage */
usb_ctl_out(udev);
}
}
} else {
usb_stall_transc(udev);
}
}
/*!
\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 none
*/
void _usb_out0_transc (usb_dev *udev, uint8_t ep_num)
{
if (((uint8_t)USBD_CONFIGURED == udev->cur_status) && (udev->class_core->ctlx_out != NULL)) {
/* device class handle */
(void)udev->class_core->ctlx_out(udev);
}
usb_transc_config(&udev->transc_out[ep_num], NULL, 0U, 0U);
/* enter the control transaction status in stage */
usb_ctl_status_in(udev);
}
/*!
\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 none
*/
void _usb_in0_transc (usb_dev *udev, uint8_t ep_num)
{
(void)ep_num;
if (udev->control.ctl_zlp) {
/* send 0 length packet */
usb_0len_packet_send(udev);
udev->control.ctl_zlp = 0U;
}
if (((uint8_t)USBD_CONFIGURED == udev->cur_status) && (udev->class_core->ctlx_in != NULL)) {
(void)udev->class_core->ctlx_in(udev);
}
/* USB control transfer status out stage */
usb_ctl_out(udev);
if (0U != udev->dev_addr) {
udev->drv_handler->set_addr(udev);
udev->dev_addr = 0U;
}
}
/*!
\brief USB stalled transaction
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static inline void usb_stall_transc (usb_dev *udev)
{
usbd_ep_stall(udev, 0x0U);
}
/*!
\brief USB control transaction status in stage
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static inline void usb_ctl_status_in (usb_dev *udev)
{
udev->drv_handler->ep_write(udev->transc_in[0].xfer_buf, 0U, 0U);
}
/*!
\brief USB control transaction data in stage
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static inline void usb_ctl_data_in (usb_dev *udev)
{
usbd_ep_send(udev, 0U, udev->transc_in[0].xfer_buf, udev->transc_in[0].xfer_len);
}
/*!
\brief USB control transaction data out & status out stage
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static inline void usb_ctl_out (usb_dev *udev)
{
udev->drv_handler->ep_rx_enable(udev, 0U);
}
/*!
\brief USB send 0 length data packet
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static inline void usb_0len_packet_send (usb_dev *udev)
{
udev->drv_handler->ep_write(udev->transc_in[0].xfer_buf, 0U, 0U);
}
@@ -0,0 +1,73 @@
/*!
\file usbd_lld_core.h
\brief USB device low level driver core
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_LLD_CORE_H
#define __USBD_LLD_CORE_H
#include "usbd_lld_regs.h"
#include "usbd_core.h"
/* double buffer endpoint direction enumeration */
enum dbuf_ep_dir
{
DBUF_EP_IN, /*!< double buffer in direction */
DBUF_EP_OUT, /*!< double buffer out direction */
DBUF_EP_ERR, /*!< double buffer error direction */
};
/* USBD endpoint ram struct */
typedef struct
{
__IO uint32_t tx_addr; /*!< transmission address */
__IO uint32_t tx_count; /*!< transmission count */
__IO uint32_t rx_addr; /*!< reception address */
__IO uint32_t rx_count; /*!< reception count */
} usbd_ep_ram;
extern struct _usb_handler usbd_drv_handler;
/* USB core driver struct */
typedef struct
{
usb_basic basic;
usb_dev *dev;
} usb_core_drv;
extern usb_core_drv usbd_core;
/* function declarations */
/* free buffer used from application by toggling the SW_BUF byte */
void user_buffer_free (uint8_t ep_num, uint8_t dir);
#endif /* __USBD_LLD_CORE_H */
@@ -0,0 +1,48 @@
/*!
\file usbd_lld_int.h
\brief USB device low level interrupt handler
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_LLD_INT_H
#define __USBD_LLD_INT_H
#include "usbd_core.h"
#include "usbd_enum.h"
#include "usbd_pwr.h"
/* function declarations */
/* USB device interrupt service routine */
void usbd_isr (void);
/* handle USB high priority successful transfer event */
void usbd_int_hpst (usb_dev *udev);
#endif /* __USBD_LLD_INT_H */
@@ -0,0 +1,227 @@
/*!
\file usbd_lld_regs.h
\brief USB device low level registers
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_LLD_REGS_H
#define __USBD_LLD_REGS_H
#include "usbd_conf.h"
/* USB device registers base address */
#define USBD USBD_BASE
#define USBD_RAM USBD_RAM_BASE
/* registers definitions */
/* common registers */
#define USBD_CTL (REG32(USBD + 0x40U)) /*!< control register */
#define USBD_INTF (REG32(USBD + 0x44U)) /*!< interrupt flag register */
#define USBD_STAT (REG32(USBD + 0x48U)) /*!< status register */
#define USBD_DADDR (REG32(USBD + 0x4CU)) /*!< device address register */
#define USBD_BADDR (REG32(USBD + 0x50U)) /*!< buffer address register */
#define USBD_LPMCS (REG32(USBD + 0x54U)) /*!< USBD LPM control and status register */
/* endpoint control and status register */
#define USBD_EPxCS(ep_num) (REG32(USBD + (ep_num) * 4U)) /*!< endpoint x control and status register address */
/* bits definitions */
/* USBD_CTL */
#define CTL_STIE BIT(15) /*!< successful transfer interrupt enable mask */
#define CTL_PMOUIE BIT(14) /*!< packet memory overrun/underrun interrupt enable mask */
#define CTL_ERRIE BIT(13) /*!< error interrupt enable mask */
#define CTL_WKUPIE BIT(12) /*!< wakeup interrupt enable mask */
#define CTL_SPSIE BIT(11) /*!< suspend state interrupt enable mask */
#define CTL_RSTIE BIT(10) /*!< reset interrupt enable mask */
#define CTL_SOFIE BIT(9) /*!< start of frame interrupt enable mask */
#define CTL_ESOFIE BIT(8) /*!< expected start of frame interrupt enable mask */
#define CTL_L1REQIE BIT(7) /*!< LPM L1 state request interrupt enable */
#define CTL_L1RSREQ BIT(5) /*!< LPM L1 resume request */
#define CTL_RSREQ BIT(4) /*!< resume request */
#define CTL_SETSPS BIT(3) /*!< set suspend state */
#define CTL_LOWM BIT(2) /*!< low-power mode at suspend state */
#define CTL_CLOSE BIT(1) /*!< goes to close state */
#define CTL_SETRST BIT(0) /*!< set USB reset */
#ifdef LPM_ENABLED
#define USBD_INTEN BITS(7, 15) /*!< USBD interrupt enable bits */
#else
#define USBD_INTEN BITS(8, 15) /*!< USBD interrupt enable bits */
#endif
/* USBD_INTF */
#define INTF_STIF BIT(15) /*!< successful transfer interrupt flag (read only bit) */
#define INTF_PMOUIF BIT(14) /*!< packet memory overrun/underrun interrupt flag (clear-only bit) */
#define INTF_ERRIF BIT(13) /*!< error interrupt flag (clear-only bit) */
#define INTF_WKUPIF BIT(12) /*!< wakeup interrupt flag (clear-only bit) */
#define INTF_SPSIF BIT(11) /*!< suspend state interrupt flag (clear-only bit) */
#define INTF_RSTIF BIT(10) /*!< reset interrupt flag (clear-only bit) */
#define INTF_SOFIF BIT(9) /*!< start of frame interrupt flag (clear-only bit) */
#define INTF_ESOFIF BIT(8) /*!< expected start of frame interrupt flag(clear-only bit) */
#define INTF_L1REQ BIT(7) /*!< LPM L1 transaction is successfully received and acknowledged */
#define INTF_DIR BIT(4) /*!< direction of transaction (read-only bit) */
#define INTF_EPNUM BITS(0, 3) /*!< endpoint number (read-only bit) */
/* USBD_STAT */
#define STAT_RXDP BIT(15) /*!< data plus line status */
#define STAT_RXDM BIT(14) /*!< data minus line status */
#define STAT_LOCK BIT(13) /*!< locked the USB */
#define STAT_SOFLN BITS(11, 12) /*!< SOF lost number */
#define STAT_FCNT BITS(0, 10) /*!< frame number count */
/* USBD_DADDR */
#define DADDR_USBEN BIT(7) /*!< USB module enable */
#define DADDR_USBADDR BITS(0, 6) /*!< USB device address */
/* USBD_EPxCS */
#define EPxCS_RX_ST BIT(15) /*!< endpoint reception successful transferred */
#define EPxCS_RX_DTG BIT(14) /*!< endpoint reception data PID toggle */
#define EPxCS_RX_STA BITS(12, 13) /*!< endpoint reception status bits */
#define EPxCS_SETUP BIT(11) /*!< endpoint setup transaction completed */
#define EPxCS_CTL BITS(9, 10) /*!< endpoint type control */
#define EPxCS_KCTL BIT(8) /*!< endpoint kind control */
#define EPxCS_TX_ST BIT(7) /*!< endpoint transmission successful transfer */
#define EPxCS_TX_DTG BIT(6) /*!< endpoint transmission data toggle */
#define EPxCS_TX_STA BITS(4, 5) /*!< endpoint transmission transfers status bits */
#define EPxCS_AR BITS(0, 3) /*!< endpoint address */
/* USBD_LPMCS */
#define LPMCS_BLSTAT BITS(4, 7) /*!< bLinkState value */
#define LPMCS_REMWK BIT(3) /*!< bRemoteWake value */
#define LPMCS_LPMACK BIT(1) /*!< LPM token acknowledge enable */
#define LPMCS_LPMEN BIT(0) /*!< LPM support enable */
/* constants definitions */
/* endpoint control and status register mask (no toggle fields) */
#define EPCS_MASK (EPxCS_RX_ST | EPxCS_SETUP | \
EPxCS_CTL | EPxCS_KCTL | EPxCS_TX_ST | EPxCS_AR)
/* EPxCS_CTL[1:0] endpoint type control */
#define ENDP_TYPE(regval) (EPxCS_CTL & ((regval) << 9U))
#define EP_BULK ENDP_TYPE(0U) /* bulk transfers */
#define EP_CONTROL ENDP_TYPE(1U) /* control transfers */
#define EP_ISO ENDP_TYPE(2U) /* isochronous transfers */
#define EP_INTERRUPT ENDP_TYPE(3U) /* interrupt transfers */
#define EP_CTL_MASK (~EPxCS_CTL & EPCS_MASK)
/* endpoint kind control mask */
#define EPKCTL_MASK (~EPxCS_KCTL & EPCS_MASK)
/* EPxCS_TX_STA[1:0] status for Tx transfer */
#define ENDP_TXSTAT(regval) (EPxCS_TX_STA & ((regval) << 4U))
#define EPTX_DISABLED ENDP_TXSTAT(0U) /* transmission state is disabled */
#define EPTX_STALL ENDP_TXSTAT(1U) /* transmission state is STALL */
#define EPTX_NAK ENDP_TXSTAT(2U) /* transmission state is NAK */
#define EPTX_VALID ENDP_TXSTAT(3U) /* transmission state is enabled */
#define EPTX_DTGMASK (EPxCS_TX_STA | EPCS_MASK)
/* EPxCS_RX_STA[1:0] status for Rx transfer */
#define ENDP_RXSTAT(regval) (EPxCS_RX_STA & ((regval) << 12U))
#define EPRX_DISABLED ENDP_RXSTAT(0U) /* reception state is disabled */
#define EPRX_STALL ENDP_RXSTAT(1U) /* reception state is STALL */
#define EPRX_NAK ENDP_RXSTAT(2U) /* reception state is NAK */
#define EPRX_VALID ENDP_RXSTAT(3U) /* reception state is enabled */
#define EPRX_DTGMASK (EPxCS_RX_STA | EPCS_MASK)
/* endpoint receive/transmission counter register bit definitions */
#define EPRCNT_BLKSIZ BIT(15) /* reception data block size */
#define EPRCNT_BLKNUM BITS(10, 14) /* reception data block number */
#define EPRCNT_CNT BITS(0, 9) /* reception data count */
#define EPTCNT_CNT BITS(0, 9) /* transmisson data count */
/* interrupt flag clear bits */
#define CLR(x) (USBD_INTF = ~INTF_##x)
/* endpoint receive/transmission counter register bit offset */
#define BLKSIZE_OFFSET (0x01U)
#define BLKNUM_OFFSET (0x05U)
#define RXCNT_OFFSET (0x0AU)
#define TXCNT_OFFSET (0x0AU)
#define BLKSIZE32_MASK (0x1fU)
#define BLKSIZE2_MASK (0x01U)
#define BLKSIZE32_OFFSETMASK (0x05U)
#define BLKSIZE2_OFFSETMASK (0x01U)
/* USBD operation macros */
/* Tx or Rx transfer status setting (bits EPTX_STA[1:0]) */
#define USBD_EP_TX_STAT_SET(ep, stat) do {\
USBD_EPxCS(ep) = (USBD_EPxCS(ep) & (uint16_t)EPTX_DTGMASK) ^ (stat); \
} while(0)
#define USBD_EP_RX_STAT_SET(ep, stat) do {\
USBD_EPxCS(ep) = (USBD_EPxCS(ep) & (uint16_t)EPRX_DTGMASK) ^ (stat); \
} while(0)
/* clear bit EPxCS_RX_ST/EPxCS_TX_ST in the endpoint control and status register */
#define USBD_EP_TX_ST_CLEAR(ep) do {\
USBD_EPxCS(ep) &= ~EPxCS_TX_ST & (uint16_t)EPCS_MASK; \
} while(0)
#define USBD_EP_RX_ST_CLEAR(ep) do {\
USBD_EPxCS(ep) &= ~EPxCS_RX_ST & (uint16_t)EPCS_MASK; \
} while(0)
/* toggle EPxCS_RX_DTG or EPxCS_TX_DTG bit in the endpoint control and status register */
#define USBD_TX_DTG_TOGGLE(ep) do {\
USBD_EPxCS(ep) = EPxCS_TX_DTG | (USBD_EPxCS(ep) & EPCS_MASK); \
} while(0)
#define USBD_RX_DTG_TOGGLE(ep) do {\
USBD_EPxCS(ep) = EPxCS_RX_DTG | (USBD_EPxCS(ep) & EPCS_MASK); \
} while(0)
/* clear EPxCS_RX_DTG or EPxCS_TX_DTG bit in the endpoint control and status register */
#define USBD_TX_DTG_CLEAR(ep) do {\
if ((USBD_EPxCS(ep_num) & EPxCS_TX_DTG) != 0U) {\
USBD_TX_DTG_TOGGLE(ep);\
} \
} while(0)
#define USBD_RX_DTG_CLEAR(ep) do {\
if ((USBD_EPxCS(ep_num) & EPxCS_RX_DTG) != 0U) {\
USBD_RX_DTG_TOGGLE(ep);\
} \
} while(0)
#define USBD_EP_DBL_BUF_SET(ep) (USBD_EPxCS(ep) = (USBD_EPxCS(ep) | EPxCS_KCTL) & EPCS_MASK)
#endif /* __USBD_LLD_REGS_H */
@@ -0,0 +1,641 @@
/*!
\file usbd_lld_core.c
\brief USB device low level driver core
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_lld_core.h"
#include "usbd_enum.h"
#define USB_EPTYPE_MASK 0x03U
#if defined (__CC_ARM) /* ARM Compiler */
static usbd_ep_ram btable_ep[EP_COUNT]__attribute__((at(USBD_RAM + BTABLE_OFFSET)));
#elif defined (__ICCARM__) /* IAR Compiler */
__no_init usbd_ep_ram btable_ep[EP_COUNT] @(USBD_RAM + BTABLE_OFFSET);
#elif defined (__GNUC__) /* GNU GCC Compiler */
usbd_ep_ram *btable_ep = (usbd_ep_ram *)(USBD_RAM + BTABLE_OFFSET);
#endif
usb_core_drv usbd_core;
static const uint32_t ep_type[] = {
[USB_EP_ATTR_CTL] = EP_CONTROL,
[USB_EP_ATTR_BULK] = EP_BULK,
[USB_EP_ATTR_INT] = EP_INTERRUPT,
[USB_EP_ATTR_ISO] = EP_ISO
};
/* local function prototypes ('static') */
static void usbd_dp_pullup (FlagStatus status);
static void usbd_core_reset (void);
static void usbd_core_stop (void);
static void usbd_address_set (usb_dev *udev);
static void usbd_ep_reset (usb_dev *udev);
static void usbd_ep_setup (usb_dev *udev, uint8_t buf_kind, uint32_t buf_addr, const usb_desc_ep *ep_desc);
static void usbd_ep_rx_enable (usb_dev *udev, uint8_t ep_addr);
static void usbd_ep_disable (usb_dev *udev, uint8_t ep_addr);
static void usbd_ep_stall_set (usb_dev *udev, uint8_t ep_addr);
static void usbd_ep_stall_clear (usb_dev *udev, uint8_t ep_addr);
static void usbd_ep_data_write (uint8_t *user_fifo, uint8_t ep_num, uint16_t bytes);
static uint16_t usbd_ep_data_read (uint8_t *user_fifo, uint8_t ep_num, uint8_t buf_kind);
static void usbd_resume (usb_dev *udev);
static void usbd_suspend (void);
static void usbd_leave_suspend (void);
static uint8_t usbd_ep_status (usb_dev *udev, uint8_t ep_addr);
struct _usb_handler usbd_drv_handler = {
.dp_pullup = usbd_dp_pullup,
.init = usbd_core_reset,
.deinit = usbd_core_stop,
.suspend = usbd_suspend,
.suspend_leave = usbd_leave_suspend,
.resume = usbd_resume,
.set_addr = usbd_address_set,
.ep_reset = usbd_ep_reset,
.ep_disable = usbd_ep_disable,
.ep_setup = usbd_ep_setup,
.ep_rx_enable = usbd_ep_rx_enable,
.ep_write = usbd_ep_data_write,
.ep_read = usbd_ep_data_read,
.ep_stall_set = usbd_ep_stall_set,
.ep_stall_clear = usbd_ep_stall_clear,
.ep_status_get = usbd_ep_status
};
/*!
\brief free buffer used from application by toggling the SW_BUF byte
\param[in] ep_num: endpoint identifier (0..7)
\param[in] dir: endpoint direction which can be OUT(0) or IN(1)
\param[out] none
\retval None
*/
void user_buffer_free (uint8_t ep_num, uint8_t dir)
{
if ((uint8_t)DBUF_EP_OUT == dir) {
USBD_TX_DTG_TOGGLE(ep_num);
} else if ((uint8_t)DBUF_EP_IN == dir) {
USBD_RX_DTG_TOGGLE(ep_num);
} else {
/* no operation */
}
}
/*!
\brief set the status of pull-up pin
\param[in] status: SET or RESET
\param[out] none
\retval none
*/
static void usbd_dp_pullup (FlagStatus status)
{
if (SET == status) {
gpio_bit_set(USB_PULLUP, USB_PULLUP_PIN);
} else {
gpio_bit_reset(USB_PULLUP, USB_PULLUP_PIN);
}
}
/*!
\brief device core register initialization
\param[in] none
\param[out] none
\retval none
*/
static void usbd_core_reset (void)
{
/* reset the CLOSE bit */
USBD_CTL = CTL_SETRST;
/* may be need wait some time(tSTARTUP) */
/* clear SETRST bit in USBD_CTL register */
USBD_CTL = 0U;
/* clear all pending interrupts */
USBD_INTF = 0U;
/* set descriptors table offset in USB dedicated SRAM */
USBD_BADDR = BTABLE_OFFSET & 0xFFF8U;
#ifdef LPM_ENABLED
/* enable L1REQ interrupt */
USBD_CTL = CTL_L1REQIE;
USBD_LPMCS = LPMCS_LPMACK | LPMCS_LPMEN;
#endif /* LPM_ENABLED */
/* enable all interrupts mask bits */
USBD_CTL |= CTL_STIE | CTL_WKUPIE | CTL_SPSIE | CTL_SOFIE | CTL_ESOFIE | CTL_RSTIE;
}
/*!
\brief device core register configure when stop device
\param[in] none
\param[out] none
\retval none
*/
static void usbd_core_stop (void)
{
/* disable all interrupts and set USB reset */
USBD_CTL = CTL_SETRST;
/* clear all interrupt flags */
USBD_INTF = 0U;
/* close device */
USBD_CTL = CTL_SETRST | CTL_CLOSE;
}
/*!
\brief set device address
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void usbd_address_set (usb_dev *udev)
{
USBD_DADDR = DADDR_USBEN | udev->dev_addr;
}
/*!
\brief handle USB reset event
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void usbd_ep_reset (usb_dev *udev)
{
uint8_t i = 0U;
usb_transc *transc = &udev->transc_in[0];
btable_ep[0].tx_addr = EP0_TX_ADDR;
btable_ep[0].tx_count = 0U;
transc->max_len = USBD_EP0_MAX_SIZE;
transc = &udev->transc_out[0];
btable_ep[0].rx_addr = EP0_RX_ADDR;
transc->max_len = USBD_EP0_MAX_SIZE;
if (transc->max_len > 62U) {
btable_ep[0].rx_count = ((uint16_t)((uint16_t)transc->max_len << 5) - 1U) | 0x8000U;
} else {
btable_ep[0].rx_count = ((transc->max_len + 1U) & ~1U) << 9U;
}
/* reset non-control endpoints */
for (i = 1U; i < EP_COUNT; i++) {
USBD_EPxCS(i) = (USBD_EPxCS(i) & EPCS_MASK) | i;
}
/* clear endpoint 0 register */
USBD_EPxCS(0U)= (uint16_t)(USBD_EPxCS(0U));
USBD_EPxCS(0U) = EP_CONTROL | EPRX_VALID | EPTX_NAK;
/* set device address as default address 0 */
USBD_DADDR = DADDR_USBEN;
udev->cur_status = (uint8_t)USBD_DEFAULT;
}
/*!
\brief endpoint initialization
\param[in] udev: pointer to USB core instance
\param[in] buf_kind: endpoint buffer kind
\param[in] buf_addr: endpoint buffer address
\param[in] ep_desc: pointer to endpoint descriptor
\param[out] none
\retval none
*/
static void usbd_ep_setup (usb_dev *udev, uint8_t buf_kind, uint32_t buf_addr, const usb_desc_ep *ep_desc)
{
uint8_t ep_addr = ep_desc->bEndpointAddress;
uint8_t ep_num = EP_ID(ep_addr);
uint16_t max_len = ep_desc->wMaxPacketSize;
usb_transc *transc = NULL;
/* set the endpoint type */
USBD_EPxCS(ep_num) = ep_type[ep_desc->bmAttributes & USB_EPTYPE_MASK] | ep_num;
if (EP_DIR(ep_addr)) {
transc = &udev->transc_in[ep_num];
transc->max_len = (uint8_t)max_len;
if ((uint8_t)EP_BUF_SNG == buf_kind) {
btable_ep[ep_num].tx_addr = buf_addr;
/* configure the endpoint status as NAK status */
USBD_EP_TX_STAT_SET(ep_num, EPTX_NAK);
} else if ((uint8_t)EP_BUF_DBL == buf_kind) {
USBD_EP_DBL_BUF_SET(ep_num);
btable_ep[ep_num].tx_addr = buf_addr & 0xFFFFU;
btable_ep[ep_num].rx_addr = (buf_addr & 0xFFFF0000U) >> 16U;
USBD_EP_TX_STAT_SET(ep_num, EPTX_VALID);
USBD_EP_RX_STAT_SET(ep_num, EPRX_DISABLED);
} else {
/* error operation */
}
} else {
transc = &udev->transc_out[ep_num];
transc->max_len = (uint8_t)max_len;
if ((uint8_t)EP_BUF_SNG == buf_kind) {
btable_ep[ep_num].rx_addr = buf_addr;
} else if ((uint8_t)EP_BUF_DBL == buf_kind) {
USBD_EP_DBL_BUF_SET(ep_num);
USBD_TX_DTG_TOGGLE(ep_num);
btable_ep[ep_num].tx_addr = buf_addr & 0xFFFFU;
btable_ep[ep_num].rx_addr = (buf_addr & 0xFFFF0000U) >> 16U;
if (max_len > 62U) {
btable_ep[ep_num].tx_count = (((uint32_t)max_len << 5) - 1U) | 0x8000U;
} else {
btable_ep[ep_num].tx_count = ((max_len + 1U) & ~1U) << 9U;
}
} else {
/* error operation */
}
if (max_len > 62U) {
btable_ep[ep_num].rx_count = (((uint32_t)max_len << 5U) - 1U) | 0x8000U;
} else {
btable_ep[ep_num].rx_count = ((max_len + 1U) & ~1U) << 9U;
}
if ((uint8_t)EP_BUF_SNG == buf_kind) {
/* configure the endpoint status as NAK status */
USBD_EP_RX_STAT_SET(ep_num, EPRX_NAK);
} else if ((uint8_t)EP_BUF_DBL == buf_kind) {
USBD_EP_RX_STAT_SET(ep_num, EPRX_DISABLED);
USBD_EP_TX_STAT_SET(ep_num, EPTX_NAK);
} else {
/* error operation */
}
}
}
/*!
\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
*/
static void usbd_ep_disable (usb_dev *udev, uint8_t ep_addr)
{
(void)udev;
uint8_t ep_num = EP_ID(ep_addr);
if (EP_DIR(ep_addr)) {
USBD_TX_DTG_CLEAR(ep_num);
/* configure the endpoint status as DISABLED */
USBD_EP_TX_STAT_SET(ep_num, EPTX_DISABLED);
} else {
USBD_RX_DTG_CLEAR(ep_num);
/* configure the endpoint status as DISABLED */
USBD_EP_RX_STAT_SET(ep_num, EPRX_DISABLED);
}
}
/*!
\brief enable endpoint to receive
\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
*/
static void usbd_ep_rx_enable (usb_dev *udev, uint8_t ep_addr)
{
(void)udev;
/* enable endpoint to receive */
USBD_EP_RX_STAT_SET(EP_ID(ep_addr), EPRX_VALID);
}
/*!
\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
*/
static void usbd_ep_stall_set (usb_dev *udev, uint8_t ep_addr)
{
uint8_t ep_num = EP_ID(ep_addr);
if (0U == ep_num) {
USBD_EP_TX_STAT_SET(0U, EPTX_STALL);
USBD_EP_RX_STAT_SET(0U, EPRX_STALL);
} else {
if (EP_DIR(ep_addr)) {
udev->transc_in[ep_num].ep_stall = 1U;
USBD_EP_TX_STAT_SET(ep_num, EPTX_STALL);
} else {
udev->transc_out[ep_num].ep_stall = 1U;
USBD_EP_RX_STAT_SET(ep_num, EPRX_STALL);
}
}
}
/*!
\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
*/
static void usbd_ep_stall_clear (usb_dev *udev, uint8_t ep_addr)
{
uint8_t ep_num = EP_ID(ep_addr);
if (EP_DIR(ep_addr)) {
/* clear endpoint data toggle bit */
USBD_TX_DTG_CLEAR(ep_num);
udev->transc_in[ep_num].ep_stall = 0U;
/* clear endpoint stall status */
USBD_EP_TX_STAT_SET(ep_num, EPTX_VALID);
} else {
/* clear endpoint data toggle bit */
USBD_RX_DTG_CLEAR(ep_num);
udev->transc_out[ep_num].ep_stall = 0U;
/* clear endpoint stall status */
USBD_EP_RX_STAT_SET(ep_num, EPRX_VALID);
}
}
/*!
\brief get the endpoint 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 endpoint status
*/
static uint8_t usbd_ep_status (usb_dev *udev, uint8_t ep_addr)
{
(void)udev;
uint32_t epcs = USBD_EPxCS(EP_ID(ep_addr));
if (EP_DIR(ep_addr)) {
return (uint8_t)(epcs & EPxCS_TX_STA);
} else {
return (uint8_t)(epcs & EPxCS_RX_STA);
}
}
/*!
\brief write data from user FIFO to USB RAM
\param[in] user_fifo: pointer to user FIFO
\param[in] ep_num: endpoint number
\param[in] bytes: the bytes count of the write data
\param[out] none
\retval none
*/
static void usbd_ep_data_write (uint8_t *user_fifo, uint8_t ep_num, uint16_t bytes)
{
if (0U != bytes) {
uint32_t n;
uint32_t *write_addr = (uint32_t *)(btable_ep[ep_num].tx_addr * 2U + USBD_RAM);
for (n = 0U; n < (bytes + 1U) / 2U; n++) {
*write_addr++ = *((uint16_t*)user_fifo);
user_fifo += 2U;
}
}
btable_ep[ep_num].tx_count = bytes;
USBD_EP_TX_STAT_SET(ep_num, EPTX_VALID);
}
/*!
\brief read data from USBRAM to user FIFO
\param[in] user_fifo: pointer to user FIFO
\param[in] ep_num: endpoint number
\param[in] buf_kind: endpoint buffer kind
\param[out] none
\retval none
*/
static uint16_t usbd_ep_data_read (uint8_t *user_fifo, uint8_t ep_num, uint8_t buf_kind)
{
uint16_t n = 0U, bytes = 0U;
uint32_t *read_addr = NULL;
if ((uint8_t)EP_BUF_SNG == buf_kind) {
bytes = (uint16_t)(btable_ep[ep_num].rx_count & EPRCNT_CNT);
read_addr = (uint32_t *)(btable_ep[ep_num].rx_addr * 2U + USBD_RAM);
} else if ((uint8_t)EP_BUF_DBL == buf_kind) {
if (USBD_EPxCS(ep_num) & EPxCS_TX_DTG) {
bytes = (uint16_t)(btable_ep[ep_num].tx_count & EPRCNT_CNT);
read_addr = (uint32_t *)(btable_ep[ep_num].tx_addr * 2U + USBD_RAM);
} else {
bytes = (uint16_t)(btable_ep[ep_num].rx_count & EPRCNT_CNT);
read_addr = (uint32_t *)(btable_ep[ep_num].rx_addr * 2U + USBD_RAM);
}
} else {
return 0U;
}
for (n = 0U; n < (bytes + 1U) / 2U; n++) {
*((uint16_t*)user_fifo) = (uint16_t)*read_addr++;
user_fifo += 2U;
}
return bytes;
}
#ifdef USBD_LOWPWR_MODE_ENABLE
/*!
\brief restore system clocks and power while exiting from suspend mode
\param[in] none
\param[out] none
\retval none
*/
static void lowpower_mode_exit (void)
{
/* restore system clock */
#ifdef LPM_ENABLED
/* enable IRC8M */
rcu_osci_on(RCU_IRC8M);
/* wait till IRC8M is ready */
while (RESET == rcu_flag_get(RCU_FLAG_IRC8MSTB)) {
}
#else
/* enable HXTAL */
rcu_osci_on(RCU_HXTAL);
/* wait till HXTAL is ready */
while(RESET == rcu_flag_get(RCU_FLAG_HXTALSTB)) {
}
#endif
/* enable PLL */
rcu_osci_on(RCU_PLL_CK);
/* wait till PLL is ready */
while(RESET == rcu_flag_get(RCU_FLAG_PLLSTB)) {
}
/* select PLL as system clock source */
rcu_system_clock_source_config(RCU_CKSYSSRC_PLL);
/* wait till PLL is used as system clock source */
while(0x08U != rcu_system_clock_source_get()) {
}
/* low power sleep on exit disabled */
system_lowpower_reset(SCB_LPM_DEEPSLEEP);
#ifdef USE_IRC48M
/* enable IRC48M clock */
rcu_osci_on(RCU_IRC48M);
/* wait till IRC48M is ready */
while (SUCCESS != rcu_osci_stab_wait(RCU_IRC48M)) {
}
rcu_ck48m_clock_config(RCU_CK48MSRC_IRC48M);
#endif
}
#endif /* USBD_LOWPWR_MODE_ENABLE */
/*!
\brief resume the USB device
\param[in] none
\param[out] none
\retval none
*/
static void usbd_resume (usb_dev *udev)
{
#ifdef LPM_ENABLED
if(1 == udev->lpm.L1_remote_wakeup){
USBD_CTL |= CTL_L1RSREQ;
}
#endif /* LPM_ENABLED */
if(1U == usbd_core.dev->pm.remote_wakeup){
/* make USB resume */
USBD_CTL |= CTL_RSREQ;
}
}
/*!
\brief set USB device to leave mode
\param[in] none
\param[out] none
\retval none
*/
static void usbd_leave_suspend (void)
{
/* clear low_power mode bit in USBD_CTL */
USBD_CTL &= ~CTL_LOWM;
#ifdef USBD_LOWPWR_MODE_ENABLE
/* restore normal operations */
lowpower_mode_exit();
#endif /* USBD_LOWPWR_MODE_ENABLE */
/* clear SETSPS bit */
USBD_CTL &= ~CTL_SETSPS;
}
/*!
\brief set USB device to enter suspend mode
\param[in] none
\param[out] none
\retval none
*/
static void usbd_suspend (void)
{
/* set USB module to suspend and low-power mode */
USBD_CTL |= CTL_SETSPS | CTL_LOWM;
#ifdef USBD_LOWPWR_MODE_ENABLE
/* check wakeup flag is set */
if (0U == (USBD_INTF & INTF_WKUPIF)) {
/* enter DEEP_SLEEP mode with LDO in low power mode */
pmu_to_deepsleepmode(PMU_LDO_LOWPOWER, WFI_CMD);
} else {
/* clear wakeup interrupt flag */
CLR(WKUPIF);
/* clear set_suspend flag */
USBD_CTL &= ~CTL_SETSPS;
}
#endif /* USBD_LOWPWR_MODE_ENABLE */
}
@@ -0,0 +1,276 @@
/*!
\file usbd_lld_int.c
\brief USB device low level interrupt routines
\version 2020-08-01, V3.0.0, firmware for GD32F30x
*/
/*
Copyright (c) 2020, 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_lld_int.h"
#include "usbd_lld_core.h"
/* local function prototypes ('static') */
static void usbd_int_suspend (usb_dev *udev);
/*!
\brief handle USB high priority successful transfer event
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
void usbd_int_hpst (usb_dev *udev)
{
__IO uint16_t int_status = 0U;
/* wait till interrupts are not pending */
while ((int_status = (uint16_t)USBD_INTF) & (uint16_t)INTF_STIF) {
/* get endpoint number */
uint8_t ep_num = (uint8_t)(int_status & INTF_EPNUM);
uint8_t transc_num = (uint8_t)TRANSC_UNKNOWN;
if (int_status & INTF_DIR) {
if (USBD_EPxCS(ep_num) & EPxCS_RX_ST) {
uint16_t count = 0U;
usb_transc *transc = &udev->transc_out[ep_num];
/* clear successful receive interrupt flag */
USBD_EP_RX_ST_CLEAR(ep_num);
count = udev->drv_handler->ep_read (transc->xfer_buf, ep_num, (uint8_t)EP_BUF_DBL);
user_buffer_free(ep_num, (uint8_t)DBUF_EP_OUT);
transc->xfer_buf += count;
transc->xfer_count += count;
transc->xfer_len -= count;
if ((0U == transc->xfer_len) || (count < transc->max_len)) {
USBD_EP_RX_STAT_SET(ep_num, EPRX_NAK);
transc_num = (uint8_t)TRANSC_OUT;
}
}
} else {
/* handle the in direction transaction */
if (USBD_EPxCS(ep_num) & EPxCS_TX_ST) {
/* clear successful transmit interrupt flag */
USBD_EP_TX_ST_CLEAR(ep_num);
transc_num = (uint8_t)TRANSC_IN;
}
}
if ((uint8_t)TRANSC_UNKNOWN != transc_num) {
udev->ep_transc[ep_num][transc_num](udev, ep_num);
}
}
}
/*!
\brief USB interrupt events service routine
\param[in] none
\param[out] none
\retval none
*/
void usbd_isr (void)
{
__IO uint16_t int_status = (uint16_t)USBD_INTF;
__IO uint16_t int_flag = (uint16_t)(USBD_INTF & (USBD_CTL & USBD_INTEN));
usb_dev *udev = usbd_core.dev;
if (INTF_STIF & int_flag) {
/* wait till interrupts are not pending */
while ((int_status = (uint16_t)USBD_INTF) & (uint16_t)INTF_STIF) {
/* get endpoint number */
uint8_t ep_num = (uint8_t)(int_status & INTF_EPNUM);
if (int_status & INTF_DIR) {
/* handle the USB OUT direction transaction */
if (USBD_EPxCS(ep_num) & EPxCS_RX_ST) {
/* clear successful receive interrupt flag */
USBD_EP_RX_ST_CLEAR(ep_num);
if (USBD_EPxCS(ep_num) & EPxCS_SETUP) {
if (ep_num == 0U) {
udev->ep_transc[ep_num][TRANSC_SETUP](udev, ep_num);
} else {
return;
}
} else {
usb_transc *transc = &udev->transc_out[ep_num];
uint16_t count = udev->drv_handler->ep_read (transc->xfer_buf, ep_num, (uint8_t)EP_BUF_SNG);
transc->xfer_buf += count;
transc->xfer_count += count;
if ((transc->xfer_count >= transc->xfer_len) || (count < transc->max_len)) {
if (udev->ep_transc[ep_num][TRANSC_OUT]) {
udev->ep_transc[ep_num][TRANSC_OUT](udev, ep_num);
}
} else {
udev->drv_handler->ep_rx_enable(udev, ep_num);
}
}
}
} else {
/* handle the USB IN direction transaction */
if (USBD_EPxCS(ep_num) & EPxCS_TX_ST) {
/* clear successful transmit interrupt flag */
USBD_EP_TX_ST_CLEAR(ep_num);
usb_transc *transc = &udev->transc_in[ep_num];
if (transc->xfer_len == 0U) {
if (udev->ep_transc[ep_num][TRANSC_IN]) {
udev->ep_transc[ep_num][TRANSC_IN](udev, ep_num);
}
} else {
usbd_ep_send(udev, ep_num, transc->xfer_buf, transc->xfer_len);
}
}
}
}
}
if (INTF_WKUPIF & int_flag) {
/* clear wakeup interrupt flag in INTF */
CLR(WKUPIF);
/* restore the old cur_status */
udev->cur_status = udev->backup_status;
#ifdef LPM_ENABLED
if ((0U == udev->pm.remote_wakeup_on) && (0U == udev->lpm.L1_resume)) {
resume_mcu(udev);
} else if (1U == udev->pm.remote_wakeup_on) {
/* no operation */
} else {
udev->lpm.L1_resume = 0U;
}
/* clear L1 remote wakeup flag */
udev->lpm.L1_remote_wakeup = 0U;
#else
if (0U == udev->pm.remote_wakeup_on) {
resume_mcu(udev);
}
#endif /* LPM_ENABLED */
}
if (INTF_SPSIF & int_flag) {
if(!(USBD_CTL & CTL_RSREQ)) {
usbd_int_suspend (udev);
/* clear of suspend interrupt flag bit must be done after setting of CTLR_SETSPS */
CLR(SPSIF);
}
}
if (INTF_SOFIF & int_flag) {
/* clear SOF interrupt flag in INTF */
CLR(SOFIF);
/* if necessary, user can add code here */
if (NULL != usbd_int_fops) {
(void)usbd_int_fops->SOF(udev);
}
}
if (INTF_ESOFIF & int_flag) {
/* clear ESOF interrupt flag in INTF */
CLR(ESOFIF);
/* control resume time by ESOFs */
if (udev->pm.esof_count > 0U) {
if (0U == --udev->pm.esof_count) {
if (udev->pm.remote_wakeup_on) {
USBD_CTL &= ~CTL_RSREQ;
udev->pm.remote_wakeup_on = 0U;
} else {
USBD_CTL |= CTL_RSREQ;
udev->pm.esof_count = 3U;
udev->pm.remote_wakeup_on = 1U;
}
}
}
}
if (INTF_RSTIF & int_flag) {
/* clear reset interrupt flag in INTF */
CLR(RSTIF);
udev->drv_handler->ep_reset(udev);
}
#ifdef LPM_ENABLED
if (INTF_L1REQ & int_flag) {
/* clear L1 ST bit in LPM INTF */
USBD_INTF = CLR(L1REQ);
/* read BESL field from subendpoint0 register which corresponds to HIRD parameter in LPM spec */
udev->lpm.besl = (USBD_LPMCS & LPMCS_BLSTAT) >> 4;
/* read BREMOTEWAKE bit from subendpoint0 register which corresponding to bRemoteWake bit in LPM request */
udev->lpm.L1_remote_wakeup = (USBD_LPMCS & LPMCS_REMWK) >> 3;
/* process USB device core layer suspend routine */
usbd_int_suspend(udev);
}
#endif /* LPM_ENABLED */
}
/*!
\brief handle USB suspend event
\param[in] udev: pointer to USB device instance
\param[out] none
\retval none
*/
static void usbd_int_suspend (usb_dev *udev)
{
/* store the device current status */
udev->backup_status = udev->cur_status;
/* set device in suspended state */
udev->cur_status = (uint8_t)USBD_SUSPENDED;
/* usb enter in suspend mode and mcu system in low power mode */
if (udev->pm.suspend_enabled) {
usbd_to_suspend(udev);
} else {
/* if not possible then resume after xx ms */
udev->pm.esof_count = 3U;
}
}