添加GD32H7的CMake工程

This commit is contained in:
2026-07-23 19:44:30 +08:00
commit d07f065b1c
202 changed files with 204039 additions and 0 deletions
@@ -0,0 +1,318 @@
/*!
\file audio_core.h
\brief the header file of USB audio device class core functions
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#ifndef AUDIO_CORE_H
#define AUDIO_CORE_H
#include "usbd_enum.h"
#define FORMAT_24BIT(x) (uint8_t)(x);(uint8_t)((x) >> 8);(uint8_t)((x) >> 16)
/* number of sub-packets in the audio transfer buffer. you can modify this value but always make sure
that it is an even number and higher than 3 */
#define OUT_PACKET_NUM 200U
/* total size of the audio transfer buffer */
#define OUT_BUF_MARGIN 0U
#define TOTAL_OUT_BUF_SIZE ((uint32_t)((SPEAKER_OUT_PACKET + OUT_BUF_MARGIN) * OUT_PACKET_NUM))
/* audio configuration descriptor length and interface descriptor size */
#define AD_CONFIG_DESC_SET_LEN (sizeof(usb_desc_config_set))
#define AD_INTERFACE_DESC_SIZE 9U
/* audio standard endpoint and streaming endpoint descriptor size */
#define USB_AD_DESC_SIZ 0x09U /*!< audio descriptor size */
#define AD_STANDARD_EP_DESC_SIZE 0x09U /*!< audio standard endpoint descriptor size */
#define AD_STREAMING_EP_DESC_SIZE 0x07U /*!< audio streaming endpoint descriptor size */
/* audio interface class code */
#define USB_CLASS_AUDIO 0x01U
/* audio interface subclass codes */
#define AD_SUBCLASS_CONTROL 0x01U /*!< audio interface control */
#define AD_SUBCLASS_AUDIOSTREAMING 0x02U /*!< audio interface audiostreaming */
#define AD_SUBCLASS_MIDISTREAMING 0x03U /*!< audio interface midistreaming */
/* audio interface protocol codes */
#define AD_PROTOCOL_UNDEFINED 0x00U /*!< audio interface undefined */
#define AD_STREAMING_GENERAL 0x01U /*!< audio interface streaming general */
#define AD_STREAMING_FORMAT_TYPE 0x02U /*!< audio interface streaming format type */
/* audio class-specific descriptor types */
#define AD_DESCTYPE_UNDEFINED 0x20U /*!< audio class-specific descriptor undefined */
#define AD_DESCTYPE_DEVICE 0x21U /*!< audio device descriptor */
#define AD_DESCTYPE_CONFIGURATION 0x22U /*!< audio configuration descriptor */
#define AD_DESCTYPE_STRING 0x23U /*!< audio string descriptor */
#define AD_DESCTYPE_INTERFACE 0x24U /*!< audio interface descriptor */
#define AD_DESCTYPE_ENDPOINT 0x25U /*!< audio endpoint descriptor */
/* audio control interface descriptor subtypes */
#define AD_CONTROL_HEADER 0x01U /*!< audio control interface header descriptor */
#define AD_CONTROL_INPUT_TERMINAL 0x02U /*!< audio control interface input terminal descriptor */
#define AD_CONTROL_OUTPUT_TERMINAL 0x03U /*!< audio control interface output terminal descriptor */
#define AD_CONTROL_MIXER_UNIT 0x04U /*!< audio control interface maximum unit descriptor */
#define AD_CONTROL_SELECTOR_UNIT 0x05U /*!< audio control interface selector unit descriptor */
#define AD_CONTROL_FEATURE_UNIT 0x06U /*!< audio control interface feature unit descriptor */
#define AD_CONTROL_PROCESSING_UNIT 0x07U /*!< audio control interface processing unit descriptor */
#define AD_CONTROL_EXTENSION_UNIT 0x08U /*!< audio control interface extension unit descriptor */
/* audio input/output terminal and streaming interface descriptor size */
#define AD_INPUT_TERMINAL_DESC_SIZE 0x0CU /*!< audio input terminal interface descriptor size */
#define AD_OUTPUT_TERMINAL_DESC_SIZE 0x09U /*!< audio output terminal interface descriptor size */
#define AD_STREAMING_INTERFACE_DESC_SIZE 0x07U /*!< audio streaming interface descriptor size */
/* audio control types */
#define AD_CONTROL_MUTE 0x01U /*!< audio control mute type */
#define AD_CONTROL_VOLUME 0x02U /*!< audio control volume type */
/* audio format types */
#define AD_FORMAT_TYPE_I 0x01U /*!< audio format typeI */
#define AD_FORMAT_TYPE_III 0x03U /*!< audio format typeIII */
/* endpoint types */
#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01U /*!< audio isochronous endpoint type */
#define AD_ENDPOINT_GENERAL 0x01U /*!< audio general endpoint type */
/* audio request types */
#define AD_REQ_UNDEFINED 0x00U /*!< audio undefined request */
#define AD_REQ_SET_CUR 0x01U /*!< current setting attribute request */
#define AD_REQ_GET_CUR 0x81U /*!< current getting attribute request */
#define AD_REQ_SET_MIN 0x02U /*!< setting minimum range attribute request */
#define AD_REQ_GET_MIN 0x82U /*!< getting minimum range attribute request */
#define AD_REQ_SET_MAX 0x03U /*!< setting maximum range attribute request */
#define AD_REQ_GET_MAX 0x83U /*!< getting maximum range attribute request */
#define AD_REQ_SET_RES 0x04U /*!< setting range attribute request */
#define AD_REQ_GET_RES 0x84U /*!< getting range attribute request */
#define AD_REQ_SET_MEM 0x05U /*!< setting memory attribute request */
#define AD_REQ_GET_MEM 0x85U /*!< getting memory attribute request */
#define AD_REQ_GET_STAT 0xFFU /*!< getting state request */
/* streaming control types */
#define AD_OUT_STREAMING_CTRL 0x05U /*!< audio streaming control OUT */
#define AD_IN_STREAMING_CTRL 0x02U /*!< audio streaming control IN */
/* audio stream interface number */
enum {
#ifdef USE_USB_AD_MICPHONE
MIC_INTERFACE_COUNT, /*!< audio microphone interface count */
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
SPEAK_INTERFACE_COUNT, /*!< audio speaker interface count */
#endif /* USE_USB_AD_SPEAKER */
CONFIG_DESC_AS_ITF_COUNT /*!< audio system interface count */
};
/* audio interface descriptor total length */
#define AC_ITF_TOTAL_LEN (sizeof(usb_desc_AC_itf) + CONFIG_DESC_AS_ITF_COUNT*(sizeof(usb_desc_input_terminal) + \
sizeof(usb_desc_mono_feature_unit) + sizeof(usb_desc_output_terminal)))
#pragma pack(1)
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< header descriptor subtype */
uint16_t bcdADC; /*!< audio device class specification release number in binary-coded decimal */
uint16_t wTotalLength; /*!< total number of bytes */
uint8_t bInCollection; /*!< the number of the streaming interfaces */
#ifdef USE_USB_AD_MICPHONE
uint8_t baInterfaceNr0; /*!< interface number of the streaming interfaces */
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
uint8_t baInterfaceNr1; /*!< interface number of the streaming interfaces */
#endif /* USE_USB_AD_SPEAKER */
} usb_desc_AC_itf;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< general audio system descriptor subtype */
uint8_t bTerminalLink; /*!< the terminal ID */
uint8_t bDelay; /*!< delay introduced by the data path */
uint16_t wFormatTag; /*!< the audio data format */
} usb_desc_AS_itf;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< input terminal descriptor subtype. */
uint8_t bTerminalID; /*!< constant uniquely identifying the terminal within the audio function */
uint16_t wTerminalType; /*!< constant characterizing the type of terminal */
uint8_t bAssocTerminal; /*!< ID of the output terminal */
uint8_t bNrChannels; /*!< number of logical output channels */
uint16_t wChannelConfig; /*!< describes the spatial location of the logical channels */
uint8_t iChannelNames; /*!< index of a string descriptor */
uint8_t iTerminal; /*!< index of a string descriptor */
} usb_desc_input_terminal;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< output terminal descriptor subtype */
uint8_t bTerminalID; /*!< constant uniquely identifying the terminal within the audio function */
uint16_t wTerminalType; /*!< constant characterizing the type of terminal */
uint8_t bAssocTerminal; /*!< constant, identifying the input terminal to which this output terminal is associated */
uint8_t bSourceID; /*!< ID of the unit or terminal */
uint8_t iTerminal; /*!< index of a string descriptor */
} usb_desc_output_terminal;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< feature unit descriptor subtype */
uint8_t bUnitID; /*!< constant uniquely identifying the unit within the audio function */
uint8_t bSourceID; /*!< ID of the unit or terminal */
uint8_t bControlSize; /*!< size in bytes of an element of the bmaControls() array */
uint8_t bmaControls0; /*!< a bit set to 1 indicates that the mentioned control is supported for master channel 0 */
uint8_t bmaControls1; /*!< a bit set to 1 indicates that the mentioned control is supported for logical channel 1 */
uint8_t iFeature; /*!< index of a string descriptor */
} usb_desc_mono_feature_unit;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< feature unit descriptor subtype */
uint8_t bUnitID; /*!< constant uniquely identifying the unit within the audio function */
uint8_t bSourceID; /*!< ID of the unit or terminal */
uint8_t bControlSize; /*!< size in bytes of an element of the bmaControls() array */
uint16_t bmaControls0; /*!< a bit set to 1 indicates that the mentioned control is supported for master channel 0 */
uint16_t bmaControls1; /*!< a bit set to 1 indicates that the mentioned control is supported for logical channel 1 */
uint16_t bmaControls2; /*!< a bit set to 1 indicates that the mentioned control is supported for logical channel 2 */
uint8_t iFeature; /*!< index of a string descriptor */
} usb_desc_stereo_feature_unit;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< format type descriptor subtype */
uint8_t bFormatType; /*!< constant identifying the format type */
uint8_t bNrChannels; /*!< indicates the number of physical channels in the audio data stream */
uint8_t bSubFrameSize; /*!< the number of bytes occupied by one audio subframe */
uint8_t bBitResolution; /*!< the number of effectively used bits from the available bits in an audio subframe */
uint8_t bSamFreqType; /*!< indicates how the sampling frequency can be programmed */
uint8_t bSamFreq[3]; /*!< sampling frequency ns in Hz for this isochronous data endpoint */
} usb_desc_format_type;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bEndpointAddress; /*!< the address of the endpoint */
uint8_t bmAttributes; /*!< transfer type and synchronization type */
uint16_t wMaxPacketSize; /*!< maximum packet size this endpoint is capable of sending or receiving */
uint8_t bInterval; /*!< left to the designer's discretion */
uint8_t bRefresh; /*!< reset to 0 */
uint8_t bSynchAddress; /*!< reset to 0 */
} usb_desc_std_ep;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bDescriptorSubtype; /*!< general endpoint descriptor subtype */
uint8_t bmAttributes; /*!< transfer type and synchronization type */
uint8_t bLockDelayUnits; /*!< indicates the units used for the lock delay field */
uint16_t wLockDelay; /*!< indicates the time it takes this endpoint to reliably lock its internal clock recovery circuitry */
} usb_desc_AS_ep;
typedef struct {
usb_desc_header header; /*!< descriptor header, including type and size */
uint8_t bEndpointAddress; /*!< general endpoint descriptor subtype */
uint8_t bmAttributes; /*!< transfer type and synchronization type */
uint16_t wMaxPacketSize; /*!< maximum packet size this endpoint is capable of sending or receiving */
uint8_t bInterval; /*!< polling interval in milliseconds for the endpoint if it is an interrupt or isochronous type */
uint8_t Refresh; /*!< refresh 1~9, power of 2 */
uint8_t bSynchAddress; /*!< synchronous address */
} usb_desc_FeedBack_ep;
#pragma pack()
/* USB configuration descriptor structure */
typedef struct {
usb_desc_config config; /*!< configuration descriptor */
usb_desc_itf std_itf; /*!< interface descriptor */
usb_desc_AC_itf ac_itf; /*!< audio controller interface descriptor */
#ifdef USE_USB_AD_MICPHONE
usb_desc_input_terminal mic_in_terminal; /*!< microphone input terminal descriptor */
usb_desc_mono_feature_unit mic_feature_unit; /*!< microphone feature unit descriptor */
usb_desc_output_terminal mic_out_terminal; /*!< microphone output terminal descriptor */
#endif
#ifdef USE_USB_AD_SPEAKER
usb_desc_input_terminal speak_in_terminal; /*!< speaker input terminal descriptor */
usb_desc_mono_feature_unit speak_feature_unit; /*!< speaker feature unit descriptor */
usb_desc_output_terminal speak_out_terminal; /*!< speaker output terminal descriptor */
#endif /* USE_USB_AD_SPEAKER */
#ifdef USE_USB_AD_MICPHONE
usb_desc_itf mic_std_as_itf_zeroband; /*!< microphone zeroband configuration standard audio streaming interface descriptor */
usb_desc_itf mic_std_as_itf_opera; /*!< microphone standard audio streaming interface descriptor */
usb_desc_AS_itf mic_as_itf; /*!< microphone audio correlation descriptor */
usb_desc_format_type mic_format_typeI; /*!< microphone typeI format type descriptor */
usb_desc_std_ep mic_std_endpoint; /*!< microphone standard endpoint descriptor */
usb_desc_AS_ep mic_as_endpoint; /*!< microphone audio dependent isochronous data endpoint descriptor */
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
usb_desc_itf speak_std_as_itf_zeroband; /*!< speaker zeroband configuration standard audio streaming interface descriptor */
usb_desc_itf speak_std_as_itf_opera; /*!< speaker standard audio streaming interface descriptor */
usb_desc_AS_itf speak_as_itf; /*!< speaker audio correlation descriptor */
usb_desc_format_type speak_format_typeI; /*!< speaker typeI format type descriptor */
usb_desc_std_ep speak_std_endpoint; /*!< speaker standard endpoint descriptor */
usb_desc_AS_ep speak_as_endpoint; /*!< speaker audio dependent isochronous data endpoint descriptor */
usb_desc_FeedBack_ep speak_feedback_endpoint; /*!< speaker feedback endpoint descriptor */
#endif /* USE_USB_AD_SPEAKER */
} usb_desc_config_set;
typedef struct {
/* main buffer for audio data OUT transfers and its relative pointers */
uint8_t isoc_out_buff[TOTAL_OUT_BUF_SIZE]; /*!< audio isochronous OUT data buff */
uint8_t* isoc_out_wrptr; /*!< audio isochronous OUT data write pointer */
uint8_t* isoc_out_rdptr; /*!< audio isochronous OUT data read pointer */
uint16_t buf_free_size; /*!< audio data buff free size */
uint16_t dam_tx_len; /*!< audio amplifier transmit length */
__IO uint32_t actual_freq; /*!< audio actual frequency */
__IO uint8_t play_flag; /*!< audio play flag */
uint8_t feedback_freq[3] __attribute__ ((aligned (4))); /*!< audio feedback frequency */
uint32_t cur_sam_freq; /*!< audio current sampling frequency */
/* USB receive buffer */
uint8_t usb_rx_buffer[SPEAKER_OUT_MAX_PACKET];
/* main buffer for audio control requests transfers and its relative variables */
uint8_t audioctl[64]; /*!< audio control requests transfers buff */
uint8_t audioctl_unit; /*!< audio control requests unit */
uint32_t audioctl_len; /*!< audio control requests length */
} usbd_audio_handler;
extern usb_desc audio_desc;
extern usb_class_core usbd_audio_cb;
extern usbd_audio_handler audio_handler;
#endif /* AUDIO_CORE_H */
@@ -0,0 +1,48 @@
/*!
\file audio_out_itf.h
\brief audio OUT (playback) interface header file
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#ifndef AUDIO_OUT_ITF_H
#define AUDIO_OUT_ITF_H
#include "usbd_conf.h"
typedef struct {
uint8_t (*audio_init)(uint32_t audio_freq, uint32_t volume);
uint8_t (*audio_deinit)(void);
uint8_t (*audio_cmd)(uint8_t* pbuf, uint32_t size, uint8_t cmd);
} audio_fops_struct;
extern audio_fops_struct audio_out_fops;
#endif /* AUDIO_OUT_ITF_H */
@@ -0,0 +1,952 @@
/*!
\file audio_core.c
\brief USB audio device class core functions
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "audio_out_itf.h"
#include "audio_core.h"
#include <string.h>
#define USBD_VID 0x28E9U
#define USBD_PID 0x9574U
#define VOL_MIN 0U /* volume minimum value */
#define VOL_MAX 100U /* volume maximum value */
#define VOL_RES 1U /* volume resolution */
#define VOL_0dB 70U /* 0dB is in the middle of VOL_MIN and VOL_MAX */
#ifdef USE_USB_AD_MICPHONE
#define LENGTH_DATA (1747 * 32)
extern volatile uint32_t count_data;
extern const char wavetestdata[];
#endif /* USE_USB_AD_MICPHONE */
__ALIGN_BEGIN usbd_audio_handler audio_handler __ALIGN_END;
/* local function prototypes ('static') */
static uint8_t audio_init(usb_dev *udev, uint8_t config_index);
static uint8_t audio_deinit(usb_dev *udev, uint8_t config_index);
static uint8_t audio_req_handler(usb_dev *udev, usb_req *req);
static uint8_t audio_set_intf(usb_dev *udev, usb_req *req);
static uint8_t audio_ctlx_out(usb_dev *udev);
static uint8_t audio_data_in(usb_dev *udev, uint8_t ep_num);
static uint8_t audio_data_out(usb_dev *udev, uint8_t ep_num);
static uint8_t audio_sof(usb_dev *udev);
static uint8_t audio_iso_in_incomplete(usb_dev *udev);
static uint8_t audio_iso_out_incomplete(usb_dev *udev);
static uint32_t usbd_audio_spk_get_feedback(usb_dev *udev);
static void get_feedback_fs_rate(uint32_t rate, uint8_t *buf);
usb_class_core usbd_audio_cb = {
.init = audio_init,
.deinit = audio_deinit,
.req_proc = audio_req_handler,
.set_intf = audio_set_intf,
.ctlx_out = audio_ctlx_out,
.data_in = audio_data_in,
.data_out = audio_data_out,
.SOF = audio_sof,
.incomplete_isoc_in = audio_iso_in_incomplete,
.incomplete_isoc_out = audio_iso_out_incomplete
};
/* note:it should use the c99 standard when compiling the below codes */
/* USB standard device descriptor */
__ALIGN_BEGIN const usb_desc_dev audio_dev_desc __ALIGN_END = {
.header =
{
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
.bcdUSB = 0x0200U,
.bDeviceClass = 0x00U,
.bDeviceSubClass = 0x00U,
.bDeviceProtocol = 0x00U,
.bMaxPacketSize0 = USB_FS_EP0_MAX_LEN,
.idVendor = USBD_VID,
.idProduct = USBD_PID,
.bcdDevice = 0x0100U,
.iManufacturer = STR_IDX_MFC,
.iProduct = STR_IDX_PRODUCT,
.iSerialNumber = STR_IDX_SERIAL,
.bNumberConfigurations = USBD_CFG_MAX_NUM
};
/* USB device configuration descriptor */
__ALIGN_BEGIN const usb_desc_config_set audio_config_set __ALIGN_END = {
.config =
{
.header =
{
.bLength = sizeof(usb_desc_config),
.bDescriptorType = USB_DESCTYPE_CONFIG
},
.wTotalLength = AD_CONFIG_DESC_SET_LEN,
.bNumInterfaces = 0x01U + CONFIG_DESC_AS_ITF_COUNT,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
.bmAttributes = 0xC0U,
.bMaxPower = 0x32U
},
.std_itf =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_CONTROL,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.ac_itf =
{
.header =
{
.bLength = sizeof(usb_desc_AC_itf),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = 0x01U,
.bcdADC = 0x0100U,
.wTotalLength = AC_ITF_TOTAL_LEN,
.bInCollection = CONFIG_DESC_AS_ITF_COUNT,
#ifdef USE_USB_AD_MICPHONE
.baInterfaceNr0 = 0x01U,
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
.baInterfaceNr1 = 0x02U
#endif /* USE_USB_AD_SPEAKER */
},
#ifdef USE_USB_AD_MICPHONE
.mic_in_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_input_terminal),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = 0x02U,
.bTerminalID = 0x01U,
.wTerminalType = 0x0201U,
.bAssocTerminal = 0x00U,
.bNrChannels = 0x02U,
.wChannelConfig = 0x0003U,
.iChannelNames = 0x00U,
.iTerminal = 0x00U
},
.mic_feature_unit =
{
.header =
{
.bLength = sizeof(usb_desc_mono_feature_unit),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_FEATURE_UNIT,
.bUnitID = AD_IN_STREAMING_CTRL,
.bSourceID = 0x01U,
.bControlSize = 0x01U,
.bmaControls0 = AD_CONTROL_MUTE | AD_CONTROL_VOLUME,
.bmaControls1 = 0x00U,
.iFeature = 0x00U
},
.mic_out_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_output_terminal),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_OUTPUT_TERMINAL,
.bTerminalID = 0x03U,
.wTerminalType = 0x0101U,
.bAssocTerminal = 0x00U,
.bSourceID = 0x02U,
.iTerminal = 0x00U
},
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
.speak_in_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_input_terminal),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_INPUT_TERMINAL,
.bTerminalID = 0x04U,
.wTerminalType = 0x0101U,
.bAssocTerminal = 0x00U,
.bNrChannels = 0x02U,
.wChannelConfig = 0x0003U,
.iChannelNames = 0x00U,
.iTerminal = 0x00U
},
.speak_feature_unit =
{
.header =
{
.bLength = sizeof(usb_desc_mono_feature_unit),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_FEATURE_UNIT,
.bUnitID = AD_OUT_STREAMING_CTRL,
.bSourceID = 0x04U,
.bControlSize = 0x01U,
.bmaControls0 = AD_CONTROL_MUTE | AD_CONTROL_VOLUME,
.bmaControls1 = 0x00U,
.iFeature = 0x00U
},
.speak_out_terminal =
{
.header =
{
.bLength = sizeof(usb_desc_output_terminal),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_CONTROL_OUTPUT_TERMINAL,
.bTerminalID = 0x06U,
.wTerminalType = 0x0301U,
.bAssocTerminal = 0x00U,
.bSourceID = 0x05U,
.iTerminal = 0x00U
},
#endif /* USE_USB_AD_SPEAKER */
#ifdef USE_USB_AD_MICPHONE
.mic_std_as_itf_zeroband =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x01U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.mic_std_as_itf_opera =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x01U,
.bAlternateSetting = 0x01U,
.bNumEndpoints = 0x01U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.mic_as_itf =
{
.header =
{
.bLength = sizeof(usb_desc_AS_itf),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_STREAMING_GENERAL,
.bTerminalLink = 0x03U,
.bDelay = 0x01U,
.wFormatTag = 0x0001U
},
.mic_format_typeI =
{
.header =
{
.bLength = sizeof(usb_desc_format_type),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_STREAMING_FORMAT_TYPE,
.bFormatType = AD_FORMAT_TYPE_I,
.bNrChannels = MIC_IN_CHANNEL_NBR,
.bSubFrameSize = 0x02U,
.bBitResolution = MIC_IN_BIT_RESOLUTION,
.bSamFreqType = 0x01U,
.bSamFreq[0] = (uint8_t)USBD_MIC_FREQ,
.bSamFreq[1] = USBD_MIC_FREQ >> 8,
.bSamFreq[2] = USBD_MIC_FREQ >> 16
},
.mic_std_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_std_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = AD_IN_EP,
.bmAttributes = USB_ENDPOINT_TYPE_ISOCHRONOUS,
.wMaxPacketSize = MIC_IN_PACKET,
.bInterval = 0x01U,
.bRefresh = 0x00U,
.bSynchAddress = 0x00U
},
.mic_as_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_AS_ep),
.bDescriptorType = AD_DESCTYPE_ENDPOINT
},
.bDescriptorSubtype = AD_ENDPOINT_GENERAL,
.bmAttributes = 0x00U,
.bLockDelayUnits = 0x00U,
.wLockDelay = 0x0000U
},
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
.speak_std_as_itf_zeroband =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x02U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x00U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.speak_std_as_itf_opera =
{
.header =
{
.bLength = sizeof(usb_desc_itf),
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x02U,
.bAlternateSetting = 0x01U,
.bNumEndpoints = 0x02U,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = AD_SUBCLASS_AUDIOSTREAMING,
.bInterfaceProtocol = AD_PROTOCOL_UNDEFINED,
.iInterface = 0x00U
},
.speak_as_itf =
{
.header =
{
.bLength = sizeof(usb_desc_AS_itf),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_STREAMING_GENERAL,
.bTerminalLink = 0x04U,
.bDelay = 0x01U,
.wFormatTag = 0x0001U
},
.speak_format_typeI =
{
.header =
{
.bLength = sizeof(usb_desc_format_type),
.bDescriptorType = AD_DESCTYPE_INTERFACE
},
.bDescriptorSubtype = AD_STREAMING_FORMAT_TYPE,
.bFormatType = AD_FORMAT_TYPE_I,
.bNrChannels = SPEAKER_OUT_CHANNEL_NBR,
.bSubFrameSize = 0x02U,
.bBitResolution = SPEAKER_OUT_BIT_RESOLUTION,
.bSamFreqType = 0x01U,
.bSamFreq[0] = (uint8_t)USBD_SPEAKER_FREQ,
.bSamFreq[1] = USBD_SPEAKER_FREQ >> 8,
.bSamFreq[2] = USBD_SPEAKER_FREQ >> 16
},
.speak_std_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_std_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = AD_OUT_EP,
.bmAttributes = USB_EP_ATTR_ISO | USB_EP_ATTR_ASYNC,
.wMaxPacketSize = SPEAKER_OUT_PACKET,
.bInterval = 0x01U,
.bRefresh = 0x00U,
.bSynchAddress = AD_FEEDBACK_IN_EP
},
.speak_as_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_AS_ep),
.bDescriptorType = AD_DESCTYPE_ENDPOINT
},
.bDescriptorSubtype = AD_ENDPOINT_GENERAL,
.bmAttributes = 0x00U,
.bLockDelayUnits = 0x00U,
.wLockDelay = 0x0000U
},
.speak_feedback_endpoint =
{
.header =
{
.bLength = sizeof(usb_desc_FeedBack_ep),
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = AD_FEEDBACK_IN_EP,
.bmAttributes = USB_EP_ATTR_ISO | USB_EP_ATTR_ASYNC | USB_EP_ATTR_FEEDBACK,
.wMaxPacketSize = FEEDBACK_IN_PACKET,
.bInterval = 0x01U,
.Refresh = FEEDBACK_IN_INTERVAL, /* refresh every 32(2^5) ms */
.bSynchAddress = 0x00U
},
#endif /* USE_USB_AD_SPEAKER */
};
/* USB language ID descriptor */
static __ALIGN_BEGIN const usb_desc_LANGID usbd_language_id_desc __ALIGN_END = {
.header =
{
.bLength = sizeof(usb_desc_LANGID),
.bDescriptorType = USB_DESCTYPE_STR
},
.wLANGID = ENG_LANGID
};
/* USB manufacture string */
static __ALIGN_BEGIN const usb_desc_str manufacturer_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(10U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'G', 'i', 'g', 'a', 'D', 'e', 'v', 'i', 'c', 'e'}
};
/* USB product string */
static __ALIGN_BEGIN const usb_desc_str product_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(14U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'G', 'D', '3', '2', '-', 'U', 'S', 'B', '_', 'A', 'u', 'd', 'i', 'o'}
};
/* USBD serial string */
static __ALIGN_BEGIN usb_desc_str serial_string __ALIGN_END = {
.header =
{
.bLength = USB_STRING_LEN(12U),
.bDescriptorType = USB_DESCTYPE_STR,
}
};
/* USB string descriptor */
void *const usbd_audio_strings[] = {
[STR_IDX_LANGID] = (uint8_t *)&usbd_language_id_desc,
[STR_IDX_MFC] = (uint8_t *)&manufacturer_string,
[STR_IDX_PRODUCT] = (uint8_t *)&product_string,
[STR_IDX_SERIAL] = (uint8_t *)&serial_string
};
/* USB descriptor configure */
usb_desc audio_desc = {
.dev_desc = (uint8_t *)&audio_dev_desc,
.config_desc = (uint8_t *)&audio_config_set,
.strings = usbd_audio_strings
};
/*!
\brief initialize the AUDIO device
\param[in] udev: pointer to USB device instance
\param[in] config_index: configuration index
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_init(usb_dev *udev, uint8_t config_index)
{
memset((void *)&audio_handler, 0U, sizeof(usbd_audio_handler));
#ifdef USE_USB_AD_MICPHONE
{
usb_desc_std_ep std_ep = audio_config_set.mic_std_endpoint;
usb_desc_ep ep = {
.header = std_ep.header,
.bEndpointAddress = std_ep.bEndpointAddress,
.bmAttributes = std_ep.bmAttributes,
.wMaxPacketSize = std_ep.wMaxPacketSize,
.bInterval = std_ep.bInterval
};
/* initialize TX endpoint */
usbd_ep_setup(udev, &ep);
}
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
{
audio_handler.isoc_out_rdptr = audio_handler.isoc_out_buff;
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
usb_desc_std_ep std_ep = audio_config_set.speak_std_endpoint;
usb_desc_ep ep1 = {
.header = std_ep.header,
.bEndpointAddress = std_ep.bEndpointAddress,
.bmAttributes = std_ep.bmAttributes,
.wMaxPacketSize = SPEAKER_OUT_MAX_PACKET,
.bInterval = std_ep.bInterval
};
/* initialize RX endpoint */
usbd_ep_setup(udev, &ep1);
/* prepare OUT endpoint to receive next audio packet */
usbd_ep_recev(udev, AD_OUT_EP, audio_handler.usb_rx_buffer, SPEAKER_OUT_MAX_PACKET);
/* initialize the audio output hardware layer */
if(USBD_OK != audio_out_fops.audio_init(USBD_SPEAKER_FREQ, DEFAULT_VOLUME)) {
return USBD_FAIL;
}
usb_desc_FeedBack_ep feedback_ep = audio_config_set.speak_feedback_endpoint;
usb_desc_ep ep2 = {
.header = feedback_ep.header,
.bEndpointAddress = feedback_ep.bEndpointAddress,
.bmAttributes = feedback_ep.bmAttributes,
.wMaxPacketSize = feedback_ep.wMaxPacketSize,
.bInterval = feedback_ep.bInterval
};
/* initialize TX endpoint */
usbd_ep_setup(udev, &ep2);
}
#endif /* USE_USB_AD_SPEAKER */
return USBD_OK;
}
/*!
\brief de-initialize the AUDIO device
\param[in] udev: pointer to USB device instance
\param[in] config_index: configuration index
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_deinit(usb_dev *udev, uint8_t config_index)
{
#ifdef USE_USB_AD_MICPHONE
/* deinitialize AUDIO endpoints */
usbd_ep_clear(udev, AD_IN_EP);
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
/* deinitialize AUDIO endpoints */
usbd_ep_clear(udev, AD_OUT_EP);
/* deinitialize the audio output hardware layer */
if(USBD_OK != audio_out_fops.audio_deinit()) {
return USBD_FAIL;
}
/* deinitialize AUDIO endpoints */
usbd_ep_clear(udev, AD_FEEDBACK_IN_EP);
#endif /* USE_USB_AD_SPEAKER */
return USBD_OK;
}
/*!
\brief handle the AUDIO class-specific requests
\param[in] udev: pointer to USB device instance
\param[in] req: device class-specific request
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_req_handler(usb_dev *udev, usb_req *req)
{
uint8_t status = REQ_NOTSUPP;
usb_transc *transc_in = &udev->dev.transc_in[0];
usb_transc *transc_out = &udev->dev.transc_out[0];
switch(req->bRequest) {
case AD_REQ_GET_CUR:
transc_in->xfer_buf = audio_handler.audioctl;
transc_in->remain_len = req->wLength;
status = REQ_SUPP;
break;
case AD_REQ_SET_CUR:
if(req->wLength) {
transc_out->xfer_buf = audio_handler.audioctl;
transc_out->remain_len = req->wLength;
udev->dev.class_core->command = AD_REQ_SET_CUR;
audio_handler.audioctl_len = req->wLength;
audio_handler.audioctl_unit = BYTE_HIGH(req->wIndex);
status = REQ_SUPP;
}
break;
case AD_REQ_GET_MIN:
*((uint16_t *)audio_handler.audioctl) = VOL_MIN;
transc_in->xfer_buf = audio_handler.audioctl;
transc_in->remain_len = req->wLength;
status = REQ_SUPP;
break;
case AD_REQ_GET_MAX:
*((uint16_t *)audio_handler.audioctl) = VOL_MAX;
transc_in->xfer_buf = audio_handler.audioctl;
transc_in->remain_len = req->wLength;
status = REQ_SUPP;
break;
case AD_REQ_GET_RES:
*((uint16_t *)audio_handler.audioctl) = VOL_RES;
transc_in->xfer_buf = audio_handler.audioctl;
transc_in->remain_len = req->wLength;
status = REQ_SUPP;
break;
default:
break;
}
return status;
}
/*!
\brief handle the AUDIO set interface requests
\param[in] udev: pointer to USB device instance
\param[in] req: device class-specific request
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_set_intf(usb_dev *udev, usb_req *req)
{
udev->dev.class_core->alter_set = req->wValue;
if(0xFFU != req->wValue) {
if(0U != req->wValue) {
/* deinit audio handler */
memset((void *)&audio_handler, 0U, sizeof(usbd_audio_handler));
audio_handler.play_flag = 0U;
audio_handler.isoc_out_rdptr = audio_handler.isoc_out_buff;
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
/* feedback calculate sample frequency */
audio_handler.actual_freq = I2S_ACTUAL_SAM_FREQ(USBD_SPEAKER_FREQ);
get_feedback_fs_rate(audio_handler.actual_freq, audio_handler.feedback_freq);
/* send feedback data of estimated frequency */
usbd_ep_send(udev, AD_FEEDBACK_IN_EP, audio_handler.feedback_freq, FEEDBACK_IN_PACKET);
} else {
/* stop audio output */
audio_out_fops.audio_cmd(audio_handler.isoc_out_rdptr, SPEAKER_OUT_PACKET / 2U, AD_CMD_STOP);
audio_handler.play_flag = 0U;
audio_handler.isoc_out_rdptr = audio_handler.isoc_out_buff;
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
usbd_fifo_flush(udev, AD_IN_EP);
usbd_fifo_flush(udev, AD_FEEDBACK_IN_EP);
usbd_fifo_flush(udev, AD_OUT_EP);
}
}
return 0U;
}
/*!
\brief handles the control transfer OUT callback
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_ctlx_out(usb_dev *udev)
{
#ifdef USE_USB_AD_SPEAKER
/* handles audio control requests data */
/* check if an audio_control request has been issued */
if(AD_REQ_SET_CUR == udev->dev.class_core->command) {
/* in this driver, to simplify code, only SET_CUR request is managed */
/* check for which addressed unit the audio_control request has been issued */
if(AD_OUT_STREAMING_CTRL == audio_handler.audioctl_unit) {
/* in this driver, to simplify code, only one unit is manage */
/* reset the audioctl_cmd variable to prevent re-entering this function */
udev->dev.class_core->command = 0U;
audio_handler.audioctl_len = 0U;
}
}
#endif /* USE_USB_AD_SPEAKER */
return USBD_OK;
}
/*!
\brief handles the audio IN data stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_data_in(usb_dev *udev, uint8_t ep_num)
{
#ifdef USE_USB_AD_MICPHONE
if(EP_ID(AD_IN_EP) == ep_num) {
if(count_data < LENGTH_DATA) {
/* Prepare next buffer to be sent: dummy data */
usbd_ep_send(udev, AD_IN_EP, (uint8_t *)&wavetestdata[count_data], MIC_IN_PACKET);
count_data += MIC_IN_PACKET;
} else {
usbd_ep_send(udev, AD_IN_EP, (uint8_t *)wavetestdata, MIC_IN_PACKET);
count_data = MIC_IN_PACKET;
}
}
#endif /* USE_USB_AD_MICPHONE */
#ifdef USE_USB_AD_SPEAKER
if(EP_ID(AD_FEEDBACK_IN_EP) == ep_num) {
/* calculate feedback actual freq */
audio_handler.actual_freq = usbd_audio_spk_get_feedback(udev);
get_feedback_fs_rate(audio_handler.actual_freq, audio_handler.feedback_freq);
usbd_ep_send(udev, AD_FEEDBACK_IN_EP, audio_handler.feedback_freq, FEEDBACK_IN_PACKET);
}
#endif /* USE_USB_AD_SPEAKER */
return USBD_OK;
}
/*!
\brief handles the audio OUT data stage
\param[in] udev: pointer to USB device instance
\param[in] ep_num: endpoint number
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_data_out(usb_dev *udev, uint8_t ep_num)
{
uint16_t usb_rx_length, tail_len;
/* get receive length */
usb_rx_length = ((usb_core_driver *)udev)->dev.transc_out[ep_num].xfer_count;
if(audio_handler.isoc_out_wrptr >= audio_handler.isoc_out_rdptr) {
audio_handler.buf_free_size = TOTAL_OUT_BUF_SIZE + audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
} else {
audio_handler.buf_free_size = audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
}
/* free buffer enough to save RX data */
if(audio_handler.buf_free_size > usb_rx_length) {
if(audio_handler.isoc_out_wrptr >= audio_handler.isoc_out_rdptr) {
tail_len = audio_handler.isoc_out_buff + TOTAL_OUT_BUF_SIZE - audio_handler.isoc_out_wrptr;
if(tail_len >= usb_rx_length) {
memcpy(audio_handler.isoc_out_wrptr, audio_handler.usb_rx_buffer, usb_rx_length);
/* increment the buffer pointer */
audio_handler.isoc_out_wrptr += usb_rx_length;
/* increment the Buffer pointer or roll it back when all buffers are full */
if(audio_handler.isoc_out_wrptr >= (audio_handler.isoc_out_buff + TOTAL_OUT_BUF_SIZE)) {
/* all buffers are full: roll back */
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
}
} else {
memcpy(audio_handler.isoc_out_wrptr, audio_handler.usb_rx_buffer, tail_len);
/* adjust write pointer */
audio_handler.isoc_out_wrptr = audio_handler.isoc_out_buff;
memcpy(audio_handler.isoc_out_wrptr, &audio_handler.usb_rx_buffer[tail_len], usb_rx_length - tail_len);
/* adjust write pointer */
audio_handler.isoc_out_wrptr += usb_rx_length - tail_len;
}
} else {
memcpy(audio_handler.isoc_out_wrptr, audio_handler.usb_rx_buffer, usb_rx_length);
/* increment the buffer pointer */
audio_handler.isoc_out_wrptr += usb_rx_length;
}
}
/* toggle the frame index */
udev->dev.transc_out[ep_num].frame_num = (udev->dev.transc_out[ep_num].frame_num) ? 0U : 1U;
/* prepare OUT endpoint to receive next audio packet */
usbd_ep_recev(udev, AD_OUT_EP, audio_handler.usb_rx_buffer, SPEAKER_OUT_MAX_PACKET);
if(audio_handler.isoc_out_wrptr >= audio_handler.isoc_out_rdptr) {
audio_handler.buf_free_size = TOTAL_OUT_BUF_SIZE + audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
} else {
audio_handler.buf_free_size = audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
}
if((0U == audio_handler.play_flag) && (audio_handler.buf_free_size < TOTAL_OUT_BUF_SIZE / 2U)) {
/* enable start of streaming */
audio_handler.play_flag = 1U;
/* initialize the audio output hardware layer */
if(USBD_OK != audio_out_fops.audio_cmd(audio_handler.isoc_out_rdptr, SPEAKER_OUT_MAX_PACKET / 2U, AD_CMD_PLAY)) {
return USBD_FAIL;
}
audio_handler.dam_tx_len = SPEAKER_OUT_MAX_PACKET;
}
return USBD_OK;
}
/*!
\brief handles the SOF event (data buffer update and synchronization)
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_sof(usb_dev *udev)
{
return USBD_OK;
}
/*!
\brief handles the audio ISO IN Incomplete event
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_iso_in_incomplete(usb_dev *udev)
{
(void)usb_txfifo_flush(&udev->regs, EP_ID(AD_FEEDBACK_IN_EP));
audio_handler.actual_freq = usbd_audio_spk_get_feedback(udev);
get_feedback_fs_rate(audio_handler.actual_freq, audio_handler.feedback_freq);
/* send feedback data of estimated frequency */
usbd_ep_send(udev, AD_FEEDBACK_IN_EP, audio_handler.feedback_freq, FEEDBACK_IN_PACKET);
return USBD_OK;
}
/*!
\brief handles the audio ISO OUT Incomplete event
\param[in] udev: pointer to USB device instance
\param[out] none
\retval USB device operation status
*/
static uint8_t audio_iso_out_incomplete(usb_dev *udev)
{
return USBD_OK;
}
/*!
\brief calculate feedback sample frequency
\param[in] udev: pointer to USB device instance
\param[out] none
\retval feedback frequency value
*/
static uint32_t usbd_audio_spk_get_feedback(usb_dev *udev)
{
static uint32_t fb_freq;
/* calculate buffer free size */
if(audio_handler.isoc_out_wrptr >= audio_handler.isoc_out_rdptr) {
audio_handler.buf_free_size = TOTAL_OUT_BUF_SIZE + audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
} else {
audio_handler.buf_free_size = audio_handler.isoc_out_rdptr - audio_handler.isoc_out_wrptr;
}
/* calculate feedback frequency */
if(audio_handler.buf_free_size <= (TOTAL_OUT_BUF_SIZE / 4U)) {
fb_freq = I2S_ACTUAL_SAM_FREQ(USBD_SPEAKER_FREQ) - FEEDBACK_FREQ_OFFSET;
} else if(audio_handler.buf_free_size >= (TOTAL_OUT_BUF_SIZE * 3U / 4U)) {
fb_freq = I2S_ACTUAL_SAM_FREQ(USBD_SPEAKER_FREQ) + FEEDBACK_FREQ_OFFSET;
} else {
fb_freq = I2S_ACTUAL_SAM_FREQ(USBD_SPEAKER_FREQ);
}
return fb_freq;
}
/*!
\brief get feedback value from rate in USB full speed
\param[in] rate: sample frequency
\param[in] buf: pointer to result buffer
\param[out] none
\retval none
*/
static void get_feedback_fs_rate(uint32_t rate, uint8_t *buf)
{
rate = ((rate / 1000U) << 14) | ((rate % 1000U) << 4);
buf[0] = rate;
buf[1] = rate >> 8;
buf[2] = rate >> 16;
}
@@ -0,0 +1,166 @@
/*!
\file audio_out_itf.c
\brief audio OUT (playback) interface functions
\version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/
/*
Copyright (c) 2025, GigaDevice Semiconductor Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#include "audio_out_itf.h"
/* local variable defines */
static uint8_t audio_state = AD_STATE_INACTIVE;
/* local function prototypes ('static') */
static uint8_t init(uint32_t audio_freq, uint32_t volume);
static uint8_t deinit(void);
static uint8_t audio_cmd(uint8_t* pbuf, uint32_t size, uint8_t cmd);
audio_fops_struct audio_out_fops = {
.audio_init = init,
.audio_deinit = deinit,
.audio_cmd = audio_cmd,
};
/*!
\brief initialize and configures all required resources
\param[in] audio_freq: statrt_up audio frequency
\param[in] volume: start_up volume to be set
\param[out] none
\retval AD_OK if all operations succeed, otherwise, AD_FAIL.
*/
static uint8_t init(uint32_t audio_freq, uint32_t volume)
{
static uint32_t initialized = 0U;
/* check if the low layer has already been initialized */
if(0U == initialized) {
/* initialize GPIO */
codec_gpio_init();
/* initialize I2S */
codec_audio_interface_init(audio_freq);
/* initialize DMA */
codec_dma_init();
/* prevent reinitializing the interface again */
initialized = 1U;
}
/* update the audio state machine */
audio_state = AD_STATE_ACTIVE;
return AD_OK;
}
/*!
\brief free all resources used by low layer and stops audio-play function
\param[in] none
\param[out] none
\retval AD_OK if all operations succeed, otherwise, AD_FAIL.
*/
static uint8_t deinit(void)
{
/* update the audio state machine */
audio_state = AD_STATE_INACTIVE;
return AD_OK;
}
/*!
\brief play, stop, pause or resume current file
\param[in] pbuf: address from which file should be played
\param[in] size: size of the current buffer/file
\param[in] cmd: command to be executed, can be:
\arg AD_CMD_PLAY
\arg AD_CMD_PAUSE
\arg AD_CMD_RESUME
\arg AD_CMD_STOP
\param[out] none
\retval AD_OK if all operations succeed, otherwise, AD_FAIL.
*/
static uint8_t audio_cmd(uint8_t* pbuf, uint32_t size, uint8_t cmd)
{
/* check the current state */
if((AD_STATE_INACTIVE == audio_state) || (AD_STATE_ERROR == audio_state)) {
audio_state = AD_STATE_ERROR;
return AD_FAIL;
}
switch(cmd) {
/* process the play command */
case AD_CMD_PLAY:
/* if current state is active or stopped */
if((AD_STATE_ACTIVE == audio_state) || \
(AD_STATE_STOPPED == audio_state) || \
(AD_STATE_PLAYING == audio_state)) {
audio_play((uint32_t)pbuf, size);
audio_state = AD_STATE_PLAYING;
return AD_OK;
} else if(AD_STATE_PAUSED == audio_state) {
audio_pause_resume(AD_RESUME, (uint32_t)pbuf, (size / 2U));
audio_state = AD_STATE_PLAYING;
return AD_OK;
} else {
return AD_FAIL;
}
/* process the stop command */
case AD_CMD_STOP:
if(AD_STATE_PLAYING != audio_state) {
/* unsupported command */
return AD_FAIL;
} else {
audio_stop();
audio_state = AD_STATE_STOPPED;
return AD_OK;
}
/* process the pause command */
case AD_CMD_PAUSE:
if(AD_STATE_PLAYING != audio_state) {
/* unsupported command */
return AD_FAIL;
} else {
audio_pause_resume(AD_PAUSE, (uint32_t)pbuf, (size / 2U));
audio_state = AD_STATE_PAUSED;
return AD_OK;
}
/* unsupported command */
default:
return AD_FAIL;
}
}