5360 lines
254 KiB
C
5360 lines
254 KiB
C
/*
|
|
*********************************************************************************************************
|
|
* uC/USB-Device
|
|
* The Embedded USB Device Stack
|
|
*
|
|
* Copyright 2004-2021 Silicon Laboratories Inc. www.silabs.com
|
|
*
|
|
* SPDX-License-Identifier: APACHE-2.0
|
|
*
|
|
* This software is subject to an open source license and is distributed by
|
|
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
|
|
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*
|
|
* USB DEVICE AUDIO CLASS
|
|
* PROCESSING LAYER
|
|
*
|
|
* Filename : usbd_audio_processing.c
|
|
* Version : V4.06.01
|
|
*********************************************************************************************************
|
|
* Note(s) : (1) 'goto' statements were used in this software module. Their usage is restricted to
|
|
* cleanup purposes in exceptional program flow (e.g. error handling), in compliance
|
|
* with CERT MEM12-C and MISRA C:2012 rules 15.2, 15.3 and 15.4.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* INCLUDE FILES
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define MICRIUM_SOURCE
|
|
#define USBD_AUDIO_PROCESSING_MODULE
|
|
#include "usbd_audio_processing.h"
|
|
#include "usbd_audio_internal.h"
|
|
#include "usbd_audio_os.h"
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL DEFINES
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define USBD_AUDIO_PLAYBACK_CORR_MIN_NBR_SAMPLES 4u /* Min nbr of samples in buf to apply playback corr. */
|
|
#define USBD_AUDIO_OVERRUN_NBR_SAMPLES_FOR_AVERAGING 4u
|
|
#define USBD_AUDIO_UNDERRUN_NBR_SAMPLES_FOR_AVERAGING 2u
|
|
|
|
#define USBD_AUDIO_REQ_CTRL_SELECTOR_MASK 0xFF00u
|
|
#define USBD_AUDIO_REQ_CH_NBR_MASK 0x00FFu
|
|
#define USBD_AUDIO_REQ_IN_CH_NBR_MASK 0xFF00u
|
|
#define USBD_AUDIO_REQ_OUT_CH_NBR_MASK 0x00FFu
|
|
|
|
|
|
#define USBD_AUDIO_LOCK_TIMEOUT_mS 1000u
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* TERMINAL CONTROL SELECTOR
|
|
*
|
|
* Note(s): (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.1.3 and appendix A.10.1 for more details about Terminal Controls.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define USBD_AUDIO_TERMINAL_CTRL_SEL_COPY_PROTECT 0x01u
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* FEATURE UNIT CONTROL SELECTOR
|
|
*
|
|
* Note(s): (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3 and appendix A.10.2 for more details about Feature Unit Controls.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#define USBD_AUDIO_FU_CTRL_SEL_UNDEF 0u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_MUTE 1u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_VOL 2u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_BASS 3u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_MID 4u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_TREBLE 5u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_GRAPHIC_EQUALIZER 6u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_AUTO_GAIN 7u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_DELAY 8u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_BASS_BOOST 9u
|
|
#define USBD_AUDIO_FU_CTRL_SEL_LOUDNESS 10u
|
|
|
|
|
|
/*
|
|
**********************************************************************************************************
|
|
* TRACE MACROS
|
|
**********************************************************************************************************
|
|
*/
|
|
|
|
#define USBD_DBG_AUDIO_PROC_MSG(msg) USBD_DBG_GENERIC ((msg), \
|
|
USBD_EP_ADDR_NONE, \
|
|
USBD_IF_NBR_NONE)
|
|
|
|
#define USBD_DBG_AUDIO_PROC_ARG(msg, arg) USBD_DBG_GENERIC_ARG((msg), \
|
|
USBD_EP_ADDR_NONE, \
|
|
USBD_IF_NBR_NONE, \
|
|
(arg))
|
|
|
|
#define USBD_DBG_AUDIO_PROC_ERR(msg, err) USBD_DBG_GENERIC_ERR((msg), \
|
|
USBD_EP_ADDR_NONE, \
|
|
USBD_IF_NBR_NONE, \
|
|
(err))
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL CONSTANTS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL DATA TYPES
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL TABLES
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL GLOBAL VARIABLES
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/* AudioStreaming IF tbl. */
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static USBD_AUDIO_AS_IF USBD_Audio_AS_Tbl[USBD_AUDIO_MAX_NBR_AS_IF_EP];
|
|
static CPU_INT08U USBD_Audio_AS_NbrNext;
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL FUNCTION PROTOTYPES
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_RecordIsocCmpl ( CPU_INT08U dev_nbr,
|
|
CPU_INT08U ep_addr,
|
|
void *p_buf,
|
|
CPU_INT32U buf_len,
|
|
CPU_INT32U xfer_len,
|
|
void *p_arg,
|
|
USBD_ERR err);
|
|
|
|
static void USBD_Audio_RecordPrime ( USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_RecordUsbBufSubmit ( USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT16U *p_xfer_submitted_cnt);
|
|
|
|
static CPU_INT16U USBD_Audio_RecordDataRateAdj ( USBD_AUDIO_AS_IF *p_as_if);
|
|
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
|
|
static void USBD_Audio_RecordCorrBuiltIn ( USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc);
|
|
#endif
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackIsocCmpl ( CPU_INT08U dev_nbr,
|
|
CPU_INT08U ep_addr,
|
|
void *p_buf,
|
|
CPU_INT32U buf_len,
|
|
CPU_INT32U xfer_len,
|
|
void *p_arg,
|
|
USBD_ERR err);
|
|
|
|
static void USBD_Audio_PlaybackPrime ( USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_PlaybackUsbBufSubmit ( USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT16U *p_xfer_submitted_cnt);
|
|
|
|
static void USBD_Audio_PlaybackBufSubmit ( USBD_AUDIO_AS_IF *p_as_if);
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackCorrExec ( USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc,
|
|
USBD_ERR *p_err);
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackCorrBuiltIn ( USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc,
|
|
USBD_ERR *p_err);
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackCorrSynchInit ( USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT32U sampling_freq,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_PlaybackIsocSynchCmpl ( CPU_INT08U dev_nbr,
|
|
CPU_INT08U ep_addr,
|
|
void *p_buf,
|
|
CPU_INT32U buf_len,
|
|
CPU_INT32U xfer_len,
|
|
void *p_arg,
|
|
USBD_ERR err);
|
|
|
|
static void USBD_Audio_PlaybackCorrSynch ( USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT16U frame_nbr,
|
|
USBD_ERR *p_err);
|
|
|
|
static CPU_INT32U *USBD_Audio_PlaybackSynchBufGet ( USBD_AUDIO_AS_IF *p_as_if);
|
|
|
|
static void USBD_Audio_PlaybackSynchBufFree ( USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT32U *p_buf);
|
|
#endif
|
|
#endif
|
|
|
|
static void USBD_Audio_Terminal_CopyProtManage ( CPU_INT08U terminal_id,
|
|
USBD_AUDIO_ENTITY_TYPE recipient,
|
|
const void *p_recipient_info,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_MuteManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_VolManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_BassManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_MidManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_TrebleManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_GraphicEqualizerManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_AutoGainManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_DlyManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_BassBoostManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_FU_LoudnessManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
|
|
static void USBD_Audio_MU_CtrlManage (const USBD_AUDIO_MU *p_mu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_in_ch_nbr,
|
|
CPU_INT08U log_out_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
|
|
static void USBD_Audio_SU_CtrlManage (const USBD_AUDIO_SU *p_su,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_AS_SamplingFreqManage (const USBD_AUDIO_AS_IF *p_as_if,
|
|
const USBD_AUDIO_AS_ALT_CFG *p_as_cfg,
|
|
CPU_INT16U ep_addr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err);
|
|
|
|
static void USBD_Audio_AS_PitchManage (const USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT16U ep_addr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err);
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_AS_IF_RingBufQInit ( USBD_AUDIO_AS_IF *p_as_if);
|
|
|
|
static USBD_AUDIO_BUF_DESC *USBD_Audio_AS_IF_RingBufQGet ( USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q,
|
|
CPU_INT16U ix);
|
|
|
|
static CPU_INT16U USBD_Audio_AS_IF_RingBufQProducerStartIxGet( USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings);
|
|
|
|
static CPU_INT16U USBD_Audio_AS_IF_RingBufQProducerEndIxGet ( USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings);
|
|
|
|
static CPU_INT16U USBD_Audio_AS_IF_RingBufQConsumerStartIxGet( USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings);
|
|
|
|
static CPU_INT16U USBD_Audio_AS_IF_RingBufQConsumerEndIxGet ( USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings);
|
|
|
|
static void USBD_Audio_AS_IF_RingBufQIxUpdate ( USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings,
|
|
CPU_INT16U *p_ix);
|
|
|
|
#if ( (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
((USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED))) || \
|
|
( (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED) )
|
|
static CPU_INT08S USBD_Audio_BufDiffGet ( USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings);
|
|
#endif
|
|
|
|
static USBD_AUDIO_AS_IF *USBD_Audio_AS_IF_Get ( USBD_AUDIO_AS_HANDLE as_handle);
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL CONFIGURATION ERRORS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* GLOBAL FUNCTIONS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_ProcessingInit()
|
|
*
|
|
* Description : Initialize the Audio Processing layer.
|
|
*
|
|
* Argument(s) : msg_qty Maximum quantity of messages for playback and record tasks' queues.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Audio class successfully initialized.
|
|
* USBD_ERR_ALLOC Buffer descriptors pool could not be allocated.
|
|
*
|
|
* -RETURNED BY USBD_Audio_OS_Init()-
|
|
* See USBD_Audio_OS_Init() for additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_Audio_ProcessingInit (CPU_INT16U msg_qty,
|
|
USBD_ERR *p_err)
|
|
{
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
Mem_Clr((void *)&USBD_Audio_AS_Tbl[0u], /* Init AudioStreaming IF tbl. */
|
|
(USBD_AUDIO_MAX_NBR_AS_IF_EP * sizeof(USBD_AUDIO_AS_IF)));
|
|
|
|
USBD_Audio_AS_NbrNext = 0u;
|
|
#endif
|
|
USBD_Audio_OS_Init(msg_qty, p_err); /* 2 tasks are used for audio streams comm. */
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_Alloc()
|
|
*
|
|
* Description : Allocate an AudioStreaming interface.
|
|
*
|
|
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE AudioStreaming interface allocated.
|
|
* USBD_ERR_ALLOC No AudioStreaming interface available.
|
|
*
|
|
* Return(s) : Pointer to AudioStreaming interface, if NO error(s).
|
|
*
|
|
* Null pointer, otherwise.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
USBD_AUDIO_AS_IF *USBD_Audio_AS_IF_Alloc (USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
CPU_INT08U as_if_nbr;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
/* ----------- GET AUDIOSTREAMING IF STRUCT ----------- */
|
|
CPU_CRITICAL_ENTER();
|
|
as_if_nbr = USBD_Audio_AS_NbrNext;
|
|
|
|
if (as_if_nbr >= USBD_AUDIO_MAX_NBR_AS_IF_EP) {
|
|
CPU_CRITICAL_EXIT();
|
|
*p_err = USBD_ERR_AUDIO_AS_IF_ALLOC;
|
|
return (DEF_NULL);
|
|
}
|
|
|
|
USBD_Audio_AS_NbrNext++;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
p_as_if = &USBD_Audio_AS_Tbl[as_if_nbr];
|
|
USBD_AUDIO_AS_IF_HANDLE_CREATE(p_as_if, as_if_nbr); /* Store AudioStreaming IF ix. */
|
|
|
|
return (p_as_if);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AC_TerminalCtrl()
|
|
*
|
|
* Description : Process Terminal's Control.
|
|
*
|
|
* Argument(s) : terminal_id Input or Output Terminal ID.
|
|
*
|
|
* recipient Recipient Terminal Type.
|
|
*
|
|
* p_recipient_info Pointer to the Terminal Recipient info.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* w_val Received request control value.
|
|
*
|
|
* p_buf Pointer to the buffer space with the request attribute to SET or GET.
|
|
*
|
|
* req_len Request attribute length (in bytes).
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Terminal Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_CTRL Invalid control.
|
|
*
|
|
* -RETURNED BY USBD_Audio_Terminal_CopyProtManage()-
|
|
* See USBD_Audio_Terminal_CopyProtManage() for additional return error codes.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_Audio_AC_TerminalCtrl ( CPU_INT08U terminal_id,
|
|
USBD_AUDIO_ENTITY_TYPE recipient,
|
|
const void *p_recipient_info,
|
|
CPU_INT08U b_req,
|
|
CPU_INT16U w_val,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_INT08U ctrl_selector;
|
|
|
|
|
|
(void)req_len;
|
|
|
|
ctrl_selector = (w_val & USBD_AUDIO_REQ_CTRL_SELECTOR_MASK) >> 8u;
|
|
|
|
switch (ctrl_selector) {
|
|
case USBD_AUDIO_TERMINAL_CTRL_SEL_COPY_PROTECT:
|
|
|
|
USBD_Audio_Terminal_CopyProtManage(terminal_id,
|
|
recipient,
|
|
p_recipient_info,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_CTRL; /* Ctrl Selector not supported. Ctrl EP will be stalled.*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AC_UnitCtrl()
|
|
*
|
|
* Description : Process Unit's Control.
|
|
*
|
|
* Argument(s) : unit_id Feature Unit ID, Mixer Unit ID or Selector Unit ID.
|
|
*
|
|
* recipient Recipient Unit Type.
|
|
*
|
|
* p_recipient_info Pointer to the Unit Recipient info.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* w_val Received request control value.
|
|
*
|
|
* p_buf Pointer to the buffer space with the request attribute to SET or GET.
|
|
*
|
|
* req_len Request control attribute length (in bytes).
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Unit Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_CTRL Invalid control value passed to this
|
|
* function.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_RECIPIENT Recipient is not a Feature Unit, Mixer
|
|
* Unit or Selector Unit ID.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Logical channel out of range or
|
|
* unsupported 2nd form.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_TrebleManage()-
|
|
* See USBD_Audio_FU_TrebleManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_VolManage()-
|
|
* See USBD_Audio_FU_VolManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_AutoGainManage()-
|
|
* See USBD_Audio_FU_AutoGainManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_DlyManage()-
|
|
* See USBD_Audio_FU_DlyManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_GraphicEqualizerManage()-
|
|
* See USBD_Audio_FU_GraphicEqualizerManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_MuteManage()-
|
|
* See USBD_Audio_FU_MuteManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_LoudnessManage()-
|
|
* See USBD_Audio_FU_LoudnessManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_MidManage()-
|
|
* See USBD_Audio_FU_MidManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_BassBoostManage()-
|
|
* See USBD_Audio_FU_BassBoostManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_FU_BassManage()-
|
|
* See USBD_Audio_FU_BassManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_SU_CtrlManage()-
|
|
* See USBD_Audio_SU_CtrlManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_MU_CtrlManage()-
|
|
* See USBD_Audio_MU_CtrlManage() for additional return error codes.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) When the channel number is set to 0xFF, one single Get or Set request is sent by the
|
|
* host to get or set an attribute for all available Controls in the Feature Unit.
|
|
* In that case, the Control parameter block is formatted according to the second
|
|
* form.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.1 and 5.2.2.4.2 for more details about the first form and second form
|
|
* of the Get/Set requests for the Feature Unit.
|
|
*
|
|
* (2) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.2.1 and 5.2.2.2.2 for more details about the second form and third form
|
|
* of the Get/Set requests for the Mixer Unit.
|
|
*
|
|
* (3) The Mixer Unit Descriptor reports which Controls are programmable in the bmControls
|
|
* bitmap field. This bitmap must be interpreted as a two-dimensional bit array that has a
|
|
* row for each logical input channel and a column for each logical output channel.
|
|
* If a bit at position [u, v] is set, this means that the Mixer Unit contains a
|
|
* programmable mixing Control that connects input channel u to output channel v.
|
|
* If bit [u, v] is clear, this indicates that the connection between input channel
|
|
* u and output channel v is non-programmable. The valid range for u is from 1 to n
|
|
* (n = number of logical input channels). The valid range for v is from 1 to m
|
|
* (m = number of logical output channels). For instance, let's consider a Mixer Unit
|
|
* with 2 stereo Input pins and 1 stereo Output pin:
|
|
*
|
|
* u +---------+
|
|
* 1. Input 1 Left --->| |
|
|
* 2. Input 1 Right --->| | v
|
|
* | MU |---> 1. Output Left
|
|
* 3. Input 2 Left --->| |---> 2. Output Right
|
|
* 4. Input 2 Right --->| |
|
|
* +---------+
|
|
*
|
|
* The 2-dimensional array will look like this one:
|
|
*
|
|
* u \ v | 1 | 2 | bit# reversed 0 1 2 3 4 5 6 7
|
|
* Row 1: 1 |'1'|'0'| bit# 7 6 5 4 3 2 1 0
|
|
* Row 2: 2 |'0'|'1'| => bmControls |Row 1|Row 2|Row 3|Row 4| = |1 0|0 1|1 0|0 1|
|
|
* Row 3: 3 |'1'|'0'|
|
|
* Row 4: 4 |'0'|'1'|
|
|
*
|
|
* In this example, bmControls has a size of 1 byte. If the host sends a SET_CUR request
|
|
* to [u;v] = [3;2], it wants to mix the Input 2 Left into the Output Right. The
|
|
* 2-dimensional array at this position indicates that the Control is non-programmable.
|
|
* The request should be stalled. The computation does the following steps:
|
|
*
|
|
* (a) Compute bit number within bmControls field using the formula [(u-1) * m] + (v-1):
|
|
* Here, [(3-1) * 2] + (2-1) = 5th bit.
|
|
* (b) Compute the byte index in which the bit number is. Here, byte index = 0
|
|
* (c) Compute the bit shift to test the bit number position within the byte.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
void USBD_Audio_AC_UnitCtrl ( CPU_INT08U unit_id,
|
|
USBD_AUDIO_ENTITY_TYPE recipient,
|
|
const void *p_recipient_info,
|
|
CPU_INT08U b_req,
|
|
CPU_INT16U w_val,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_FU *p_fu;
|
|
CPU_INT08U ctrl_selector;
|
|
CPU_INT08U log_ch_nbr;
|
|
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
|
|
USBD_AUDIO_MU *p_mu;
|
|
CPU_INT08U log_in_ch_nbr;
|
|
CPU_INT08U log_out_ch_nbr;
|
|
CPU_INT32U total_nbr_log_in_ch;
|
|
CPU_INT08U byte_ix;
|
|
CPU_INT08U bit_shift;
|
|
CPU_INT32U bit_nbr;
|
|
CPU_BOOLEAN programmable_ctrl;
|
|
CPU_INT16U i;
|
|
#endif
|
|
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
|
|
USBD_AUDIO_SU *p_su;
|
|
#endif
|
|
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_DISABLED)
|
|
(void)req_len;
|
|
#endif
|
|
|
|
switch (recipient) {
|
|
case USBD_AUDIO_ENTITY_FU: /* ------------------- FEATURE UNIT ------------------- */
|
|
p_fu = (USBD_AUDIO_FU *)p_recipient_info;
|
|
|
|
if (unit_id != p_fu->ID) { /* Check if ID is known. */
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
ctrl_selector = (w_val & USBD_AUDIO_REQ_CTRL_SELECTOR_MASK) >> 8u;
|
|
log_ch_nbr = (w_val & USBD_AUDIO_REQ_CH_NBR_MASK);
|
|
|
|
if ((log_ch_nbr == 0xFFu) || /* 2nd form of Get/Set req not supported (see Note #1). */
|
|
(log_ch_nbr > p_fu->FU_CfgPtr->LogChNbr)) { /* log ch nbr out of range. */
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
/* Check if ctrl selector is supported. */
|
|
if (DEF_BIT_IS_CLR(p_fu->FU_CfgPtr->LogChCtrlPtr[log_ch_nbr], DEF_BIT(ctrl_selector - 1u)) == DEF_YES) {
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_CTRL;
|
|
break;
|
|
}
|
|
|
|
switch (ctrl_selector) {
|
|
case USBD_AUDIO_FU_CTRL_SEL_MUTE:
|
|
USBD_Audio_FU_MuteManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_FU_CTRL_SEL_VOL:
|
|
USBD_Audio_FU_VolManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
case USBD_AUDIO_FU_CTRL_SEL_BASS:
|
|
USBD_Audio_FU_BassManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_FU_CTRL_SEL_MID:
|
|
USBD_Audio_FU_MidManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_FU_CTRL_SEL_TREBLE:
|
|
USBD_Audio_FU_TrebleManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_FU_CTRL_SEL_GRAPHIC_EQUALIZER:
|
|
USBD_Audio_FU_GraphicEqualizerManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
req_len,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_FU_CTRL_SEL_AUTO_GAIN:
|
|
USBD_Audio_FU_AutoGainManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_FU_CTRL_SEL_DELAY:
|
|
USBD_Audio_FU_DlyManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_FU_CTRL_SEL_BASS_BOOST:
|
|
USBD_Audio_FU_BassBoostManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_FU_CTRL_SEL_LOUDNESS:
|
|
USBD_Audio_FU_LoudnessManage(p_fu,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
#endif
|
|
|
|
default: /* Ctrl Selector not supported. Ctrl EP will be stalled.*/
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_CTRL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
|
|
case USBD_AUDIO_ENTITY_MU: /* -------------------- MIXER UNIT -------------------- */
|
|
p_mu = (USBD_AUDIO_MU *)p_recipient_info;
|
|
|
|
if (unit_id != p_mu->ID) { /* Check if ID is known. */
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
log_in_ch_nbr = (w_val & USBD_AUDIO_REQ_IN_CH_NBR_MASK) >> 8u;
|
|
log_out_ch_nbr = (w_val & USBD_AUDIO_REQ_OUT_CH_NBR_MASK);
|
|
/* 2nd and 3rd forms of Get/Set req not supported... */
|
|
/* ...(see Note #2). */
|
|
if (((log_in_ch_nbr == 0xFFu) || (log_in_ch_nbr == 0x00u)) &&
|
|
(log_in_ch_nbr == log_out_ch_nbr)) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
total_nbr_log_in_ch = (p_mu->MU_CfgPtr->LogOutChNbr * p_mu->MU_CfgPtr->NbrInPins);
|
|
if (log_in_ch_nbr > total_nbr_log_in_ch) { /* Log IN ch nbr exceeds total nbr of log IN ch. */
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
/* Log OUT ch nbr exceeds total nbr of log OUT ch. */
|
|
if (log_out_ch_nbr > p_mu->MU_CfgPtr->LogOutChNbr) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
if (b_req == USBD_AUDIO_REQ_SET_CUR) {
|
|
/* Verify if Mixer Ctrls are programmable. */
|
|
programmable_ctrl = DEF_FALSE;
|
|
for (i = 0; i < p_mu->ControlsSize; i++) {
|
|
if (p_mu->ControlsTblPtr[i] != USBD_AUDIO_MU_CTRL_NONE) {
|
|
programmable_ctrl = DEF_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (programmable_ctrl == DEF_FALSE) {
|
|
*p_err = USBD_ERR_AUDIO_REQ; /* No programmable ctrl. */
|
|
break;
|
|
}
|
|
/* Verify if specified Ctrl is programmable... */
|
|
/* ...(see Note #3). */
|
|
bit_nbr = ((log_in_ch_nbr - 1u) * p_mu->MU_CfgPtr->LogOutChNbr) + (log_out_ch_nbr - 1u);
|
|
byte_ix = bit_nbr / DEF_OCTET_NBR_BITS;
|
|
bit_shift = bit_nbr % DEF_OCTET_NBR_BITS;
|
|
|
|
if (DEF_BIT_IS_CLR(p_mu->ControlsTblPtr[byte_ix], ((DEF_BIT_07) >> bit_shift)) == DEF_YES) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
}
|
|
|
|
USBD_Audio_MU_CtrlManage(p_mu,
|
|
unit_id,
|
|
log_in_ch_nbr,
|
|
log_out_ch_nbr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
|
|
case USBD_AUDIO_ENTITY_SU: /* ------------------ SELECTOR UNIT ------------------- */
|
|
if (w_val == 0u) {
|
|
p_su = (USBD_AUDIO_SU *)p_recipient_info;
|
|
|
|
if (unit_id != p_su->ID) { /* Check if ID is known. */
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
USBD_Audio_SU_CtrlManage(p_su,
|
|
unit_id,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_CTRL;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_RECIPIENT; /* Unit not supported. Ctrl EP will be stalled. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_EP_CtrlProcess()
|
|
*
|
|
* Description : Process AudioStreaming endpoint's Control.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to AudioStreaming Interface.
|
|
*
|
|
* p_as_cfg Pointer to the AudioStreaming configuration.
|
|
*
|
|
* ep_addr Endpoint address.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* w_val Received request control value.
|
|
*
|
|
* p_buf Pointer to the buffer space with the request attribute.
|
|
*
|
|
* req_len Request attribute length (in bytes).
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Endpoint Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_CTRL Invalid or unsupported control value passed
|
|
* to this function.
|
|
*
|
|
* -RETURNED BY USBD_Audio_AS_IF_Start()-
|
|
* See USBD_Audio_AS_IF_Start() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_AS_SamplingFreqManage()-
|
|
* See USBD_Audio_AS_SamplingFreqManage() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_ProcPlaybackSynchCorrInit()-
|
|
* See USBD_Audio_ProcPlaybackSynchCorrInit() for additional return error codes.
|
|
*
|
|
* -RETURNED BY USBD_Audio_AS_PitchManage()-
|
|
* See USBD_Audio_AS_PitchManage() for additional return error codes.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) According to Audio 1.0 specification (section 5.2.3.2.3.1), the sampling frequency
|
|
* value holds on 3 bytes. Since the sampling frequency is referenced as a 32-bit word,
|
|
* clearing the most significant byte ensures that the correct value is read from
|
|
* memory.
|
|
*
|
|
* (2) A general purpose OS (Windows, Mac OS, Linux) opens a record stream by sending a
|
|
* SET_INTERFACE request to select the operational interface of an AudioStreaming
|
|
* interface. The SET_INTERFACE request is always followed by a SET_CUR request to
|
|
* configure the sampling frequency. Getting audio record data must be started ONLY when
|
|
* the sampling frequency has been configured.
|
|
*
|
|
* (3) For certain sampling frequencies (i.e. 11.025, 22.050 and 44.1 KHz), a data rate
|
|
* adjustment is necessary because the number of audio samples per ms is not an
|
|
* integer number. Partial audio samples are not possible. For those sampling
|
|
* frequencies, the table below gives the required adjustment:
|
|
*
|
|
* Samples per frm/ms | Typical Packet Size | Adjustment
|
|
* 11.025 | 11 samples | 12 samples every 40 packets (i.e. ms)
|
|
* 22.050 | 22 samples | 23 samples every 20 packets (i.e. ms)
|
|
* 44.1 | 44 samples | 45 samples every 10 packets (i.e. ms)
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
void USBD_Audio_AS_EP_CtrlProcess ( USBD_AUDIO_AS_IF *p_as_if,
|
|
const USBD_AUDIO_AS_ALT_CFG *p_as_cfg,
|
|
CPU_INT16U ep_addr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT16U w_val,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_INT08U ctrl_selector;
|
|
CPU_INT32U sampling_freq;
|
|
#if ((USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED) || \
|
|
((USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)))
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
#endif
|
|
|
|
|
|
#if ((USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED) || \
|
|
((USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)))
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
#endif
|
|
ctrl_selector = (w_val & USBD_AUDIO_REQ_CTRL_SELECTOR_MASK) >> 8u;
|
|
|
|
switch (ctrl_selector) {
|
|
case USBD_AUDIO_AS_EP_CTRL_SAMPLING_FREQ:
|
|
/* Verify if Sampling Freq Control supported by this EP.*/
|
|
if (DEF_BIT_IS_CLR(p_as_cfg->EP_Attrib, USBD_AUDIO_AS_EP_CTRL_SAMPLING_FREQ) == DEF_YES) {
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_CTRL;
|
|
break;
|
|
}
|
|
|
|
USBD_Audio_AS_SamplingFreqManage(p_as_if,
|
|
p_as_cfg,
|
|
ep_addr,
|
|
b_req,
|
|
p_buf,
|
|
req_len,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
break;
|
|
}
|
|
|
|
sampling_freq = MEM_VAL_GET_INT32U_LITTLE(p_buf);
|
|
sampling_freq &= 0x00FFFFFFu; /* Ensure correct sampling freq is read (see Note #1). */
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
if (p_as_if_settings->StreamDir == USBD_AUDIO_STREAM_IN) {
|
|
/* Compute buf len according to sample rate, bit... */
|
|
/* ...resolution and nbr of channels. */
|
|
p_as_if_settings->RecordBufLen = (sampling_freq / DEF_TIME_NBR_mS_PER_SEC) * p_as_cfg->SubframeSize * p_as_cfg->NbrCh;
|
|
|
|
/* See Note #2. */
|
|
if ((USBD_EP_IS_IN(ep_addr) == DEF_YES) &&
|
|
(b_req == USBD_AUDIO_REQ_SET_CUR)) {
|
|
|
|
if ((sampling_freq % 1000u) != 0u) { /* Keep some info if data rate adjustment is needed... */
|
|
/* ...See Note #3. */
|
|
CPU_INT16U temp;
|
|
|
|
|
|
temp = sampling_freq % 1000u;
|
|
p_as_if_settings->RecordRateAdjMs = 1000u / temp;
|
|
p_as_if_settings->RecordRateAdjXferCtr = 0u;
|
|
}
|
|
|
|
USBD_Audio_AS_IF_Start(p_as_if, p_err);
|
|
}
|
|
}
|
|
#endif
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
|
|
if ((p_as_if_settings->StreamDir == USBD_AUDIO_STREAM_OUT) &&
|
|
(p_as_if->AS_IF_AltCurPtr->SynchIsocAddr != USBD_EP_ADDR_NONE)) {
|
|
|
|
USBD_Audio_PlaybackCorrSynchInit(p_as_if, sampling_freq, p_err);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_AS_EP_CTRL_PITCH:
|
|
/* Verify if Pitch Control supported by this EP. */
|
|
if (DEF_BIT_IS_CLR(p_as_cfg->EP_Attrib, USBD_AUDIO_AS_EP_CTRL_PITCH) == DEF_YES) {
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_CTRL;
|
|
break;
|
|
}
|
|
|
|
USBD_Audio_AS_PitchManage(p_as_if,
|
|
ep_addr,
|
|
b_req,
|
|
p_buf,
|
|
p_err);
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_AS_EP_CTRL_MAX_PKT_ONLY:
|
|
default: /* Ctrl Selector not supported. Ctrl EP will be stalled.*/
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_CTRL;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordTaskHandler()
|
|
*
|
|
* Description : Process events sent by the codec driver when the audio transfers have finished.
|
|
*
|
|
* Argument(s) : None.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) USBD_Audio_RecordPrime() is called in two distinct situations:
|
|
*
|
|
* (a) As soon as stream priming is done, that is a certain number of ready buffers
|
|
* have accumulated, the first USB transfer is started.
|
|
*
|
|
* (b) When there is no more ongoing isochronous transfers in the USB driver during
|
|
* an ongoing stream communication, that is the stream loop is broken. In that
|
|
* case, the Record task restarts the stream with a new USB transfer.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
void USBD_Audio_RecordTaskHandler (void)
|
|
{
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
USBD_AUDIO_AS_HANDLE as_if_handle;
|
|
CPU_INT16U ix;
|
|
CPU_INT16U isoc_tx_ongoing_cnt;
|
|
CPU_BOOLEAN pre_buf_compl;
|
|
USBD_ERR err_usbd;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
while (DEF_TRUE) {
|
|
/* ------ WAIT FOR AUDIO XFER COMPLETION SIGNAL ------- */
|
|
as_if_handle = (USBD_AUDIO_AS_HANDLE)(CPU_ADDR)USBD_Audio_OS_RecordReqPend(&err_usbd);
|
|
|
|
p_as_if = USBD_Audio_AS_IF_Get(as_if_handle);
|
|
if (p_as_if == DEF_NULL) {
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordTaskHandler(): Invalid AS IF handle\r\n");
|
|
continue;
|
|
}
|
|
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrReqPendRecordTask);
|
|
|
|
USBD_Audio_OS_AS_IF_LockAcquire(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle),
|
|
USBD_AUDIO_LOCK_TIMEOUT_mS,
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("RecordTaskHandler(): lock acquire failed w/ err = %d\r\n", err_usbd);
|
|
continue;
|
|
}
|
|
|
|
if (USBD_AUDIO_AS_IF_HANDLE_VALIDATE(p_as_if, as_if_handle) != DEF_OK) {
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordTaskHandler(): Invalid AS IF handle\r\n");
|
|
goto end_lock_rel;
|
|
}
|
|
/* -------------- GET RDY BUF FROM CODEC -------------- */
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQProducerEndIxGet(p_as_if_settings);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordTaskHandler(): no buffer descriptor\r\n");
|
|
goto end_lock_rel;
|
|
}
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if_settings->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordTaskHandler(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
goto end_lock_rel;
|
|
}
|
|
|
|
p_as_if_settings->AS_API_Ptr->StreamRecordRx(p_as_if_settings->DrvInfoPtr,
|
|
p_as_if_settings->TerminalID,
|
|
p_buf_desc->BufPtr,
|
|
&p_buf_desc->BufLen,
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("RecordTaskHandler(): cannot get ready buf w/ err = %d\r\n", err_usbd);
|
|
goto end_lock_rel;
|
|
}
|
|
|
|
/* Update ix only after writing to buf desc. */
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if_settings, &p_as_if_settings->StreamRingBufQ.ProducerEndIx);
|
|
|
|
/* --------------- PRIMING DONE OR NOT ---------------- */
|
|
CPU_CRITICAL_ENTER();
|
|
isoc_tx_ongoing_cnt = p_as_if_settings->RecordIsocTxOngoingCnt;
|
|
CPU_CRITICAL_EXIT();
|
|
/* If priming done and isoc xfers in progress,... */
|
|
/* ...wait for nxt audio xfer completion. */
|
|
if ((p_as_if_settings->StreamPrimingDone == DEF_YES) &&
|
|
(isoc_tx_ongoing_cnt > 0u )) {
|
|
goto end_lock_rel;
|
|
}
|
|
|
|
/* If priming not done, wait for nxt audio xfer cmpl. */
|
|
pre_buf_compl = p_as_if_settings->StreamRingBufQ.ProducerEndIx >= p_as_if_settings->StreamPreBufMax ? DEF_YES : DEF_NO;
|
|
if ((p_as_if_settings->StreamPrimingDone == DEF_NO) &&
|
|
(pre_buf_compl == DEF_NO)) {
|
|
goto end_lock_rel;
|
|
}
|
|
|
|
USBD_Audio_RecordPrime(p_as_if, &err_usbd); /* Start 1st isoc IN xfer (see Note #1). */
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("RecordTaskHandler(): starting first isoc IN transfer failed w/ err = %d\r\n", err_usbd);
|
|
goto end_lock_rel;
|
|
}
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxSubmitRecordTask);
|
|
|
|
if (p_as_if_settings->StreamPrimingDone == DEF_NO) {
|
|
#if (USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
/* Get initial frame nbr for corr period computation. */
|
|
p_as_if_settings->CorrFrameNbr = USBD_DevFrameNbrGet(p_as_if->DevNbr, &err_usbd);
|
|
p_as_if_settings->CorrFrameNbr = USBD_FRAME_NBR_GET(p_as_if_settings->CorrFrameNbr);
|
|
#endif
|
|
|
|
p_as_if_settings->StreamPrimingDone = DEF_YES;
|
|
}
|
|
|
|
end_lock_rel:
|
|
USBD_Audio_OS_AS_IF_LockRelease(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackTaskHandler()
|
|
*
|
|
* Description : Process events sent by the codec driver when the audio transfers have finished.
|
|
*
|
|
* Argument(s) : none.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
void USBD_Audio_PlaybackTaskHandler (void)
|
|
{
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
USBD_AUDIO_AS_HANDLE as_if_handle;
|
|
USBD_ERR err_usbd;
|
|
|
|
|
|
while (DEF_TRUE) {
|
|
/* Wait for an AS IF handle. */
|
|
as_if_handle = (USBD_AUDIO_AS_HANDLE)(CPU_ADDR)USBD_Audio_OS_PlaybackReqPend(&err_usbd);
|
|
|
|
p_as_if = USBD_Audio_AS_IF_Get(as_if_handle);
|
|
if (p_as_if == DEF_NULL) {
|
|
USBD_DBG_AUDIO_PROC_MSG("PlaybackTaskHandler(): Invalid AS IF handle\r\n");
|
|
continue;
|
|
}
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if->AS_IF_SettingsPtr->StatPtr->AudioProc_Playback_NbrReqPendPlaybackTask);
|
|
|
|
USBD_Audio_OS_AS_IF_LockAcquire(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle),
|
|
USBD_AUDIO_LOCK_TIMEOUT_mS,
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("PlaybackTaskHandler(): lock acquire failed w/ err = %d\r\n", err_usbd);
|
|
continue;
|
|
}
|
|
|
|
if (USBD_AUDIO_AS_IF_HANDLE_VALIDATE(p_as_if, as_if_handle) != DEF_OK) {
|
|
USBD_DBG_AUDIO_PROC_MSG("PlaybackTaskHandler(): Invalid AS IF handle\r\n");
|
|
goto end_lock_rel;
|
|
}
|
|
|
|
USBD_Audio_PlaybackBufSubmit(p_as_if); /* Codec xfer finished, submit a new rdy buf to codec. */
|
|
|
|
end_lock_rel:
|
|
USBD_Audio_OS_AS_IF_LockRelease(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_Start()
|
|
*
|
|
* Description : Start record or playback streaming.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the AudioStreaming Interface structure.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Record or playback stream successfully started.
|
|
* USBD_ERR_FAIL Audio driver failed to process the start request.
|
|
*
|
|
* -RETURNED BY USBD_Audio_OS_PlaybackEventPut()-
|
|
* See USBD_Audio_OS_PlaybackEventPut() for additional return error codes.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
void USBD_Audio_AS_IF_Start (USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
|
|
|
|
USBD_Audio_OS_AS_IF_LockAcquire(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle),
|
|
USBD_AUDIO_LOCK_TIMEOUT_mS,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("AS_IF_Start(): lock acquire failed w/ err = %d\r\n", *p_err);
|
|
return;
|
|
}
|
|
|
|
/* ------------- START RECORD OR PLAYBACK ------------- */
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
p_as_if_settings->StreamStarted = DEF_YES;
|
|
USBD_AUDIO_STAT_RESET(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrBufDescInUse);
|
|
USBD_AUDIO_STAT_RESET(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxOngoingCnt);
|
|
|
|
USBD_Audio_AS_IF_RingBufQInit(p_as_if); /* Alloc buf and init ring buf q w/ it. */
|
|
|
|
if (p_as_if_settings->StreamDir == USBD_AUDIO_STREAM_IN) {
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
/* Start record streaming on audio codec side. */
|
|
valid = p_as_if_settings->AS_API_Ptr->StreamStart(p_as_if_settings->DrvInfoPtr,
|
|
p_as_if->Handle,
|
|
p_as_if_settings->TerminalID);
|
|
if (valid != DEF_OK) {
|
|
*p_err = USBD_ERR_FAIL;
|
|
goto end_lock_clean;
|
|
}
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
} else {
|
|
USBD_Audio_PlaybackPrime(p_as_if, p_err); /* Submit 1st isoc xfer to start priming. */
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("AS_IF_Start(): starting playback priming failed w/ err = %d\r\n", *p_err);
|
|
goto end_lock_clean;
|
|
}
|
|
#endif
|
|
}
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_NbrStreamOpen);
|
|
|
|
USBD_Audio_OS_AS_IF_LockRelease(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle));
|
|
return;
|
|
|
|
end_lock_clean:
|
|
p_as_if_settings->StreamStarted = DEF_NO;
|
|
USBD_AUDIO_AS_IF_HANDLE_INVALIDATE(p_as_if);
|
|
USBD_Audio_OS_AS_IF_LockRelease(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle));
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_Stop()
|
|
*
|
|
* Description : Stop record or playback streaming.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the AudioStreaming Interface structure.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Record or playback stream successfully stopped.
|
|
* USBD_ERR_FAIL Audio driver failed to process the stop request.
|
|
*
|
|
* -RETURNED BY USBD_Audio_PlaybackBufQFlush()-
|
|
* See USBD_Audio_PlaybackBufQFlush() for additional return error codes.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) When closing the stream, any buffer currently processed by the codec driver, the
|
|
* USB driver or waiting to be processed inside the Audio Processing layer are freed
|
|
* implicitly by resetting the ring buffer queue. The buffers does not need to be
|
|
* explicitly returned in a pool or in a list. The buffers reside permanently in the
|
|
* stream memory segment. They will be re-allocated to the ring buffer queue at the next
|
|
* stream opening.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
void USBD_Audio_AS_IF_Stop (USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
CPU_BOOLEAN valid;
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
CPU_SR_ALLOC();
|
|
#endif
|
|
|
|
USBD_Audio_OS_AS_IF_LockAcquire(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle),
|
|
USBD_AUDIO_LOCK_TIMEOUT_mS,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("AS_IF_Stop(): lock acquire failed w/ err = %d\r\n", *p_err);
|
|
return;
|
|
}
|
|
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
if (p_as_if_settings->StreamStarted == DEF_NO) { /* If dev disconnects & stream not started, return. */
|
|
*p_err = USBD_ERR_NONE;
|
|
goto end_lock_clean;
|
|
}
|
|
/* ---------------- INVALIDATE HANDLE ----------------- */
|
|
USBD_AUDIO_AS_IF_HANDLE_INVALIDATE(p_as_if);
|
|
/* ---------------- RESET AS IF STRUCT ---------------- */
|
|
p_as_if_settings->StreamStarted = DEF_NO;
|
|
p_as_if_settings->StreamPrimingDone = DEF_NO;
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
p_as_if_settings->CorrFrameNbr = 0u;
|
|
#endif
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
if (p_as_if_settings->StreamDir == USBD_AUDIO_STREAM_IN) {
|
|
|
|
p_as_if_settings->RecordRateAdjMs = 0u;
|
|
p_as_if_settings->RecordRateAdjXferCtr = 0u;
|
|
p_as_if_settings->RecordBufLen = 0u;
|
|
p_as_if_settings->RecordIsocTxOngoingCnt = 0u;
|
|
}
|
|
#endif
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_NbrStreamClosed);
|
|
|
|
/* ------------------- STOP STREAM -------------------- */
|
|
valid = p_as_if_settings->AS_API_Ptr->StreamStop(p_as_if_settings->DrvInfoPtr,
|
|
p_as_if_settings->TerminalID);
|
|
if (valid != DEF_OK) {
|
|
*p_err = USBD_ERR_FAIL;
|
|
USBD_DBG_AUDIO_PROC_ERR("AS_IF_Stop(): codec playback stop failed w/ err = %d\r\n", *p_err);
|
|
goto end_lock_clean;
|
|
}
|
|
|
|
/* ----------------- RESET RING BUF Q ----------------- */
|
|
p_as_if_settings->StreamRingBufQ.ProducerStartIx = 0u;
|
|
p_as_if_settings->StreamRingBufQ.ProducerEndIx = 0u;
|
|
p_as_if_settings->StreamRingBufQ.ConsumerStartIx = 0u;
|
|
p_as_if_settings->StreamRingBufQ.ConsumerEndIx = 0u;
|
|
/* See Note #1. */
|
|
Mem_Clr((void *)p_as_if_settings->StreamRingBufQ.BufDescTblPtr,
|
|
(p_as_if_settings->BufTotalNbr * sizeof(USBD_AUDIO_BUF_DESC)));
|
|
|
|
end_lock_clean:
|
|
USBD_Audio_OS_AS_IF_LockRelease(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle));
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordBufGet()
|
|
*
|
|
* Description : Get a buffer from the stream ring buffer queue.
|
|
*
|
|
* Argument(s) : as_handle AudioStreaming interface handle.
|
|
*
|
|
* p_buf_len Pointer to allocated buffer length (in bytes).
|
|
*
|
|
* Return(s) : Pointer to record buffer, if NO error(s).
|
|
*
|
|
* Null pointer, otherwise.
|
|
*
|
|
* Note(s) : (1) Record buffer length is computed according to sample rate, bit resolution and number
|
|
* of channels in USBD_Audio_AS_EP_CtrlProcess(). The buffer length takes into account
|
|
* the data rate adjustment and the built-in correction if this one has been enabled.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
void *USBD_Audio_RecordBufGet (USBD_AUDIO_AS_HANDLE as_handle,
|
|
CPU_INT16U *p_buf_len)
|
|
{
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
void *p_buf;
|
|
CPU_INT16U ix;
|
|
|
|
|
|
/* ------------------- VALIDATE ARG ------------------- */
|
|
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
|
|
if (p_buf_len == DEF_NULL) { /* Validate buf len ptr. */
|
|
return (DEF_NULL);
|
|
}
|
|
#endif
|
|
|
|
p_as_if = USBD_Audio_AS_IF_Get(as_handle);
|
|
if (p_as_if == DEF_NULL) {
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordBufGet(): Invalid AS IF handle\r\n");
|
|
return (DEF_NULL);
|
|
}
|
|
|
|
if (USBD_AUDIO_AS_IF_HANDLE_VALIDATE(p_as_if, as_handle) != DEF_OK) {
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordBufGet(): Invalid AS IF handle\r\n");
|
|
return (DEF_NULL);
|
|
}
|
|
/* ----------------- RECORD BUF ALLOC ----------------- */
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQProducerStartIxGet(p_as_if->AS_IF_SettingsPtr);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
return (DEF_NULL);
|
|
}
|
|
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if->AS_IF_SettingsPtr->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if->AS_IF_SettingsPtr->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordBufGet(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
return (DEF_NULL);
|
|
}
|
|
|
|
p_buf = p_buf_desc->BufPtr;
|
|
*p_buf_len = p_buf_desc->BufLen; /* See Note #1. */
|
|
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if->AS_IF_SettingsPtr, &p_as_if->AS_IF_SettingsPtr->StreamRingBufQ.ProducerStartIx);
|
|
|
|
return (p_buf);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordRxCmpl()
|
|
*
|
|
* Description : Signal to the Record task that a buffer is ready.
|
|
*
|
|
* Argument(s) : as_handle AudioStreaming interface handle.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
void USBD_Audio_RecordRxCmpl (USBD_AUDIO_AS_HANDLE as_handle)
|
|
{
|
|
USBD_ERR err_usbd;
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
|
|
|
|
p_as_if = USBD_Audio_AS_IF_Get(as_handle);
|
|
if (p_as_if == DEF_NULL) {
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordRxCmpl(): Invalid AS IF handle\r\n");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
USBD_Audio_OS_RecordReqPost((void *)(CPU_ADDR)as_handle, /* Signal audio xfer cmpl. */
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("PlaybackTxCmpl(): signaling record task failed w/ err = %d\r\n", err_usbd);
|
|
} else {
|
|
USBD_AUDIO_STAT_INC(p_as_if->AS_IF_SettingsPtr->StatPtr->AudioProc_Record_NbrReqPostRecordTask);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordBufFree()
|
|
*
|
|
* Description : Free aborted record buffer.
|
|
*
|
|
* Argument(s) : as_handle AudioStreaming interface handle.
|
|
*
|
|
* p_buf Pointer to buffer to free.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) This function is DEPRECATED and will be removed in a future version of this product.
|
|
* The new record path implementation does not require the codec driver to free
|
|
* explicitly a consumed buffer.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
void USBD_Audio_RecordBufFree (USBD_AUDIO_AS_HANDLE as_handle,
|
|
void *p_buf)
|
|
{
|
|
(void)as_handle;
|
|
(void)p_buf;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackTxCmpl()
|
|
*
|
|
* Description : Signal end of audio transfer to playback task.
|
|
*
|
|
* Argument(s) : as_handle AudioStreaming interface handle.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
void USBD_Audio_PlaybackTxCmpl (USBD_AUDIO_AS_HANDLE as_handle)
|
|
{
|
|
USBD_ERR err_usbd;
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
|
|
|
|
p_as_if = USBD_Audio_AS_IF_Get(as_handle);
|
|
if (p_as_if == DEF_NULL) {
|
|
USBD_DBG_AUDIO_PROC_MSG("PlaybackTxCmpl(): Invalid AS IF handle\r\n");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
USBD_Audio_OS_PlaybackReqPost((void *)(CPU_ADDR)as_handle, /* Signal audio xfer cmpl. */
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("PlaybackTxCmpl(): signaling playback task failed w/ err = %d\r\n", err_usbd);
|
|
} else {
|
|
USBD_AUDIO_STAT_INC(p_as_if->AS_IF_SettingsPtr->StatPtr->AudioProc_Playback_NbrReqPostPlaybackTask);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackBufFree()
|
|
*
|
|
* Description : Update the Consumer End index in the stream ring buffer queue meaning that a buffer is
|
|
* available for a new USB transfer.
|
|
*
|
|
* Argument(s) : as_handle AudioStreaming interface handle.
|
|
*
|
|
* p_buf Pointer to buffer to free.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
void USBD_Audio_PlaybackBufFree (USBD_AUDIO_AS_HANDLE as_handle,
|
|
void *p_buf)
|
|
{
|
|
CPU_INT16U ix;
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
|
|
|
|
(void)p_buf;
|
|
|
|
p_as_if = USBD_Audio_AS_IF_Get(as_handle);
|
|
if (p_as_if == DEF_NULL) {
|
|
USBD_DBG_AUDIO_PROC_MSG("PlaybackBufFree(): Invalid AS IF handle\r\n");
|
|
return;
|
|
}
|
|
|
|
if (USBD_AUDIO_AS_IF_HANDLE_VALIDATE(p_as_if, as_handle) != DEF_OK) {
|
|
USBD_DBG_AUDIO_PROC_MSG("PlaybackBufFree(): Invalid AS IF handle\r\n");
|
|
return;
|
|
}
|
|
|
|
ix = USBD_Audio_AS_IF_RingBufQConsumerEndIxGet(p_as_if->AS_IF_SettingsPtr);
|
|
if (ix != USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) { /* Update ConsumerEndIx only if it did not catch up... */
|
|
/* ...ConsumerStartIx or ProducerStartIx. */
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if->AS_IF_SettingsPtr, &p_as_if->AS_IF_SettingsPtr->StreamRingBufQ.ConsumerEndIx);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_StatGet()
|
|
*
|
|
* Description : Get the audio statistics structure associated to the specified AudioStreaming interface.
|
|
*
|
|
* Argument(s) : as_handle AudioStreaming handle.
|
|
*
|
|
* Return(s) : Pointer to audio statistics.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
USBD_AUDIO_STAT *USBD_Audio_StatGet (USBD_AUDIO_AS_HANDLE as_handle)
|
|
{
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
CPU_INT08U as_if_ix;
|
|
|
|
|
|
as_if_ix = USBD_AUDIO_AS_IF_HANDLE_IX_GET(as_handle);
|
|
|
|
p_as_if = &USBD_Audio_AS_Tbl[as_if_ix];
|
|
|
|
return (p_as_if->AS_IF_SettingsPtr->StatPtr);
|
|
#else
|
|
(void)as_handle;
|
|
|
|
return (DEF_NULL);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
* LOCAL FUNCTIONS
|
|
*********************************************************************************************************
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordIsocCmpl()
|
|
*
|
|
* Description : Process isochronous IN transfer completion.
|
|
*
|
|
* Argument(s) : dev_nbr Device Number.
|
|
*
|
|
* ep_addr Endpoint Address.
|
|
*
|
|
* p_buf Pointer to the buffer containing Record Data that was sent to the host.
|
|
*
|
|
* buf_len Buffer length (in bytes).
|
|
*
|
|
* xfer_len Number of bytes transferred (in bytes).
|
|
*
|
|
* p_arg Pointer to the AudioStreaming IF passed into argument to this function.
|
|
*
|
|
* err Error status.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) If a transfer completes with an error code different from USBD_ERR_EP_ABORT, all
|
|
* the operations done in a normal situation, that is no error, are performed to reuse
|
|
* the freed buffer for a new buffer request.
|
|
*
|
|
* (2) An isochronous IN transfer is aborted (error USBD_ERR_EP_ABORT) if the stream is
|
|
* closed by the host or if the device disconnects from the host.
|
|
*
|
|
* (3) Ring buffer queue lock protects the 'ConsumerStartIx' when the record task must
|
|
* restart internally. When restarting a stream, the Record task starts an isochronous
|
|
* IN transfer. Record task may be preempted by the Core task because the isochronous
|
|
* IN transfer has finished but the Record task must update 'ConsumerStartIx' before
|
|
* 'ConsumerEndIx' checks it. Stream restart occurs because there is no more ongoing
|
|
* isochronous transfer in the USB driver.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_RecordIsocCmpl (CPU_INT08U dev_nbr,
|
|
CPU_INT08U ep_addr,
|
|
void *p_buf,
|
|
CPU_INT32U buf_len,
|
|
CPU_INT32U xfer_len,
|
|
void *p_arg,
|
|
USBD_ERR err)
|
|
{
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
CPU_INT16U nbr_xfer_submitted;
|
|
CPU_INT16U ix;
|
|
USBD_ERR err_usbd;
|
|
#if (USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
|
|
CPU_INT16U frame_nbr_cur;
|
|
CPU_INT16U frame_nbr_diff;
|
|
#endif
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
(void)dev_nbr;
|
|
(void)ep_addr;
|
|
(void)p_buf;
|
|
(void)buf_len;
|
|
(void)xfer_len;
|
|
|
|
p_as_if = (USBD_AUDIO_AS_IF *)p_arg;
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxCmpl);
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_as_if_settings->RecordIsocTxOngoingCnt--;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
/* -------- CHECK IF ERR UPON XFER COMPLETION --------- */
|
|
if ((err != USBD_ERR_NONE) &&
|
|
(err != USBD_ERR_EP_ABORT)) { /* See Note #1. */
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxCmplErrOther);
|
|
|
|
} else if (err == USBD_ERR_EP_ABORT) { /* See Note #2. */
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxCmplErrAbort);
|
|
return;
|
|
}
|
|
/* --------------- PREPARE NXT BUF REQ ---------------- */
|
|
USBD_Audio_OS_RingBufQLockAcquire(p_as_if_settings->Ix, /* See Note #3. */
|
|
USBD_AUDIO_LOCK_TIMEOUT_mS,
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("RecordisocCmpl(): lock acquire failed w/ err = %d\r\n", err_usbd);
|
|
return;
|
|
}
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQConsumerEndIxGet(p_as_if_settings);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
USBD_Audio_OS_RingBufQLockRelease(p_as_if_settings->Ix);
|
|
return;
|
|
}
|
|
|
|
USBD_Audio_OS_RingBufQLockRelease(p_as_if_settings->Ix);
|
|
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if_settings->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordisocCmpl(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
return;
|
|
}
|
|
|
|
p_buf_desc->BufLen = USBD_Audio_RecordDataRateAdj(p_as_if);
|
|
/* -------------- EVALUATE BUILT-IN CORR -------------- */
|
|
#if (USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
|
|
frame_nbr_cur = USBD_DevFrameNbrGet(p_as_if->DevNbr, &err_usbd);
|
|
frame_nbr_cur = USBD_FRAME_NBR_GET(frame_nbr_cur);
|
|
frame_nbr_diff = USBD_FRAME_NBR_DIFF_GET(p_as_if_settings->CorrFrameNbr, frame_nbr_cur);
|
|
/* Check if we match or exceed the corr period. */
|
|
if (frame_nbr_diff >= p_as_if_settings->CorrPeriod) {
|
|
|
|
USBD_Audio_RecordCorrBuiltIn(p_as_if, p_buf_desc);
|
|
|
|
p_as_if_settings->CorrFrameNbr = frame_nbr_cur; /* Keep frame nbr for next period computation. */
|
|
}
|
|
#endif
|
|
/* Update ix only after writing to buf desc. */
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if_settings, &p_as_if_settings->StreamRingBufQ.ConsumerEndIx);
|
|
|
|
/* -------- SUBMIT ISOC XFER(S) TO USB DEV DRV -------- */
|
|
USBD_Audio_RecordUsbBufSubmit(p_as_if, &nbr_xfer_submitted);
|
|
USBD_AUDIO_STAT_ADD(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxSubmitCoreTask, nbr_xfer_submitted);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordPrime()
|
|
*
|
|
* Description : Start the first isochronous transfer that will prime the record stream.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio streaming interface.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Stream priming successfully started.
|
|
* USBD_ERR_FAIL No buffer descriptor obtained from ring buffer queue.
|
|
*
|
|
* - RETURNED BY USBD_Audio_OS_RingBufQLockAcquire() : -
|
|
* USBD_ERR_OS_TIMEOUT OS lock NOT successfully acquired in the time
|
|
* specified by 'timeout_ms'.
|
|
* USBD_ERR_OS_ABORT OS lock aborted.
|
|
* USBD_ERR_OS_FAIL OS lock not acquired because another error.
|
|
*
|
|
* ------- RETURNED BY USBD_IsocTxAsync() : -------
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
|
|
* configured state.
|
|
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
|
|
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
|
|
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
|
|
*
|
|
* See specific device driver(s) 'USBD_EP_Tx()' for
|
|
* additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Ring buffer queue lock protects the 'ConsumerStartIx' when the record task must restart
|
|
* internally. When restarting a stream, the Record task starts an isochronous IN
|
|
* transfer. Record task may be preempted by the Core task because the isochronous IN
|
|
* transfer has finished but the Record task must update 'ConsumerStartIx' before
|
|
* 'ConsumerEndIx' checks it. Stream restart occurs because there is no more ongoing
|
|
* isochronous transfer in the USB driver.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_RecordPrime (USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt = p_as_if->AS_IF_AltCurPtr;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
CPU_INT16U ix;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
USBD_Audio_OS_RingBufQLockAcquire(p_as_if_settings->Ix, /* See Note #1. */
|
|
USBD_AUDIO_LOCK_TIMEOUT_mS,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("RecordPrime(): lock acquire failed w/ err = %d\r\n", *p_err);
|
|
return;
|
|
}
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQConsumerStartIxGet(p_as_if_settings);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxBufNotAvail);
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordPrime(): no buffer descriptor\r\n");
|
|
*p_err = USBD_ERR_FAIL;
|
|
goto end_lock_clean;
|
|
}
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if_settings->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordPrime(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
*p_err = USBD_ERR_FAIL;
|
|
goto end_lock_clean;
|
|
}
|
|
/* Submit buf to USB device driver. */
|
|
USBD_IsocTxAsync( p_as_if->DevNbr,
|
|
p_as_if_alt->DataIsocAddr,
|
|
p_buf_desc->BufPtr,
|
|
p_buf_desc->BufLen,
|
|
USBD_Audio_RecordIsocCmpl,
|
|
(void *)p_as_if,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("RecordPrime(): isochronous Tx failed w/ err = %d\r\n", *p_err);
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxSubmitErr);
|
|
goto end_lock_clean;
|
|
}
|
|
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if_settings, &p_as_if_settings->StreamRingBufQ.ConsumerStartIx);
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxSubmitSuccess);
|
|
CPU_CRITICAL_ENTER();
|
|
p_as_if_settings->RecordIsocTxOngoingCnt++;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
|
|
end_lock_clean:
|
|
USBD_Audio_OS_RingBufQLockRelease(p_as_if_settings->Ix);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordUsbBufSubmit()
|
|
*
|
|
* Description : Submit all possible record buffers to the USB device driver.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio streaming interface.
|
|
*
|
|
* p_xfer_submitted_cnt Pointer to number of transfers successfully submitted.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) The Core layer submits buffers as long as there are free buffers available and that
|
|
* the transfers can be queued by the USB device driver.
|
|
*
|
|
* (2) The USB device driver can queue isochronous transfers. USBD_ERR_EP_QUEUING is returned
|
|
* when there is no room left to queue the current isochronous transfer. In that case,
|
|
* another isochronous transfer will be submitted next time an isochronous transfer
|
|
* completes.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_RecordUsbBufSubmit (USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT16U *p_xfer_submitted_cnt)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt = p_as_if->AS_IF_AltCurPtr;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
CPU_BOOLEAN loop_end = DEF_NO;
|
|
USBD_ERR err_usbd;
|
|
CPU_INT16U ix;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
*p_xfer_submitted_cnt = 0u;
|
|
do {
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQConsumerStartIxGet(p_as_if_settings);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxBufNotAvail);
|
|
break;
|
|
}
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if_settings->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordUsbBufSubmit(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
break;
|
|
}
|
|
|
|
USBD_IsocTxAsync( p_as_if->DevNbr,
|
|
p_as_if_alt->DataIsocAddr,
|
|
p_buf_desc->BufPtr,
|
|
p_buf_desc->BufLen,
|
|
USBD_Audio_RecordIsocCmpl,
|
|
(void *)p_as_if,
|
|
&err_usbd);
|
|
if (err_usbd == USBD_ERR_NONE) { /* See Note #1. */
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if_settings, &p_as_if_settings->StreamRingBufQ.ConsumerStartIx);
|
|
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxSubmitSuccess);
|
|
*p_xfer_submitted_cnt += 1u;
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_as_if_settings->RecordIsocTxOngoingCnt++;
|
|
CPU_CRITICAL_EXIT();
|
|
} else { /* See Note #2. */
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_Record_NbrIsocTxSubmitErr);
|
|
loop_end = DEF_YES;
|
|
}
|
|
} while (loop_end != DEF_YES);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordDataRateAdj()
|
|
*
|
|
* Description : Adjusts buffer length if the sampling frequency (e.g. 11.025, 22.050, 44.1 KHz) produces
|
|
* a non-integer number of audio samples per ms.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio streaming interface.
|
|
*
|
|
* Return(s) : Adjusted buffer length.
|
|
*
|
|
* Note(s) : (1) Audio transfer for record adjusted by adding one sample frame for certain sampling
|
|
* frequencies (i.e. 11.025, 22.050 and 44.1 kHz) which produce a non-integer number
|
|
* of samples per ms. See function USBD_Audio_AS_EP_CtrlProcess() 'Note #3' for details
|
|
* about this data rate adjustment.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static CPU_INT16U USBD_Audio_RecordDataRateAdj (USBD_AUDIO_AS_IF *p_as_if)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_AS_IF_ALT *p_as_alt = p_as_if->AS_IF_AltCurPtr;
|
|
USBD_AUDIO_AS_ALT_CFG *p_as_cfg = p_as_alt->AS_CfgPtr;
|
|
CPU_INT08U sample_frame = p_as_cfg->SubframeSize * p_as_cfg->NbrCh;
|
|
CPU_INT16U buf_len = p_as_if_settings->RecordBufLen;
|
|
|
|
|
|
if (p_as_if_settings->RecordRateAdjMs != 0u) { /* Adjust buf len if necessary (see Note #1). */
|
|
p_as_if_settings->RecordRateAdjXferCtr++; /* Increment ctr tracking buf len adjustment. */
|
|
|
|
if (p_as_if_settings->RecordRateAdjXferCtr == p_as_if_settings->RecordRateAdjMs) {
|
|
buf_len = p_as_if_settings->RecordBufLen + sample_frame;
|
|
p_as_if_settings->RecordRateAdjXferCtr = 0u;
|
|
}
|
|
}
|
|
|
|
return (buf_len);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_RecordCorrBuiltIn()
|
|
*
|
|
* Description : Apply data rate error correction by inserting or removing audio sample frame (see Note #1).
|
|
*
|
|
* Argument(s) : p_as_if Pointer to AudioStreaming interface.
|
|
*
|
|
* p_buf_desc Pointer to buffer descriptor.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Record correction is done by removing (overrun situation) or inserting (underrun) an
|
|
* audio sample frame. The correction is done implicitly by the Audio Peripheral
|
|
* hardware by directly getting the right number of audio samples (-1 sample frame or +1
|
|
* sample frame) to accommodate the overrun or underrun situation. The benefit of
|
|
* handling the correction here is to avoid using a software algorithm in the Record
|
|
* task to remove or add an audio sample frame.
|
|
*
|
|
* (2) The overrun or underrun situation is detected by computing a buffers difference
|
|
* representing the difference between the ready buffers production by the codec and
|
|
* the consumption of these ready buffers by USB. This buffers' difference is obtained
|
|
* from the stream ring buffer queue. The ring buffer queue scheme is common to the
|
|
* playback and record streams. Within the audio class, the definition of overrun and
|
|
* underrun situation is "USB-centric". For record, a negative buffers' difference
|
|
* indicates an overrun situation, that is the USB consumes too quickly buffers and thus
|
|
* a sample is removed so that the codec production speeds up. Conversely, a positive
|
|
* buffers' difference indicates an underrun situation.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
|
|
static void USBD_Audio_RecordCorrBuiltIn (USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
|
|
USBD_AUDIO_AS_ALT_CFG *p_as_cfg;
|
|
CPU_INT08S buf_diff;
|
|
CPU_INT08U sample_frame;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
buf_diff = USBD_Audio_BufDiffGet(p_as_if_settings); /* Get cur buf diff between USB & codec. */
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
/* If safe zone, no correction applies. */
|
|
if ((buf_diff > p_as_if_settings->CorrBoundaryHeavyNeg) &&
|
|
(buf_diff < p_as_if_settings->CorrBoundaryHeavyPos)) {
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_CorrNbrSafeZone);
|
|
return;
|
|
}
|
|
|
|
p_as_if_alt = p_as_if->AS_IF_AltCurPtr;
|
|
p_as_cfg = p_as_if_alt->AS_CfgPtr;
|
|
sample_frame = p_as_cfg->NbrCh * p_as_cfg->SubframeSize; /* Compute audio frame size. */
|
|
/* -------------- OVERRUN: REMOVE SAMPLE -------------- */
|
|
if (buf_diff <= p_as_if_settings->CorrBoundaryHeavyNeg) { /* See Note 2. */
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_CorrNbrOverrun);
|
|
p_buf_desc->BufLen -= sample_frame;
|
|
|
|
} else { /* ------------- UNDERRUN: INSERT SAMPLE -------------- */
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_CorrNbrUnderrun);
|
|
p_buf_desc->BufLen += sample_frame;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_IsocPlaybackCmpl()
|
|
*
|
|
* Description : Process isochronous OUT transfer completion.
|
|
*
|
|
* Argument(s) : dev_nbr Device Number.
|
|
*
|
|
* ep_addr Endpoint Address.
|
|
*
|
|
* p_buf Pointer to the buffer containing audio data received from the host.
|
|
*
|
|
* buf_len Buffer length (in bytes).
|
|
*
|
|
* xfer_len Number of bytes received (in bytes).
|
|
*
|
|
* p_arg Pointer to the AudioStreaming IF passed into argument to this function.
|
|
*
|
|
* err Error status.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) An isochronous OUT transfer is aborted (error USBD_ERR_EP_ABORT) if the stream is
|
|
* closed by the host or if the device disconnects from the host.
|
|
*
|
|
* (2) Ring buffer queue lock protects the 'ProducerStartIx' when the Playback task must
|
|
* restart internally. When restarting a stream, the Playback task starts an isochronous
|
|
* OUT transfer. Playback task may be preempted by the Core task because the isochronous
|
|
* IN transfer has finished but the Playback task must update 'ProducerStartIx' before
|
|
* 'ProducerEndIx' checks it. Stream restart occurs because there is no more ongoing
|
|
* isochronous transfer in the USB driver.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackIsocCmpl (CPU_INT08U dev_nbr,
|
|
CPU_INT08U ep_addr,
|
|
void *p_buf,
|
|
CPU_INT32U buf_len,
|
|
CPU_INT32U xfer_len,
|
|
void *p_arg,
|
|
USBD_ERR err)
|
|
{
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED)
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
|
|
#endif
|
|
CPU_INT16U ix;
|
|
CPU_BOOLEAN valid;
|
|
CPU_INT16U nbr_xfer_submitted;
|
|
CPU_BOOLEAN pre_buf_compl;
|
|
USBD_ERR err_usbd;
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
CPU_SR_ALLOC();
|
|
#endif
|
|
|
|
|
|
(void)dev_nbr;
|
|
(void)ep_addr;
|
|
(void)p_buf;
|
|
(void)buf_len;
|
|
|
|
p_as_if = (USBD_AUDIO_AS_IF *)p_arg;
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_STAT_PROT_DEC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxOngoingCnt);
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxCmpl);
|
|
|
|
/* -------- CHECK IF ERR UPON XFER COMPLETION --------- */
|
|
if ((err != USBD_ERR_NONE) &&
|
|
(err != USBD_ERR_EP_ABORT)) {
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxCmplErrOther);
|
|
return;
|
|
|
|
} else if (err == USBD_ERR_EP_ABORT) { /* See Note #1. */
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxCmplErrAbort);
|
|
return;
|
|
}
|
|
/* ------------- STORE BUF IN RING BUF Q -------------- */
|
|
USBD_Audio_OS_RingBufQLockAcquire(p_as_if_settings->Ix, /* See note #2. */
|
|
USBD_AUDIO_LOCK_TIMEOUT_mS,
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("IsocPlaybackCmpl(): lock acquire failed w/ err = %d\r\n", err_usbd);
|
|
return;
|
|
}
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQProducerEndIxGet(p_as_if_settings);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_Audio_OS_RingBufQLockRelease(p_as_if_settings->Ix);
|
|
return;
|
|
}
|
|
|
|
USBD_Audio_OS_RingBufQLockRelease(p_as_if_settings->Ix);
|
|
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if_settings->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("IsocPlaybackCmpl(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
return;
|
|
}
|
|
/* Init buf desc. */
|
|
p_buf_desc->BufLen = xfer_len;
|
|
/* Update ix only after writing to buf desc. */
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if_settings, &p_as_if_settings->StreamRingBufQ.ProducerEndIx);
|
|
|
|
/* -------- SUBMIT ISOC XFER(S) TO USB DEV DRV -------- */
|
|
USBD_Audio_PlaybackUsbBufSubmit(p_as_if, &nbr_xfer_submitted);
|
|
USBD_AUDIO_STAT_ADD(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxSubmitCoreTask, nbr_xfer_submitted);
|
|
|
|
/* ----- START PLAYBACK ON CODEC IF PRIMING DONE ------ */
|
|
pre_buf_compl = p_as_if_settings->StreamRingBufQ.ProducerEndIx >= p_as_if_settings->StreamPreBufMax ? DEF_YES : DEF_NO;
|
|
|
|
if ((p_as_if_settings->StreamPrimingDone == DEF_NO) &&
|
|
(pre_buf_compl == DEF_YES)) {
|
|
|
|
valid = p_as_if_settings->AS_API_Ptr->StreamStart(p_as_if_settings->DrvInfoPtr,
|
|
p_as_if->Handle,
|
|
p_as_if_settings->TerminalID);
|
|
if (valid != DEF_OK) {
|
|
USBD_AUDIO_AS_IF_HANDLE_INVALIDATE(p_as_if); /* = stream marked as closed internally. */
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_NbrStreamClosed);
|
|
|
|
USBD_DBG_AUDIO_PROC_MSG("IsocPlaybackCmpl(): playback not started\r\n");
|
|
return;
|
|
}
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED)
|
|
/* Sample removal/insertion corr is en. */
|
|
p_as_if_alt = p_as_if->AS_IF_AltCurPtr;
|
|
if (p_as_if_alt->SynchIsocAddr == USBD_EP_ADDR_NONE) {
|
|
/* Get initial frame nbr for corr period computation. */
|
|
p_as_if_settings->CorrFrameNbr = USBD_DevFrameNbrGet(p_as_if->DevNbr, &err_usbd);
|
|
p_as_if_settings->CorrFrameNbr = USBD_FRAME_NBR_GET(p_as_if_settings->CorrFrameNbr);
|
|
}
|
|
#endif
|
|
|
|
p_as_if_settings->StreamPrimingDone = DEF_YES;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackPrime()
|
|
*
|
|
* Description : Start the first isochronous transfer that will prime the playback stream.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio streaming interface.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Stream priming successfully started.
|
|
* USBD_ERR_FAIL No buffer descriptor obtained from ring buffer queue.
|
|
*
|
|
* ------- RETURNED BY USBD_IsocRxAsync() : -------
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
|
|
* configured state.
|
|
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
|
|
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
|
|
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
|
|
*
|
|
* See specific device driver(s) 'USBD_EP_Rx()' for
|
|
* additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackPrime (USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt = p_as_if->AS_IF_AltCurPtr;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
CPU_INT16U ix;
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
CPU_SR_ALLOC();
|
|
#endif
|
|
|
|
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQProducerStartIxGet(p_as_if_settings);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
*p_err = USBD_ERR_FAIL;
|
|
return;
|
|
}
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if_settings->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("PlaybackPrime(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
*p_err = USBD_ERR_FAIL;
|
|
return;
|
|
}
|
|
|
|
USBD_IsocRxAsync( p_as_if->DevNbr,
|
|
p_as_if_alt->DataIsocAddr,
|
|
p_buf_desc->BufPtr,
|
|
p_as_if_settings->BufTotalLen,
|
|
USBD_Audio_PlaybackIsocCmpl,
|
|
(void *)p_as_if,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("PlaybackPrime(): isochronous Rx failed w/ err = %d\r\n", *p_err);
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxSubmitErr);
|
|
return;
|
|
}
|
|
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if_settings, &p_as_if_settings->StreamRingBufQ.ProducerStartIx);
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxSubmitSuccess);
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxSubmitCoreTask);
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxOngoingCnt);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackUsbBufSubmit()
|
|
*
|
|
* Description : Submit all possible playback buffers to the USB device driver.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio streaming interface.
|
|
*
|
|
* p_xfer_submitted_cnt Pointer to number of transfers successfully submitted.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Playback task or Core layer submits buffers as long as there are free buffers available
|
|
* and that the transfers can be queued by the USB device driver.
|
|
*
|
|
* (2) The USB device driver can queue isochronous transfers. USBD_ERR_EP_QUEUING is returned
|
|
* when there is no room left to queue the current isochronous transfer. In that case,
|
|
* another isochronous transfer will be submitted next time an isochronous transfer
|
|
* completes or the playback task processes an event.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackUsbBufSubmit (USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT16U *p_xfer_submitted_cnt)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt = p_as_if->AS_IF_AltCurPtr;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
CPU_BOOLEAN loop_end = DEF_NO;
|
|
CPU_INT16U ix;
|
|
USBD_ERR err_usbd;
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
CPU_SR_ALLOC();
|
|
#endif
|
|
|
|
|
|
*p_xfer_submitted_cnt = 0u;
|
|
|
|
do {
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQProducerStartIxGet(p_as_if_settings);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxBufNotAvail);
|
|
break;
|
|
}
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if_settings->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if->AS_IF_SettingsPtr->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("PlaybackUsbBufSubmit(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
break;
|
|
}
|
|
|
|
USBD_IsocRxAsync( p_as_if->DevNbr,
|
|
p_as_if_alt->DataIsocAddr,
|
|
p_buf_desc->BufPtr,
|
|
p_as_if_settings->BufTotalLen,
|
|
USBD_Audio_PlaybackIsocCmpl,
|
|
(void *)p_as_if,
|
|
&err_usbd);
|
|
if (err_usbd == USBD_ERR_NONE) {
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if_settings, &p_as_if_settings->StreamRingBufQ.ProducerStartIx);
|
|
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxSubmitSuccess);
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxOngoingCnt);
|
|
*p_xfer_submitted_cnt += 1u;
|
|
} else {
|
|
USBD_AUDIO_STAT_PROT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxSubmitErr);
|
|
loop_end = DEF_YES;
|
|
}
|
|
} while (loop_end != DEF_YES);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackBufSubmit()
|
|
*
|
|
* Description : Submits empty buffer(s) to USB and a ready buffer to the codec.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio streaming interface.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Ring buffer queue lock protects the index 'ProducerStartIx' from a specific race
|
|
* condition which can occur when the Playback task must restart internally.
|
|
* The race condition is more likely to happen when priority(core task) > priority
|
|
* (playback task). When restarting a stream, the Playback task starts an isochronous
|
|
* OUT transfer. Playback task may be preempted by the Core task because the isochronous
|
|
* OUT transfer has finished but the Playback task must update 'ProducerStartIx' before
|
|
* 'ProducerEndIx' checks it. The lock allows the safe update of 'ProducerStartIx' by
|
|
* the Playback task.
|
|
* When priority(playback task) > priority(core task), the race condition won't happen.
|
|
* Stream restart occurs because there are no more ongoing isochronous transfers in the
|
|
* USB driver.
|
|
*
|
|
* (2) USBD_Audio_PlaybackUsbBufSubmit() serves two purposes:
|
|
*
|
|
* (a) Restarting the stream if this one was broken because no more isochronous OUT
|
|
* transfers stored in the USB device driver
|
|
*
|
|
* (b) Simply submitting new USB transfer(s) to the USB driver besides the Core layer
|
|
* doing the same in USBD_Audio_PlaybackIsocCmpl().
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackBufSubmit (USBD_AUDIO_AS_IF *p_as_if)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
CPU_INT16U ix;
|
|
CPU_INT16U nbr_xfer_submitted;
|
|
USBD_ERR err_usbd;
|
|
|
|
|
|
/* --------------------- USB SIDE --------------------- */
|
|
USBD_Audio_OS_RingBufQLockAcquire(p_as_if_settings->Ix, /* See Note #1. */
|
|
USBD_AUDIO_LOCK_TIMEOUT_mS,
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("PlaybackCodecBufSubmit(): lock acquire failed w/ err = %d\r\n", err_usbd);
|
|
return;
|
|
}
|
|
/* See Note #2. */
|
|
USBD_Audio_PlaybackUsbBufSubmit(p_as_if, &nbr_xfer_submitted);
|
|
USBD_AUDIO_STAT_ADD(p_as_if_settings->StatPtr->AudioProc_Playback_NbrIsocRxSubmitPlaybackTask, nbr_xfer_submitted);
|
|
|
|
USBD_Audio_OS_RingBufQLockRelease(p_as_if_settings->Ix);
|
|
/* -------------------- CODEC SIDE -------------------- */
|
|
/* Get a buf desc from the Ring Buf Q. */
|
|
ix = USBD_Audio_AS_IF_RingBufQConsumerStartIxGet(p_as_if_settings);
|
|
if (ix == USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX) {
|
|
USBD_Audio_OS_DlyMs(1u); /* Wait 1ms to give chance to other tasks to execute. */
|
|
USBD_Audio_PlaybackTxCmpl(p_as_if->Handle);
|
|
return;
|
|
}
|
|
p_buf_desc = USBD_Audio_AS_IF_RingBufQGet(&p_as_if_settings->StreamRingBufQ, ix);
|
|
if (p_buf_desc == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrErr);
|
|
USBD_DBG_AUDIO_PROC_MSG("RecordPrime(): buf desc retrieved from Ring Buf Q should NOT be a NULL ptr\r\n");
|
|
return;
|
|
}
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED)
|
|
/* Apply correction if necessary. */
|
|
USBD_Audio_PlaybackCorrExec(p_as_if, p_buf_desc, &err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
return;
|
|
}
|
|
#endif
|
|
/* Submit 1 rdy buf to codec. */
|
|
p_as_if_settings->AS_API_Ptr->StreamPlaybackTx(p_as_if_settings->DrvInfoPtr,
|
|
p_as_if_settings->TerminalID,
|
|
p_buf_desc->BufPtr,
|
|
p_buf_desc->BufLen,
|
|
&err_usbd);
|
|
if (err_usbd != USBD_ERR_NONE) {
|
|
USBD_DBG_AUDIO_PROC_ERR("PlaybackCodecBufSubmit(): audio tx xfer not started w/ err = %d\r\n", err_usbd);
|
|
return;
|
|
}
|
|
|
|
USBD_Audio_AS_IF_RingBufQIxUpdate(p_as_if_settings, &p_as_if_settings->StreamRingBufQ.ConsumerStartIx);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackCorrExec()
|
|
*
|
|
* Description : Executes playback correction alogrithm if enabled and required.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio streaming interface.
|
|
*
|
|
* p_buf_desc Pointer to buffer to correct.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Synch (feedback) endpoint successfully processed.
|
|
* USBD_ERR_ALLOC Synch Buffer Alloc failed.
|
|
* USBD_ERR_FAIL USB driver failed to return the frame nbr.
|
|
*
|
|
* ------- RETURNED BY USBD_IsocTxAsync() : -------
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in configured
|
|
* state.
|
|
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
|
|
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
|
|
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
|
|
*
|
|
* See specific device driver(s) 'USBD_EP_Tx()' for
|
|
* additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) Playback built-in correction CANNOT be active when the endpoint uses the asynchronous
|
|
* synchronization as two different corrections method would coexist. Only one at a
|
|
* time can be applied on the stream.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
((USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED))
|
|
static void USBD_Audio_PlaybackCorrExec (USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
|
|
CPU_INT16U frame_nbr;
|
|
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_DISABLED)
|
|
(void)p_buf_desc;
|
|
#endif
|
|
|
|
frame_nbr = USBD_DevFrameNbrGet(p_as_if->DevNbr, p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return;
|
|
}
|
|
frame_nbr = USBD_FRAME_NBR_GET(frame_nbr);
|
|
|
|
p_as_if_alt = p_as_if->AS_IF_AltCurPtr;
|
|
if (p_as_if_alt->SynchIsocAddr != USBD_EP_ADDR_NONE) { /* See Note #1. */
|
|
/* ------------ SYNCH FEEDBACK CORRECTION ------------- */
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
|
|
USBD_Audio_PlaybackCorrSynch(p_as_if, frame_nbr, p_err);
|
|
#endif
|
|
|
|
} else {
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED)
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
CPU_INT16U frame_nbr_diff;
|
|
|
|
|
|
/* --------------- BUILT-IN CORRECTION ---------------- */
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
frame_nbr_diff = USBD_FRAME_NBR_DIFF_GET(p_as_if_settings->CorrFrameNbr, frame_nbr);
|
|
|
|
if (frame_nbr_diff >= p_as_if_settings->CorrPeriod) { /* Check if we match or exceed the corr period. */
|
|
|
|
USBD_Audio_PlaybackCorrBuiltIn(p_as_if, /* Apply corr on the received buf. */
|
|
p_buf_desc,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return;
|
|
}
|
|
|
|
p_as_if_settings->CorrFrameNbr = frame_nbr; /* Keep frame nbr for next period computation. */
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackCorrBuiltIn()
|
|
*
|
|
* Description : Apply data rate error correction by inserting or removing data samples (see Note #1).
|
|
*
|
|
* Argument(s) : p_as_if Pointer to AudioStreaming Interface.
|
|
*
|
|
* p_buf_desc Pointer to buffer descriptor.
|
|
*
|
|
* p_err Pointer to variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Correction successfully skipped or applied.
|
|
* USBD_ERR_FAIL AudioStreaming interface not active.
|
|
*
|
|
* -RETURNED BY p_as_if->PlaybackCorrCallbackPtr()-
|
|
* See p_as_if->PlaybackCorrCallbackPtr() for additional return error codes.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) This stream correction is convenient for low-cost audio design. It will give good
|
|
* results as long as the incoming USB audio sampling frequency is very close to the DAC
|
|
* input clock frequency. However, if the difference between the two frequencies is too
|
|
* high, this will add audio distortion.
|
|
*
|
|
* The stream correction supports signed PCM and unsigned PCM8 format.
|
|
*
|
|
* (2) A current buffers consumption difference between the USB and codec sides obtained
|
|
* from the playback ring buffer queue is monitored with lower and upper boundaries.
|
|
* Inside these boundaries, a safe zone is considered where no playback correction is
|
|
* required.
|
|
*
|
|
* (3) A user can provide his own stream correction algorithm when an underrun or overrun
|
|
* occurs. If it is available, the user's correction should be used instead of the
|
|
* built-in stream correction.
|
|
*
|
|
* (4) An overrun situation occurs when the USB part is faster than the audio part.
|
|
* In that case, an audio sample will be removed on each logical channel to
|
|
* re-synchronize the audio part so that this one can progressively catch up the USB
|
|
* rate. The audio sample removal algorithm is the following:
|
|
*
|
|
* Sample ^ Sample ^
|
|
* level | ___ level | ___
|
|
* |___| | |___| |
|
|
* | | |___ | | |___
|
|
* | | | |___ ====> | | | |
|
|
* | | | | |___ | | | |___
|
|
* | | | | | |___ | | | | |___
|
|
* _|___|___|___|___|___|___|_____> _|___|___|___|___|___|______>
|
|
* |N-5 N-4 N-3 N-2 N-1 N t |N-5 N-4 N-3 N-2 N-1 N t
|
|
*
|
|
* (a) Sample N-2 is rebuilt and equal to the average of N, N-1, N-2 and N-3
|
|
* (b) Sample N is moved at N-1
|
|
* (c) The packet size is reduced of one sample
|
|
*
|
|
* (5) An audio subframe holds a single PCM audio sample. An audio frame is a collection
|
|
* of audio subframes, each containing a PCM audio sample of a different physical
|
|
* audio channel, taken at the same moment in time.
|
|
*
|
|
* (6) An underrun situation occurs when the audio part is faster than the USB part.
|
|
* In that case, an audio sample will be added on each logical channel to
|
|
* re-synchronize the audio part so that this one can progressively relapsed on the USB
|
|
* rate. The audio sample insertion algorithm is the following:
|
|
*
|
|
* Sample ^ Sample ^
|
|
* level | ___ level | ___
|
|
* |___| | |___| |
|
|
* | | |___ | | |___
|
|
* | | | |___ ====> | | | |___
|
|
* | | | | | | | | | |___
|
|
* | | | | |___ | | | | | |___
|
|
* _|___|___|___|___|___|_____> _|___|___|___|___|___|___|__________>
|
|
* |N-4 N-3 N-2 N-1 N t |N-4 N-3 N-2 N-1 N N+1 t
|
|
*
|
|
* (a) Sample N is moved at N+1
|
|
* (b) Sample N is rebuilt and equal to the average of N-1 and N+1
|
|
* (c) The packet size is increased of one sample
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackCorrBuiltIn (USBD_AUDIO_AS_IF *p_as_if,
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
|
|
USBD_AUDIO_AS_ALT_CFG *p_as_cfg;
|
|
CPU_INT08U *p_subframe;
|
|
CPU_INT08U *p_frame;
|
|
CPU_INT08U *p_buf_end;
|
|
CPU_INT08U *p_frame_n_p1;
|
|
CPU_INT08U *p_frame_n;
|
|
CPU_INT08U *p_frame_n_m1;
|
|
CPU_INT08U *p_frame_n_m2;
|
|
CPU_INT32S buf_diff;
|
|
CPU_INT08U subframe_len;
|
|
CPU_INT08U frame_len;
|
|
CPU_INT16U buf_len_min;
|
|
CPU_INT08U ch_ix;
|
|
CPU_INT08U i;
|
|
CPU_INT32S sample_val;
|
|
CPU_INT64S sum;
|
|
CPU_INT32S average;
|
|
CPU_INT16U new_buf_len;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
buf_diff = USBD_Audio_BufDiffGet(p_as_if_settings); /* Get cur buf diff between USB & codec. */
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
/* If safe zone, no correction applies (see Note #2). */
|
|
if ((buf_diff > p_as_if_settings->CorrBoundaryHeavyNeg) &&
|
|
(buf_diff < p_as_if_settings->CorrBoundaryHeavyPos)) {
|
|
|
|
*p_err = USBD_ERR_NONE; /* No need of stream correction. */
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_CorrNbrSafeZone);
|
|
return;
|
|
}
|
|
|
|
p_as_if_alt = p_as_if->AS_IF_AltCurPtr; /* Get active alternate setting for given AS IF. */
|
|
p_as_cfg = p_as_if_alt->AS_CfgPtr; /* Get user AS IF cfg. */
|
|
/* Compute audio frame size. */
|
|
subframe_len = p_as_cfg->SubframeSize;
|
|
frame_len = p_as_cfg->NbrCh * subframe_len;
|
|
/* Check if enough samples in buf to apply corr. */
|
|
buf_len_min = USBD_AUDIO_PLAYBACK_CORR_MIN_NBR_SAMPLES * frame_len;
|
|
if (p_buf_desc->BufLen < buf_len_min) {
|
|
*p_err = USBD_ERR_FAIL;
|
|
return;
|
|
}
|
|
/* -------------- OVERRUN: REMOVE SAMPLE -------------- */
|
|
if (buf_diff >= p_as_if_settings->CorrBoundaryHeavyPos) {
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_CorrNbrOverrun);
|
|
|
|
if (p_as_if_settings->CorrCallbackPtr != (USBD_AUDIO_PLAYBACK_CORR_FNCT)0) {
|
|
/* See Note #3. */
|
|
new_buf_len = p_as_if_settings->CorrCallbackPtr(p_as_cfg,
|
|
DEF_NO,
|
|
p_buf_desc->BufPtr,
|
|
p_buf_desc->BufLen,
|
|
p_as_if_settings->BufTotalLen,
|
|
p_err);
|
|
|
|
p_buf_desc->BufLen = new_buf_len; /* Buf size has been reduced of X samples by app... */
|
|
/* ...corr algorithm. */
|
|
|
|
} else { /* See Note #4. */
|
|
/* Get ptr to different audio frames within buf. */
|
|
p_buf_end = ((CPU_INT08U *)p_buf_desc->BufPtr) + p_buf_desc->BufLen;
|
|
p_frame_n = p_buf_end - frame_len;
|
|
p_frame_n_m1 = p_frame_n - frame_len;
|
|
p_frame_n_m2 = p_frame_n_m1 - frame_len;
|
|
|
|
for (ch_ix = 0u; ch_ix < p_as_cfg->NbrCh; ch_ix++) {/* Iterate through every log ch. */
|
|
|
|
p_frame = p_buf_end; /* Point to buf end. */
|
|
sum = 0u;
|
|
for (i = 0u; i < USBD_AUDIO_OVERRUN_NBR_SAMPLES_FOR_AVERAGING; i++) {
|
|
|
|
p_frame -= frame_len; /* Point to sample N-x (with x = 0, 1, 2, 3). */
|
|
p_subframe = p_frame;
|
|
p_subframe += (subframe_len * ch_ix); /* Within frame, point to subframe associated to a... */
|
|
/* ...given log ch (see Note #5). */
|
|
/* Get sample value. */
|
|
sample_val = 0u;
|
|
MEM_VAL_COPY_GET_INTU(&sample_val, p_subframe, subframe_len);
|
|
if ((subframe_len == USBD_AUDIO_FMT_TYPE_I_SUBFRAME_SIZE_2) ||
|
|
(subframe_len == USBD_AUDIO_FMT_TYPE_I_SUBFRAME_SIZE_3)) {
|
|
/* Ensure proper signed integer representation by... */
|
|
/* ...testing bit sign. */
|
|
if (DEF_BIT_IS_SET(sample_val, DEF_BIT((p_as_cfg->BitRes - 1u))) == DEF_YES) {
|
|
sample_val |= DEF_BIT_FIELD_32((32u - p_as_cfg->BitRes), p_as_cfg->BitRes);
|
|
}
|
|
}
|
|
sum += sample_val;
|
|
}
|
|
/* ...sample N-2 is rebuilt and equal to the average... */
|
|
/* ... of N, N-1, N-2 and N-3. */
|
|
average = (CPU_INT32S)(sum / USBD_AUDIO_OVERRUN_NBR_SAMPLES_FOR_AVERAGING);
|
|
p_subframe = p_frame_n_m2;
|
|
p_subframe += (subframe_len * ch_ix);
|
|
MEM_VAL_COPY(p_subframe, &average, subframe_len);
|
|
}
|
|
|
|
Mem_Copy(p_frame_n_m1, p_frame_n, frame_len); /* Sample N is moved at N-1. */
|
|
|
|
p_buf_desc->BufLen -= frame_len; /* The packet size is reduced by one sample. */
|
|
}
|
|
|
|
} else { /* ------------- UNDERRUN: INSERT SAMPLE -------------- */
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_CorrNbrUnderrun);
|
|
/* Call app corr callback. */
|
|
if (p_as_if_settings->CorrCallbackPtr != (USBD_AUDIO_PLAYBACK_CORR_FNCT)0) {
|
|
/* See Note #3. */
|
|
new_buf_len = p_as_if_settings->CorrCallbackPtr(p_as_cfg,
|
|
DEF_YES,
|
|
p_buf_desc->BufPtr,
|
|
p_buf_desc->BufLen,
|
|
p_as_if_settings->BufTotalLen,
|
|
p_err);
|
|
|
|
p_buf_desc->BufLen = new_buf_len; /* Buf size has been increased of X samples by app... */
|
|
/* ...corr algorithm. */
|
|
|
|
} else { /* See Note #6. */
|
|
/* Get ptr to different frames within playback buf. */
|
|
p_buf_end = ((CPU_INT08U *)p_buf_desc->BufPtr) + p_buf_desc->BufLen;
|
|
p_frame_n_p1 = p_buf_end;
|
|
p_frame_n = p_buf_end - frame_len;
|
|
p_frame_n_m1 = p_frame_n - frame_len;
|
|
|
|
Mem_Copy(p_frame_n_p1, p_frame_n, frame_len); /* Sample N is moved at N+1 */
|
|
|
|
for (ch_ix = 0u; ch_ix < p_as_cfg->NbrCh; ch_ix++) {/* Iterate through every log ch. */
|
|
|
|
sum = 0u;
|
|
for (i = 0u; i < USBD_AUDIO_UNDERRUN_NBR_SAMPLES_FOR_AVERAGING; i++) {
|
|
|
|
if (i == 0u) { /* Point to sample N+1. */
|
|
p_frame = p_frame_n_p1;
|
|
} else {
|
|
p_frame = p_frame_n_m1; /* Point to sample N-1. */
|
|
}
|
|
p_subframe = p_frame;
|
|
p_subframe += (subframe_len * ch_ix); /* Within frame, point to subframe associated to a... */
|
|
/* ...given log ch (see Note #5). */
|
|
sample_val = 0u;
|
|
MEM_VAL_COPY_GET_INTU(&sample_val, p_subframe, subframe_len);
|
|
if ((subframe_len == USBD_AUDIO_FMT_TYPE_I_SUBFRAME_SIZE_2) ||
|
|
(subframe_len == USBD_AUDIO_FMT_TYPE_I_SUBFRAME_SIZE_3)) {
|
|
/* Ensure proper signed integer representation by... */
|
|
/* ...testing bit sign. */
|
|
if (DEF_BIT_IS_SET(sample_val, DEF_BIT((p_as_cfg->BitRes - 1u))) == DEF_YES) {
|
|
sample_val |= DEF_BIT_FIELD_32((32u - p_as_cfg->BitRes), p_as_cfg->BitRes);
|
|
}
|
|
}
|
|
sum += sample_val;
|
|
}
|
|
|
|
average = (CPU_INT32S)(sum / USBD_AUDIO_UNDERRUN_NBR_SAMPLES_FOR_AVERAGING);
|
|
p_subframe = p_frame_n;
|
|
p_subframe += (subframe_len * ch_ix);
|
|
/* ...sample N is rebuilt and equal to the average of...*/
|
|
/* ...N-1 and N+1. */
|
|
MEM_VAL_COPY(p_subframe, &average, subframe_len);
|
|
}
|
|
|
|
p_buf_desc->BufLen += frame_len; /* The packet size is increased by one sample. */
|
|
}
|
|
}
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackCorrSynchInit()
|
|
*
|
|
* Description : Initializes playback synch correction.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the AudioStreaming interface.
|
|
*
|
|
* sampling_freq Value of the current sampling frequency of this streaming interface.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Synch (feedback) endpoint successfully processed.
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid dev_nbr passed to USBD_DevSpdGet().
|
|
* USBD_ERR_FAIL USB driver failed to return the frame nbr.
|
|
* USBD_ERR_ALLOC Codec driver API not found.
|
|
*
|
|
* ------- RETURNED BY USBD_IsocTxAsync() : -------
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in configured
|
|
* state.
|
|
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
|
|
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
|
|
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
|
|
*
|
|
* See specific device driver(s) 'USBD_EP_Tx()' for
|
|
* additional return error codes.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackCorrSynchInit (USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT32U sampling_freq,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_DEV_SPD spd;
|
|
CPU_INT08U bit_shift;
|
|
CPU_INT08U xfer_len;
|
|
CPU_INT32U *p_feedback_buf;
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
|
|
|
|
spd = USBD_DevSpdGet(p_as_if->DevNbr, p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return;
|
|
}
|
|
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
|
|
p_as_if_settings->PlaybackSynch.SynchFrameNbr = USBD_DevFrameNbrGet(p_as_if->DevNbr, p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
*p_err = USBD_ERR_FAIL;
|
|
return;
|
|
}
|
|
|
|
if (spd == USBD_DEV_SPD_FULL) {
|
|
/* Value formatted in FS 10.14 format. */
|
|
bit_shift = USBD_AUDIO_PLAYBACK_FS_BIT_SHIFT;
|
|
xfer_len = USBD_AUDIO_FS_SYNCH_EP_XFER_LEN;
|
|
} else {
|
|
bit_shift = USBD_AUDIO_PLAYBACK_HS_BIT_SHIFT;
|
|
xfer_len = USBD_AUDIO_HS_SYNCH_EP_XFER_LEN;
|
|
}
|
|
|
|
/* Init frame nbr for synch EP. */
|
|
p_as_if_settings->PlaybackSynch.SynchFrameNbr = USBD_FRAME_NBR_GET(p_as_if_settings->PlaybackSynch.SynchFrameNbr);
|
|
p_as_if_settings->PlaybackSynch.PrevFrameNbr = p_as_if_settings->PlaybackSynch.SynchFrameNbr;
|
|
/* Get integer part of freq. */
|
|
p_as_if_settings->PlaybackSynch.FeedbackNominalVal = (sampling_freq / 1000u) << bit_shift;
|
|
/* Get fractional part of feedback val. */
|
|
p_as_if_settings->PlaybackSynch.FeedbackNominalVal += ((sampling_freq % 1000u) << bit_shift) / 1000u;
|
|
p_as_if_settings->PlaybackSynch.FeedbackCurVal = p_as_if_settings->PlaybackSynch.FeedbackNominalVal;
|
|
p_as_if_settings->PlaybackSynch.PrevBufDiff = 0u;
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_NO;
|
|
|
|
p_feedback_buf = USBD_Audio_PlaybackSynchBufGet(p_as_if); /* Get synch buf. */
|
|
if (p_feedback_buf == DEF_NULL) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrBufNotAvail);
|
|
*p_err = USBD_ERR_ALLOC;
|
|
USBD_DBG_AUDIO_PROC_ERR("PlaybackCorrSynchInit(): cannot get a feedback buffer w/ err = %d\r\n", *p_err);
|
|
return;
|
|
}
|
|
|
|
MEM_VAL_SET_INT32U_LITTLE(p_feedback_buf, p_as_if_settings->PlaybackSynch.FeedbackNominalVal);
|
|
|
|
USBD_IsocTxAsync( p_as_if->DevNbr, /* Start new xfer with nominal feedback val. */
|
|
p_as_if->AS_IF_AltCurPtr->SynchIsocAddr,
|
|
(void *)p_feedback_buf,
|
|
xfer_len,
|
|
USBD_Audio_PlaybackIsocSynchCmpl,
|
|
(void *)p_as_if,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
USBD_Audio_PlaybackSynchBufFree(p_as_if, p_feedback_buf);
|
|
USBD_DBG_AUDIO_PROC_ERR("PlaybackCorrSynchInit(): isoc tx xfer not started w/ err = %d\r\n", *p_err);
|
|
return;
|
|
}
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrIsocTxSubmitted);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_IsocPlaybackSynchCmpl()
|
|
*
|
|
* Description : Playback Synch Out transfer completion.
|
|
*
|
|
* Argument(s) : dev_nbr Device Number.
|
|
*
|
|
* ep_addr Endpoint Address.
|
|
*
|
|
* p_buf Pointer to the buffer containing data that was sent to the host.
|
|
*
|
|
* buf_len Buffer length (in bytes).
|
|
*
|
|
* xfer_len Number of bytes received (in bytes).
|
|
*
|
|
* p_arg Pointer to the AudioStreaming IF passed into argument to this function.
|
|
*
|
|
* err Error status.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : None.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackIsocSynchCmpl (CPU_INT08U dev_nbr,
|
|
CPU_INT08U ep_addr,
|
|
void *p_buf,
|
|
CPU_INT32U buf_len,
|
|
CPU_INT32U xfer_len,
|
|
void *p_arg,
|
|
USBD_ERR err)
|
|
{
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
|
|
|
|
(void)dev_nbr;
|
|
(void)ep_addr;
|
|
(void)buf_len;
|
|
(void)xfer_len;
|
|
(void)p_arg;
|
|
(void)err;
|
|
|
|
p_as_if = (USBD_AUDIO_AS_IF *)p_arg;
|
|
|
|
USBD_Audio_PlaybackSynchBufFree(p_as_if, p_buf);
|
|
USBD_AUDIO_STAT_INC(p_as_if->AS_IF_SettingsPtr->StatPtr->AudioProc_Playback_SynchNbrIsocTxCmpl);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_PlaybackCorrSynch()
|
|
*
|
|
* Description : Performs feedback correction.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the AudioStreaming interface.
|
|
*
|
|
* frame_nbr Current frame number.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Synch (feedback) correction successfully.
|
|
*
|
|
* ------- RETURNED BY USBD_IsocTxAsync() : -------
|
|
* USBD_ERR_DEV_INVALID_NBR Invalid device number.
|
|
* USBD_ERR_DEV_INVALID_STATE Transfer type only available if device is in
|
|
* configured state.
|
|
* USBD_ERR_EP_INVALID_ADDR Invalid endpoint address.
|
|
* USBD_ERR_EP_INVALID_STATE Invalid endpoint state.
|
|
* USBD_ERR_EP_INVALID_TYPE Invalid endpoint type.
|
|
*
|
|
* See specific device driver(s) 'USBD_EP_Tx()' for
|
|
* additional return error codes.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) Ff is expressed in number of samples per (micro)frame. for full-speed endpoints, the
|
|
* Ff value shall be encoded in an unsigned 10.10 (K=10) format which fits into three
|
|
* bytes. Because the maximum integer value is fixed to 1,023, the 10.10 number will be
|
|
* left-justified in the 24 bits, so that it has a 10.14 format.
|
|
* See 'Universal Serial Bus Specification Revision 2.0, Release 2.0, April 27, 2000',
|
|
* Section 5.12.4.2 for more information about Feedback.
|
|
*
|
|
* (2) The feedback processing is done by monitoring the difference between the buffers
|
|
* produced by the USB side and the buffers consumed by the codec side. The following
|
|
* figure illustrates the processing principle:
|
|
*
|
|
* Speed USB << Codec USB < Codec USB = Codec USB > Codec USB >> Codec
|
|
* Adjustment MAX_ADJ MED_ADJ MIN_ADJ/MAX_ADJ - - - - MIN_ADJ/MAX_ADJ MED_ADJ MAX_ADJ
|
|
* Adj (sample) +1 +1/2 +1 > adj > +1/2048 - - - - -1/2048 > adj> -1 -1/2 -1
|
|
*
|
|
* <-|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-----------|-->
|
|
* Buffer diff -5 -4 -3 -2 -1 0 1 2 3 4 5
|
|
* |_______________________|_______________________________________________________________________|_______________________|
|
|
* | UNDERRUN | SAFE ZONE | OVERRUN |
|
|
* heavy light light heavy
|
|
*
|
|
* The underrun situation occurs when the USB side is slower than the codec side. In
|
|
* that case, depending how fast is the codec, the underrun situation could be light
|
|
* or heavy. The processing will adjust the feedback value by telling the host to
|
|
* add up to one sample per frame depending of the underrun degree.
|
|
* The overun situation occurs when the USB side is faster than the codec side. In
|
|
* that case, depending how slow is the codec, the overrun situation could be light
|
|
* or heavy. The processing will adjust the feedback value by telling the host to
|
|
* remove up to one sample per frame depending of the overrun degree.
|
|
*
|
|
* (3) When coming from the safe zone, light underrun or overrun is corrected with a
|
|
* feedback value taking into account the variation of buffers during a certain
|
|
* number of elapsed frames. This allows to correct smoothly the stream deviation.
|
|
* The feedback value adjustment is between:
|
|
*
|
|
* MINIMIM ADJUSTMENT < adjustment < MAXIMUM ADJUSTMENT
|
|
* +1/2048 sample < adjustment < +1 sample (underrun situation)
|
|
* -1 sample < adjustment < -1/2048 sample (overrun situation)
|
|
*
|
|
* (4) Practical tests with Windows has shown that Windows occasionally does NOT respect the
|
|
* bRefresh interval. Given that the feedback value uses one unique synch buffer, if a
|
|
* new feedback value is ready for the host, it may happen that the single buffer is not
|
|
* yet available because the host has not yet retrieved the current feedback value waiting
|
|
* in the USB driver. In that case, the new feedback value is lost and another feedback
|
|
* value may be sent to the host at the next bRefresh interval.
|
|
*
|
|
* (5) The bRefresh field of the synch standard endpoint descriptor is used to report the
|
|
* exponent (10-P) to the Host. It can range from 9 down to 1. (512 ms down to 2 ms)
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* Section 3.7.2.2 for more details about Isochronous Synch Endpoint.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackCorrSynch (USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT16U frame_nbr,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
CPU_INT16U frame_nbr_diff;
|
|
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
|
|
CPU_INT32U *p_feedback_buf;
|
|
CPU_INT32S buf_diff;
|
|
CPU_INT32S prev_buf_diff;
|
|
CPU_INT32U feedback_val_adj;
|
|
CPU_INT16U prev_frame_nbr;
|
|
CPU_INT16U frame_synch_refresh;
|
|
CPU_BOOLEAN save_info;
|
|
USBD_DEV_SPD spd;
|
|
CPU_INT08U feedback_val_bit_shift;
|
|
CPU_INT08U feedback_len;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
p_as_if_alt = p_as_if->AS_IF_AltCurPtr;
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
buf_diff = USBD_Audio_BufDiffGet(p_as_if_settings); /* Get cur buf diff between USB & codec. */
|
|
CPU_CRITICAL_EXIT();
|
|
prev_buf_diff = p_as_if_settings->PlaybackSynch.PrevBufDiff;
|
|
prev_frame_nbr = p_as_if_settings->PlaybackSynch.PrevFrameNbr;
|
|
frame_nbr_diff = USBD_FRAME_NBR_DIFF_GET(prev_frame_nbr, frame_nbr);
|
|
save_info = DEF_NO;
|
|
|
|
spd = USBD_DevSpdGet(p_as_if->DevNbr, p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
return;
|
|
}
|
|
|
|
if (spd == USBD_DEV_SPD_FULL) { /* See Note 1. */
|
|
/* Value formatted in FS 10.14 format. */
|
|
feedback_val_bit_shift = USBD_AUDIO_PLAYBACK_FS_BIT_SHIFT;
|
|
feedback_len = USBD_AUDIO_FS_SYNCH_EP_XFER_LEN;
|
|
} else {
|
|
feedback_val_bit_shift = USBD_AUDIO_PLAYBACK_HS_BIT_SHIFT;
|
|
feedback_len = USBD_AUDIO_HS_SYNCH_EP_XFER_LEN;
|
|
}
|
|
/* Processing to monitor feedback needs (see Note #2). */
|
|
if (buf_diff == 0) { /* No diff between received and consumed nb of buffs. */
|
|
save_info = DEF_YES;
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrSafeZone);
|
|
|
|
if (prev_buf_diff != 0) { /* Coming from underrun or overrun. */
|
|
/* Restore feedback val to its nominal val. */
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_YES;
|
|
p_as_if_settings->PlaybackSynch.FeedbackCurVal = p_as_if_settings->PlaybackSynch.FeedbackNominalVal;
|
|
}
|
|
} else if (buf_diff >= p_as_if_settings->CorrBoundaryHeavyPos) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrOverrun);
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrHeavyOverrun);
|
|
|
|
/* Heavy overrun. Do nothing if not the first time. */
|
|
if (prev_buf_diff < p_as_if_settings->CorrBoundaryHeavyPos) {
|
|
/* First time in heavy overrun. */
|
|
save_info = DEF_YES;
|
|
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_YES;
|
|
/* Apply max adj to reduce nbr of samples rx'd. */
|
|
p_as_if_settings->PlaybackSynch.FeedbackCurVal = p_as_if_settings->PlaybackSynch.FeedbackNominalVal -
|
|
USBD_AUDIO_PLAYBACK_SYNCH_MAX_ADJ(feedback_val_bit_shift);
|
|
}
|
|
} else if (buf_diff <= p_as_if_settings->CorrBoundaryHeavyNeg) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrUnderrun);
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrHeavyUnderrun);
|
|
|
|
/* Heavy underrun. Do nothing if not the first time. */
|
|
if (prev_buf_diff > p_as_if_settings->CorrBoundaryHeavyNeg) {
|
|
/* First time in heavy underrun. */
|
|
/* Apply max adj to increase nbr of samples rx'd. */
|
|
save_info = DEF_YES;
|
|
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_YES;
|
|
p_as_if_settings->PlaybackSynch.FeedbackCurVal = p_as_if_settings->PlaybackSynch.FeedbackNominalVal +
|
|
USBD_AUDIO_PLAYBACK_SYNCH_MAX_ADJ(feedback_val_bit_shift);
|
|
}
|
|
} else if (buf_diff >= p_as_if_settings->PlaybackSynch.SynchBoundaryLightPos) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrOverrun);
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrLightOverrun);
|
|
|
|
/* Light overrun. Do nothing if not the first time. */
|
|
if (prev_buf_diff >= p_as_if_settings->CorrBoundaryHeavyPos) {
|
|
/* First time in light overrun. Coming from heavy ... */
|
|
/* ... overrun, reduce adj to avoid overshoot. */
|
|
save_info = DEF_YES;
|
|
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_YES;
|
|
p_as_if_settings->PlaybackSynch.FeedbackCurVal = p_as_if_settings->PlaybackSynch.FeedbackNominalVal -
|
|
USBD_AUDIO_PLAYBACK_SYNCH_MED_ADJ(feedback_val_bit_shift);
|
|
} else if (prev_buf_diff < p_as_if_settings->PlaybackSynch.SynchBoundaryLightPos) {
|
|
/* First time in light overrun. Was 'OK' before. */
|
|
save_info = DEF_YES;
|
|
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_YES;
|
|
/* Calculate feedback val to send (see Note 3). */
|
|
feedback_val_adj = (p_as_if_settings->PlaybackSynch.SynchBoundaryLightPos << feedback_val_bit_shift) / frame_nbr_diff;
|
|
feedback_val_adj = (feedback_val_adj < USBD_AUDIO_PLAYBACK_SYNCH_MIN_ADJ(feedback_val_bit_shift)) ?
|
|
USBD_AUDIO_PLAYBACK_SYNCH_MIN_ADJ(feedback_val_bit_shift) : feedback_val_adj;
|
|
feedback_val_adj = (feedback_val_adj > USBD_AUDIO_PLAYBACK_SYNCH_MAX_ADJ(feedback_val_bit_shift)) ?
|
|
USBD_AUDIO_PLAYBACK_SYNCH_MAX_ADJ(feedback_val_bit_shift) : feedback_val_adj;
|
|
p_as_if_settings->PlaybackSynch.FeedbackCurVal = p_as_if_settings->PlaybackSynch.FeedbackCurVal - feedback_val_adj;
|
|
}
|
|
} else if (buf_diff <= p_as_if_settings->PlaybackSynch.SynchBoundaryLightNeg) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrUnderrun);
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrLightUnderrun);
|
|
/* Light underrun. Do nothing if not the first time. */
|
|
if (prev_buf_diff <= p_as_if_settings->CorrBoundaryHeavyNeg) {
|
|
/* First time in light underrun. Coming from heavy ... */
|
|
/* ... underrun, reduce adj to avoid overshoot. */
|
|
save_info = DEF_YES;
|
|
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_YES;
|
|
p_as_if_settings->PlaybackSynch.FeedbackCurVal = p_as_if_settings->PlaybackSynch.FeedbackNominalVal +
|
|
USBD_AUDIO_PLAYBACK_SYNCH_MED_ADJ(feedback_val_bit_shift);
|
|
|
|
} else if (prev_buf_diff > p_as_if_settings->PlaybackSynch.SynchBoundaryLightNeg) {
|
|
/* First time in light underrun. Was 'OK' before. */
|
|
save_info = DEF_YES;
|
|
|
|
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_YES;
|
|
/* Calculate feedback val to send (see Note 3). */
|
|
feedback_val_adj = (p_as_if_settings->PlaybackSynch.SynchBoundaryLightPos << feedback_val_bit_shift) / frame_nbr_diff;
|
|
feedback_val_adj = (feedback_val_adj < USBD_AUDIO_PLAYBACK_SYNCH_MIN_ADJ(feedback_val_bit_shift)) ?
|
|
USBD_AUDIO_PLAYBACK_SYNCH_MIN_ADJ(feedback_val_bit_shift) : feedback_val_adj;
|
|
feedback_val_adj = (feedback_val_adj > USBD_AUDIO_PLAYBACK_SYNCH_MAX_ADJ(feedback_val_bit_shift)) ?
|
|
USBD_AUDIO_PLAYBACK_SYNCH_MAX_ADJ(feedback_val_bit_shift) : feedback_val_adj;
|
|
p_as_if_settings->PlaybackSynch.FeedbackCurVal = p_as_if_settings->PlaybackSynch.FeedbackCurVal + feedback_val_adj;
|
|
}
|
|
}
|
|
|
|
if (save_info == DEF_YES) {
|
|
p_as_if_settings->PlaybackSynch.PrevBufDiff = buf_diff;
|
|
p_as_if_settings->PlaybackSynch.PrevFrameNbr = frame_nbr;
|
|
}
|
|
|
|
frame_synch_refresh = (1u << (p_as_if_alt->AS_CfgPtr->EP_SynchRefresh));
|
|
frame_nbr_diff = USBD_FRAME_NBR_DIFF_GET(p_as_if_settings->PlaybackSynch.SynchFrameNbr, frame_nbr);
|
|
|
|
if (frame_nbr_diff >= frame_synch_refresh) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrRefreshPeriodReached);
|
|
p_as_if_settings->PlaybackSynch.SynchFrameNbr = frame_nbr; /* Update the frame nbr. */
|
|
|
|
/* Check if a transmit is required. */
|
|
if (p_as_if_settings->PlaybackSynch.FeedbackValUpdate == DEF_YES) {
|
|
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_NO;
|
|
|
|
p_feedback_buf = USBD_Audio_PlaybackSynchBufGet(p_as_if);
|
|
if (p_feedback_buf == DEF_NULL) { /* See Note 4. */
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrBufNotAvail);
|
|
return;
|
|
}
|
|
/* See Note #5 */
|
|
MEM_VAL_SET_INT32U_LITTLE(p_feedback_buf, p_as_if_settings->PlaybackSynch.FeedbackCurVal);
|
|
|
|
USBD_IsocTxAsync( p_as_if->DevNbr, /* Start new xfer with new feedback val. */
|
|
p_as_if_alt->SynchIsocAddr,
|
|
(void *)p_feedback_buf,
|
|
feedback_len,
|
|
USBD_Audio_PlaybackIsocSynchCmpl,
|
|
(void *)p_as_if,
|
|
p_err);
|
|
if (*p_err != USBD_ERR_NONE) {
|
|
USBD_Audio_PlaybackSynchBufFree(p_as_if, p_feedback_buf);
|
|
USBD_DBG_AUDIO_PROC_ERR("ProcPlaybackCorrExec(): isoc tx xfer not started w/ err = %d\r\n", *p_err);
|
|
return;
|
|
}
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrIsocTxSubmitted);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_SynchBufGet()
|
|
*
|
|
* Description : Get a buffer for the synch endpoint to use.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio stream interface.
|
|
*
|
|
* Return(s) : Pointer to a synch buffer, if NO error(s).
|
|
*
|
|
* Null pointer, otherwise.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
|
|
static CPU_INT32U *USBD_Audio_PlaybackSynchBufGet (USBD_AUDIO_AS_IF *p_as_if)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
CPU_INT32U *p_buf;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
if (p_as_if_settings->PlaybackSynch.SynchBufFree == DEF_NO) {
|
|
CPU_CRITICAL_EXIT();
|
|
return (DEF_NULL);
|
|
}
|
|
|
|
p_as_if_settings->PlaybackSynch.SynchBufFree = DEF_NO;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
p_buf = p_as_if_settings->PlaybackSynch.SynchBufPtr;
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrBufGet);
|
|
|
|
return (p_buf);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_SynchBufFree()
|
|
*
|
|
* Description : Return a synch buffer to the free list.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio stream interface.
|
|
*
|
|
* p_buf Pointer to buffer to free.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
|
|
static void USBD_Audio_PlaybackSynchBufFree (USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT32U *p_buf)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
(void)p_buf;
|
|
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
p_as_if_settings->PlaybackSynch.SynchBufFree = DEF_YES;
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_Playback_SynchNbrBufFree);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_Terminal_CopyProtManage()
|
|
*
|
|
* Description : Process Copy Protect Control of a particular Terminal (see note #1).
|
|
*
|
|
* Argument(s) : terminal_id Terminal id number.
|
|
*
|
|
* recipient Terminal Type.
|
|
*
|
|
* p_recipient_info Pointer to terminal info structure casted into (void *).
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to copy protection level attribute.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type or invalid copy
|
|
* protection level attribute.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_RECIPIENT Invalid terminal recipient.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) The Copy Protect Control is used to manipulate the Copy Protection Level (CPL)
|
|
* associated with a particular Terminal. Not all Terminals are required to support
|
|
* this Control. Only the Terminals that represent a connection to the audio function
|
|
* where audio streams enter or leave the audio function in a form that enables lossless
|
|
* duplication should consider Copy Protect Control.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.1.3.1 for more details about Copy Protect Control.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_Audio_Terminal_CopyProtManage ( CPU_INT08U terminal_id,
|
|
USBD_AUDIO_ENTITY_TYPE recipient,
|
|
const void *p_recipient_info,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_IT *p_it;
|
|
USBD_AUDIO_OT *p_ot;
|
|
CPU_INT08U copy_prot_level;
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (recipient) {
|
|
case USBD_AUDIO_ENTITY_IT:
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
p_it = (USBD_AUDIO_IT *)p_recipient_info;
|
|
|
|
if (terminal_id != p_it->ID) { /* Check if ID is known. */
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
if (p_it->IT_CfgPtr->CopyProtEn == DEF_ENABLED) {
|
|
*p_buf = p_it->IT_CfgPtr->CopyProtLevel;
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case USBD_AUDIO_ENTITY_OT:
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
p_ot = (USBD_AUDIO_OT *)p_recipient_info;
|
|
|
|
if (terminal_id != p_ot->ID) { /* Check if ID is known. */
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
if ((p_ot->OT_CfgPtr->CopyProtEn == DEF_ENABLED) &&
|
|
(p_ot->OT_API_Ptr->OT_CopyProtSet != DEF_NULL)) {
|
|
|
|
copy_prot_level = *p_buf;
|
|
|
|
valid = p_ot->OT_API_Ptr->OT_CopyProtSet(p_ot->DrvInfoPtr,
|
|
terminal_id,
|
|
copy_prot_level);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_RECIPIENT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_MuteManage()
|
|
*
|
|
* Description : Mute or unmute one logical or several channels inside a cluster (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to mute or unmute.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer containing the mute state.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) A Mute Control can have only the current setting attribute (CUR). The position of a
|
|
* Mute Control CUR attribute can be either TRUE or FALSE.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.1 for more details about Mute Control.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_Audio_FU_MuteManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN mute;
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_MuteManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_MuteManage(p_fu->DrvInfoPtr,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
DEF_FALSE,
|
|
&mute);
|
|
if (valid == DEF_OK) {
|
|
p_buf[0u] = (mute == DEF_OFF) ? 0u : 1u;
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_MuteManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
mute = (p_buf[0u] == 0u) ? DEF_OFF : DEF_ON;
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_MuteManage(p_fu->DrvInfoPtr,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
DEF_TRUE,
|
|
&mute);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Mute Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_VolManage()
|
|
*
|
|
* Description : Handle volume control for one or several logical channels inside a cluster (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical channel Number to apply volume control.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer containing the volume.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) A Volume Control can support all possible Control attributes (CUR, MIN, MAX, and
|
|
* RES). The settings for the CUR, MIN, and MAX attributes can range from +127.9961 dB
|
|
* (0x7FFF) down to -127.9961 dB (0x8001) in steps of 1/256 dB or 0.00390625 dB
|
|
* (0x0001). The range for the CUR attribute is extended by code 0x8000, representing
|
|
* silence, i.e., -infinit dB. The settings for the RES attribute can only take positive
|
|
* values and range from 1/256 dB (0x0001) to +127.9961 dB (0x7FFF).
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.2 for more details about Volume Control.
|
|
*
|
|
* (2) The audio 1.0 indicates: "The MIN, MAX, and RES attributes are usually not supported
|
|
* for the Set request" (section 5.2.2.4.1). Thus SET_MIN/MAX/RES are rejected.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
static void USBD_Audio_FU_VolManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_VolManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_VolManage( p_fu->DrvInfoPtr,
|
|
b_req,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
(CPU_INT16U *)p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN: /* See Note #2. */
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Vol Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_BassManage()
|
|
*
|
|
* Description : Handle bass control for one or several logical channels inside a cluster (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to the Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to apply bass control.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer containing the bass attribute.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) The Bass Control influences the general Bass behavior of the Feature Unit. A Bass
|
|
* Control can support all possible Control attributes (CUR, MIN, MAX, and RES). The
|
|
* settings for the CUR, MIN, and MAX attributes can range from +31.75 dB (0x7F) down
|
|
* to 32.00 dB (0x80) in steps of 0.25 dB (0x01). The settings for the RES attribute
|
|
* can only take positive values and range from 0.25 dB (0x01) to +31.75 dB (0x7F).
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.3 for more details about Bass Control.
|
|
*
|
|
* (2) The audio 1.0 indicates: "The MIN, MAX, and RES attributes are usually not supported
|
|
* for the Set request" (section 5.2.2.4.1). Thus SET_MIN/MAX/RES are rejected.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_BassManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_BassManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_BassManage(p_fu->DrvInfoPtr,
|
|
b_req,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN: /* See Note #2. */
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Bass Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_MidManage()
|
|
*
|
|
* Description : Handle middle control for one or several logical channels inside a cluster (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to the Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to apply mid control.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer containing the mid band attribute.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) The Mid Control influences the general Mid behavior of the Feature Unit. A Mid
|
|
* Control can support all possible Control attributes (CUR, MIN, MAX, and RES). The
|
|
* settings for the CUR, MIN, and MAX attributes can range from +31.75 dB (0x7F) down
|
|
* to 32.00 dB (0x80) in steps of 0.25 dB (0x01). The settings for the RES attribute
|
|
* can only take positive values and range from 0.25 dB (0x01) to +31.75 dB (0x7F).
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.4 for more details about Mid Control.
|
|
*
|
|
* (2) The audio 1.0 indicates: "The MIN, MAX, and RES attributes are usually not supported
|
|
* for the Set request" (section 5.2.2.4.1). Thus SET_MIN/MAX/RES are rejected.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_MidManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_MidManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_MidManage(p_fu->DrvInfoPtr,
|
|
b_req,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN: /* See Note #2. */
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Mid Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_TrebleManage()
|
|
*
|
|
* Description : Handle treble control for one or several logical channels inside a cluster (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to the Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to apply treble control.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer containing the treble attribute.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) The Treble Control influences the general Treble behavior of the Feature Unit. A
|
|
* Treble Control can support all possible Control attributes (CUR, MIN, MAX, and RES).
|
|
* The settings for the CUR, MIN, and MAX attributes can range from +31.75 dB (0x7F)
|
|
* down to 32.00 dB (0x80) in steps of 0.25 dB (0x01). The settings for the RES
|
|
* attribute can only take positive values and range from 0.25 dB (0x01) to +31.75 dB
|
|
* (0x7F).
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.5 for more details about Treble Control.
|
|
*
|
|
* (2) The audio 1.0 indicates: "The MIN, MAX, and RES attributes are usually not supported
|
|
* for the Set request" (section 5.2.2.4.1). Thus SET_MIN/MAX/RES are rejected.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_TrebleManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_TrebleManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_TrebleManage(p_fu->DrvInfoPtr,
|
|
b_req,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN: /* See Note #2. */
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Treble Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_GraphicEqualizerManage()
|
|
*
|
|
* Description : Handle graphic analyzer control for one or several logical channels inside a cluster
|
|
* (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to the Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to apply graphic analyzer control.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer containing the graphic equalizer attribute.
|
|
*
|
|
* req_len Length of the buffer in octets.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) The Audio Device Class definition provides for standard support of a third octave
|
|
* graphic equalizer. The bands are defined according to the ANSI S1.11-1986 standard.
|
|
* Bands are numbered from 14 (center frequency of 25 Hz) up to 43 (center frequency of
|
|
* 20,000 Hz), making a total of 30 possible bands.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.6 for more details about Graphic Equalizer Control.
|
|
*
|
|
* (2) 'req_len' is the value of wLength from the SETUP packet. It is equal to
|
|
* 4 + (number of bits set in bmBandsPresent : NrBits).
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* Table 5-27 for more details.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_GraphicEqualizerManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
CPU_INT08U nbr_bands_present;
|
|
CPU_INT32U bm_bands_present;
|
|
CPU_INT08U nbr_bits_set;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
if (p_fu->FU_API_Ptr->FU_GraphicEqualizerManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
bm_bands_present = 0u;
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_GraphicEqualizerManage(p_fu->DrvInfoPtr,
|
|
b_req,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
0u,
|
|
&bm_bands_present,
|
|
(p_buf + 4u)); /* bBand fields start after bmBandsPresent.*/
|
|
if (valid == DEF_OK) {
|
|
MEM_VAL_SET_INT32U_LITTLE(&p_buf[0u], bm_bands_present);
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_GraphicEqualizerManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
nbr_bands_present = (CPU_INT08U)req_len - 4u; /* See Note #2. */
|
|
if (nbr_bands_present < 1u) { /* At least one frequency band must be specified. */
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB;
|
|
return;
|
|
}
|
|
|
|
bm_bands_present = (*(CPU_INT32U *)p_buf); /* Get bitmap of bands effectively implemented. */
|
|
|
|
/* Verify if nbr of bits set in 'bmBandsPresent'... */
|
|
/* ...matches nbr of param specified in Param Block. */
|
|
USBD_AUDIO_BIT_SET_CNT(nbr_bits_set, bm_bands_present);
|
|
if (nbr_bits_set != nbr_bands_present) {
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB;
|
|
return;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_GraphicEqualizerManage(p_fu->DrvInfoPtr,
|
|
b_req,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
nbr_bands_present,
|
|
&bm_bands_present,
|
|
(p_buf + 4u));
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Graphic Equalizer Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_AutoGainManage()
|
|
*
|
|
* Description : Enable or disable auto gain for one or several logical channels inside a cluster
|
|
* (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to the Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to enable or disable auto gain.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer containing the autogain state.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) An Automatic Gain Control can have only the current setting attribute (CUR). The
|
|
* position of an Automatic Gain Control CUR attribute can be either TRUE or FALSE.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.7 for more details about Automatic Gain Control.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_AutoGainManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN auto_gain;
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_AutoGainManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_AutoGainManage(p_fu->DrvInfoPtr,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
DEF_FALSE,
|
|
&auto_gain);
|
|
if (valid == DEF_OK) {
|
|
p_buf[0u] = (auto_gain == DEF_OFF) ? 0u : 1u;
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_AutoGainManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
auto_gain = (p_buf[0u] == 0u) ? DEF_OFF : DEF_ON;
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_AutoGainManage(p_fu->DrvInfoPtr,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
DEF_TRUE,
|
|
&auto_gain);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Auto Gain Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_DlyManage()
|
|
*
|
|
* Description : Handle delay control for one or several logical channels inside a cluster (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to apply delay control.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer that contains the delay.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) A Delay Control can support all possible Control attributes (CUR, MIN, MAX, and RES).
|
|
* The settings for the CUR, MIN, MAX, and RES attributes can range from zero (0x0000)
|
|
* to 1023.9844ms (0xFFFF) in steps of 1/64 ms or 0.015625 ms (0x0001).
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.8 for more details about Delay Control.
|
|
*
|
|
* (2) The audio 1.0 indicates: "The MIN, MAX, and RES attributes are usually not supported
|
|
* for the Set request" (section 5.2.2.4.1). Thus SET_MIN/MAX/RES are rejected.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_DlyManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_DlyManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_DlyManage( p_fu->DrvInfoPtr,
|
|
b_req,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
(CPU_INT16U *)p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN: /* See Note #2. */
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Dly Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_BassBoostManage()
|
|
*
|
|
* Description : Enable or disable bass boost for one or several logical channels inside a cluster
|
|
* (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to enable or disable bass boost.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer that contains the bass boost state.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) A Bass Boost Control can have only the current setting attribute (CUR). The position
|
|
* of a Bass Boost Control CUR attribute can be either TRUE or FALSE.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.9 for more details about Bass Boost Control.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_BassBoostManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN bass_boost;
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_BassBoostManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_BassBoostManage(p_fu->DrvInfoPtr,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
DEF_FALSE,
|
|
&bass_boost);
|
|
if (valid == DEF_OK) {
|
|
p_buf[0u] = (bass_boost == DEF_OFF) ? 0u : 1u;
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_BassBoostManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
bass_boost = (p_buf[0u] == 0) ? DEF_OFF : DEF_ON;
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_BassBoostManage(p_fu->DrvInfoPtr,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
DEF_TRUE,
|
|
&bass_boost);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Auto Gain Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_FU_LoudnessManage()
|
|
*
|
|
* Description : Enable or disable loudness for one or several logical channels inside a cluster
|
|
* (see note #1).
|
|
*
|
|
* Argument(s) : p_fu Pointer to Feature Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_ch_nbr Logical Channel Number to enable or disable bass boost.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer that contains the loudness state.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) A Loudness Control can have only the current setting attribute (CUR). The position
|
|
* of a Loudness Control CUR attribute can be either TRUE or FALSE.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.4.3.10 for more details about Loudness Control.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_FU_MAX_CTRL == DEF_ENABLED)
|
|
static void USBD_Audio_FU_LoudnessManage (const USBD_AUDIO_FU *p_fu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN loudness;
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_LoudnessManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_LoudnessManage(p_fu->DrvInfoPtr,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
DEF_FALSE,
|
|
&loudness);
|
|
if (valid == DEF_OK) {
|
|
p_buf[0u] = (loudness == DEF_OFF) ? 0u : 1u;
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_fu->FU_API_Ptr->FU_LoudnessManage == DEF_NULL) {
|
|
break;
|
|
}
|
|
|
|
loudness = (p_buf[0u] == 0u) ? DEF_OFF : DEF_ON;
|
|
|
|
valid = p_fu->FU_API_Ptr->FU_LoudnessManage(p_fu->DrvInfoPtr,
|
|
unit_id,
|
|
log_ch_nbr,
|
|
DEF_TRUE,
|
|
&loudness);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Auto Gain Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_MU_CtrlManage()
|
|
*
|
|
* Description : Process Mixer Unit Control.
|
|
*
|
|
* Argument(s) : p_mu Pointer to Mixer Unit structure.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* log_in_ch_nbr Logical channel number input.
|
|
*
|
|
* log_out_ch_nbr Logical channel number output.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer that contains the mixer control attribute.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) The Set Mixer Unit Control request can have 2 forms and the Get Mixer Unit Control
|
|
* request can have 3 forms. However the 2nd and 3nd are optional and are not
|
|
* supported by this implementation.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.2 for more details about Mixer Unit Control Requests.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
|
|
static void USBD_Audio_MU_CtrlManage (const USBD_AUDIO_MU *p_mu,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U log_in_ch_nbr,
|
|
CPU_INT08U log_out_ch_nbr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if ((p_mu->MU_API_Ptr == DEF_NULL) ||
|
|
(p_mu->MU_API_Ptr->MU_CtrlManage == DEF_NULL)) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_mu->MU_API_Ptr->MU_CtrlManage( p_mu->DrvInfoPtr,
|
|
b_req,
|
|
unit_id,
|
|
log_in_ch_nbr,
|
|
log_out_ch_nbr,
|
|
(CPU_INT16U *)p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Mixer Unit Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_SU_CtrlManage()
|
|
*
|
|
* Description : Process Selector Unit Control.
|
|
*
|
|
* Argument(s) : p_su Pointer to Selector Unit structure.
|
|
*
|
|
* p_su Pointer to selector unit info.
|
|
*
|
|
* unit_id Feature Unit ID number.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer that contains the input pin number.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) Set request does not support the MIN, MAX, and RES. Also the Get request does not
|
|
* support RES.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.2.3 for more details about Mixer Unit Control Requests.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
|
|
static void USBD_Audio_SU_CtrlManage (const USBD_AUDIO_SU *p_su,
|
|
CPU_INT08U unit_id,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
CPU_BOOLEAN valid;
|
|
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
p_buf[0u] = 1u; /* Min is always 1 input pin. */
|
|
*p_err = USBD_ERR_NONE;
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
p_buf[0u] = p_su->SU_CfgPtr->NbrInPins;
|
|
*p_err = USBD_ERR_NONE;
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
if (p_su->SU_API_Ptr->SU_InPinManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_su->SU_API_Ptr->SU_InPinManage(p_su->DrvInfoPtr,
|
|
unit_id,
|
|
DEF_FALSE,
|
|
p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_su->SU_API_Ptr->SU_InPinManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_su->SU_API_Ptr->SU_InPinManage(p_su->DrvInfoPtr,
|
|
unit_id,
|
|
DEF_TRUE,
|
|
p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Auto Gain Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_SamplingFreqManage()
|
|
*
|
|
* Description : Set/Get initial sampling frequency for an isochronous audio data endpoint.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to AudioStreaming interface.
|
|
*
|
|
* p_as_cfg Pointer to the AudioStreaming configuration
|
|
*
|
|
* ep_addr Endpoint address.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer that contains the sampling frequency.
|
|
*
|
|
* req_len Request control attribute length (in bytes).
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) If the sampling frequency is continuous, the resolution in 1 Hz.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.3.2.3.1 for more details.
|
|
*
|
|
* (2) If the endpoint supports a discrete number of sampling frequencies, a fixed
|
|
* resolution is not available between the different discrete sampling frequencies.
|
|
*
|
|
* (3) According to Audio 1.0 specification (section 5.2.3.2.3.1), the sampling frequency
|
|
* value holds on 3 bytes. Since the sampling frequency is referenced as a 32-bit word,
|
|
* clearing the most significant byte ensures that the correct value is read from
|
|
* memory.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_AS_SamplingFreqManage (const USBD_AUDIO_AS_IF *p_as_if,
|
|
const USBD_AUDIO_AS_ALT_CFG *p_as_cfg,
|
|
CPU_INT16U ep_addr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
CPU_INT16U req_len,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
CPU_BOOLEAN valid;
|
|
CPU_INT32U sampling_freq = 0u;
|
|
CPU_BOOLEAN freq_match;
|
|
CPU_INT08U ix;
|
|
CPU_INT32U *p_sampling_freq;
|
|
|
|
|
|
(void)ep_addr;
|
|
(void)req_len;
|
|
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
if (p_as_if_settings->AS_API_Ptr->AS_SamplingFreqManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_as_if_settings->AS_API_Ptr->AS_SamplingFreqManage( p_as_if_settings->DrvInfoPtr,
|
|
p_as_if_settings->TerminalID,
|
|
DEF_FALSE,
|
|
(CPU_INT32U *)p_buf);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
if (p_as_cfg->NbrSamplingFreq == 0u) { /* Continuous sampling freq. */
|
|
MEM_VAL_SET_INT32U_LITTLE(p_buf, p_as_cfg->LowerSamplingFreq);
|
|
} else { /* Discrete sampling frequencies. */
|
|
/* Find the minimum frequency. */
|
|
p_sampling_freq = &p_as_cfg->SamplingFreqTblPtr[0u];
|
|
for (ix = 1u; ix < p_as_cfg->NbrSamplingFreq; ix++) {
|
|
if (*p_sampling_freq > p_as_cfg->SamplingFreqTblPtr[ix]) {
|
|
p_sampling_freq = &p_as_cfg->SamplingFreqTblPtr[ix];
|
|
}
|
|
}
|
|
|
|
MEM_VAL_SET_INT32U_LITTLE(p_buf, *p_sampling_freq);
|
|
}
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
if (p_as_cfg->NbrSamplingFreq == 0u) { /* Continuous sampling freq. */
|
|
MEM_VAL_SET_INT32U_LITTLE(p_buf, p_as_cfg->UpperSamplingFreq);
|
|
} else { /* Discrete sampling frequencies. */
|
|
/* Find the maximum frequency. */
|
|
p_sampling_freq = &p_as_cfg->SamplingFreqTblPtr[0u];
|
|
for (ix = 1u; ix < p_as_cfg->NbrSamplingFreq; ix++) {
|
|
if (*p_sampling_freq < p_as_cfg->SamplingFreqTblPtr[ix]) {
|
|
p_sampling_freq = &p_as_cfg->SamplingFreqTblPtr[ix];
|
|
}
|
|
}
|
|
|
|
MEM_VAL_SET_INT32U_LITTLE(p_buf, *p_sampling_freq);
|
|
}
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
if (p_as_cfg->NbrSamplingFreq == 0u) { /* Continuous sampling freq. */
|
|
p_buf[0u] = 1u; /* See Note #1. */
|
|
|
|
*p_err = USBD_ERR_NONE;
|
|
} else { /* Discrete sampling frequencies. */
|
|
*p_err = USBD_ERR_AUDIO_REQ; /* See Note #2. */
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_as_if_settings->AS_API_Ptr->AS_SamplingFreqManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
sampling_freq = MEM_VAL_GET_INT32U_LITTLE(p_buf);
|
|
sampling_freq &= 0x00FFFFFFu; /* Ensure correct sampling freq is read (see Note #3). */
|
|
|
|
/* Check if sampling freq is supported. */
|
|
if (p_as_cfg->NbrSamplingFreq == 0u) { /* Continuous sampling freq. */
|
|
|
|
if ((sampling_freq < p_as_cfg->LowerSamplingFreq) ||
|
|
(sampling_freq > p_as_cfg->UpperSamplingFreq)) {
|
|
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
} else { /* Discrete sampling frequencies. */
|
|
freq_match = DEF_FALSE;
|
|
for (ix = 0u; ix < p_as_cfg->NbrSamplingFreq; ix++) {
|
|
|
|
if (sampling_freq == p_as_cfg->SamplingFreqTblPtr[ix]) {
|
|
freq_match = DEF_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (freq_match == DEF_FALSE) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
}
|
|
|
|
valid = p_as_if_settings->AS_API_Ptr->AS_SamplingFreqManage(p_as_if_settings->DrvInfoPtr,
|
|
p_as_if_settings->TerminalID,
|
|
DEF_TRUE,
|
|
&sampling_freq);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Sampling Freq Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_PitchManage()
|
|
*
|
|
* Description : Enable or disable ability of an adaptive endpoint to dynamically track its sampling
|
|
* frequency.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to AudioStreaming interface.
|
|
*
|
|
* ep_addr Endpoint address.
|
|
*
|
|
* b_req Received request type (SET_XXX or GET_XXX).
|
|
*
|
|
* p_buf Pointer to the buffer that contains the pitch state.
|
|
*
|
|
* p_err Pointer to the variable that will receive the return error code from this function:
|
|
*
|
|
* USBD_ERR_NONE Control successfully processed.
|
|
* USBD_ERR_AUDIO_REQ_INVALID_ATTRIB Invalid request type.
|
|
* USBD_ERR_AUDIO_REQ_FAIL Audio driver failed to process the request.
|
|
*
|
|
* Return(s) : None.
|
|
*
|
|
* Note(s) : (1) Pitch Control enables or disables the ability of an adaptive endpoint to
|
|
* dynamically track its sampling frequency. The Control is necessary because the clock
|
|
* recovery circuitry must be informed whether it should allow for relatively large
|
|
* swings in the sampling frequency. A Pitch Control can have only the current setting
|
|
* attribute (CUR). The position of a Pitch Control CUR attribute can be either TRUE or
|
|
* FALSE.
|
|
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
|
|
* section 5.2.3.2.3.2 for more details on Pitch Control.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_AS_PitchManage (const USBD_AUDIO_AS_IF *p_as_if,
|
|
CPU_INT16U ep_addr,
|
|
CPU_INT08U b_req,
|
|
CPU_INT08U *p_buf,
|
|
USBD_ERR *p_err)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
|
|
CPU_BOOLEAN valid;
|
|
CPU_BOOLEAN pitch;
|
|
|
|
|
|
(void)ep_addr;
|
|
|
|
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
|
|
switch (b_req) {
|
|
case USBD_AUDIO_REQ_GET_CUR:
|
|
if (p_as_if_settings->AS_API_Ptr->AS_PitchManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
valid = p_as_if_settings->AS_API_Ptr->AS_PitchManage(p_as_if_settings->DrvInfoPtr,
|
|
p_as_if_settings->TerminalID,
|
|
DEF_FALSE,
|
|
&pitch);
|
|
if (valid == DEF_OK) {
|
|
*p_buf = (pitch != DEF_OFF) ? 1u : 0u;
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_SET_CUR:
|
|
if (p_as_if_settings->AS_API_Ptr->AS_PitchManage == DEF_NULL) {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
break;
|
|
}
|
|
|
|
pitch = (*p_buf == 0u) ? DEF_OFF : DEF_ON;
|
|
|
|
valid = p_as_if_settings->AS_API_Ptr->AS_PitchManage(p_as_if_settings->DrvInfoPtr,
|
|
p_as_if_settings->TerminalID,
|
|
DEF_TRUE,
|
|
&pitch);
|
|
if (valid == DEF_OK) {
|
|
*p_err = USBD_ERR_NONE;
|
|
} else {
|
|
*p_err = USBD_ERR_AUDIO_REQ;
|
|
}
|
|
break;
|
|
|
|
|
|
case USBD_AUDIO_REQ_GET_MIN:
|
|
case USBD_AUDIO_REQ_GET_MAX:
|
|
case USBD_AUDIO_REQ_GET_RES:
|
|
case USBD_AUDIO_REQ_SET_MIN:
|
|
case USBD_AUDIO_REQ_SET_MAX:
|
|
case USBD_AUDIO_REQ_SET_RES:
|
|
default:
|
|
*p_err = USBD_ERR_AUDIO_REQ_INVALID_ATTRIB; /* Stall req not supported by Pitch Ctrl. */
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_RingBufQInit()
|
|
*
|
|
* Description : Initialize the record ring buffer queue.
|
|
*
|
|
* Argument(s) : p_as_if Pointer to the audio streaming interface.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : (1) The ring buffer queue contains buffer descriptors. Each buffer descriptor stores
|
|
* information about a buffer (pointer to buffer and buffer size). Buffers are taken
|
|
* from a memory segment allocated at the stream initialization. The memory segment
|
|
* is split into several buffers whose alignment takes into account any DMA and/or cache
|
|
* alignment requirement specified by the user via USBD_AUDIO_CFG_BUF_ALIGN_OCTETS.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_AS_IF_RingBufQInit (USBD_AUDIO_AS_IF *p_as_if)
|
|
{
|
|
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
|
|
USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q = &p_as_if_settings->StreamRingBufQ;
|
|
void *p_buf_new;
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
CPU_INT16U mem_blk_len_worst = MATH_ROUND_INC_UP(p_as_if_settings->BufTotalLen, USBD_AUDIO_CFG_BUF_ALIGN_OCTETS);
|
|
CPU_INT08U ix;
|
|
|
|
|
|
for (ix = 0u; ix < p_as_if_settings->BufTotalNbr; ix++) {
|
|
/* Alloc a buf (see Note #1). */
|
|
p_buf_new = (void *)(p_as_if_settings->BufMemPtr + (ix * mem_blk_len_worst));
|
|
|
|
/* Init buf desc. */
|
|
p_buf_desc = &p_ring_buf_q->BufDescTblPtr[ix];
|
|
if (p_as_if_settings->StreamDir == USBD_AUDIO_STREAM_IN) {
|
|
#if (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
p_buf_desc->BufLen = USBD_Audio_RecordDataRateAdj(p_as_if);
|
|
#endif
|
|
} else {
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
|
|
p_buf_desc->BufLen = 0u;
|
|
#endif
|
|
}
|
|
p_buf_desc->BufPtr = p_buf_new;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_RingBufQGet()
|
|
*
|
|
* Description : Get a buffer descriptor at the specified index.
|
|
*
|
|
* Argument(s) : p_ring_buf_q Pointer to ring buffer queue.
|
|
*
|
|
* ix Index.
|
|
*
|
|
* Return(s) : Pointer to buffer descriptor.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static USBD_AUDIO_BUF_DESC *USBD_Audio_AS_IF_RingBufQGet (USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q,
|
|
CPU_INT16U ix)
|
|
{
|
|
USBD_AUDIO_BUF_DESC *p_buf_desc;
|
|
|
|
|
|
p_buf_desc = &p_ring_buf_q->BufDescTblPtr[ix];
|
|
|
|
return (p_buf_desc);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_RingBufQProducerStartIxGet()
|
|
*
|
|
* Description : Get the current Producer Start index.
|
|
*
|
|
* Argument(s) : p_as_if_settings Pointer to AudioStreaming interface settings.
|
|
*
|
|
* Return(s) : Current Producer Start index.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static CPU_INT16U USBD_Audio_AS_IF_RingBufQProducerStartIxGet (USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings)
|
|
{
|
|
USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q = &p_as_if_settings->StreamRingBufQ;
|
|
CPU_INT16U nxt_ix;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
nxt_ix = p_ring_buf_q->ProducerStartIx + 1;
|
|
if (nxt_ix == p_as_if_settings->BufTotalNbr) {
|
|
nxt_ix = 0u;
|
|
}
|
|
|
|
if ((nxt_ix == p_ring_buf_q->ConsumerEndIx) ||
|
|
(nxt_ix == p_ring_buf_q->ProducerEndIx)) {
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrProducerStartIxCatchUp);
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX);
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrBufDescInUse);
|
|
|
|
return (p_ring_buf_q->ProducerStartIx);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_RingBufQProducerEndIxGet()
|
|
*
|
|
* Description : Get the current Producer End index.
|
|
*
|
|
* Argument(s) : p_as_if_settings Pointer to AudioStreaming interface settings.
|
|
*
|
|
* Return(s) : Current Producer End index.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static CPU_INT16U USBD_Audio_AS_IF_RingBufQProducerEndIxGet (USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings)
|
|
{
|
|
USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q = &p_as_if_settings->StreamRingBufQ;
|
|
CPU_INT16U cur_ix;
|
|
CPU_INT16U nxt_ix;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
cur_ix = p_ring_buf_q->ProducerEndIx;
|
|
nxt_ix = p_ring_buf_q->ProducerEndIx + 1;
|
|
if (nxt_ix == p_as_if_settings->BufTotalNbr) {
|
|
nxt_ix = 0u;
|
|
}
|
|
|
|
if ((cur_ix == p_ring_buf_q->ProducerStartIx) ||
|
|
(nxt_ix == p_ring_buf_q->ConsumerStartIx)) {
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrProducerEndIxCatchUp);
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX);
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
return (cur_ix);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_RingBufQConsumerStartIxGet()
|
|
*
|
|
* Description : Get the current Consumer Start index.
|
|
*
|
|
* Argument(s) : p_as_if_settings Pointer to AudioStreaming interface settings.
|
|
*
|
|
* Return(s) : Current Consumer Start index.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static CPU_INT16U USBD_Audio_AS_IF_RingBufQConsumerStartIxGet (USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings)
|
|
{
|
|
USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q = &p_as_if_settings->StreamRingBufQ;
|
|
CPU_INT16U cur_ix;
|
|
CPU_INT16U nxt_ix;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
cur_ix = p_ring_buf_q->ConsumerStartIx;
|
|
nxt_ix = p_ring_buf_q->ConsumerStartIx + 1;
|
|
if (nxt_ix == p_as_if_settings->BufTotalNbr) {
|
|
nxt_ix = 0u;
|
|
}
|
|
|
|
if ((cur_ix == p_ring_buf_q->ProducerEndIx) ||
|
|
(nxt_ix == p_ring_buf_q->ConsumerEndIx)) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrConsumerStartIxCatchUp);
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX);
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
return (cur_ix);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_RingBufQConsumerEndIxGet()
|
|
*
|
|
* Description : Get the current Consumer End index.
|
|
*
|
|
* Argument(s) : p_as_if_settings Pointer to AudioStreaming interface settings.
|
|
*
|
|
* Return(s) : Current Consumer End index.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static CPU_INT16U USBD_Audio_AS_IF_RingBufQConsumerEndIxGet (USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings)
|
|
{
|
|
USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q = &p_as_if_settings->StreamRingBufQ;
|
|
CPU_INT16U cur_ix;
|
|
CPU_INT16U nxt_ix;
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
cur_ix = p_ring_buf_q->ConsumerEndIx;
|
|
nxt_ix = p_ring_buf_q->ConsumerEndIx + 1;
|
|
if (nxt_ix == p_as_if_settings->BufTotalNbr) {
|
|
nxt_ix = 0u;
|
|
}
|
|
|
|
if ((cur_ix == p_ring_buf_q->ConsumerStartIx) ||
|
|
(nxt_ix == p_ring_buf_q->ProducerStartIx)) {
|
|
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrConsumerEndIxCatchUp);
|
|
CPU_CRITICAL_EXIT();
|
|
return (USBD_AUDIO_AS_IF_RING_BUF_Q_INVALID_IX);
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
|
|
USBD_AUDIO_STAT_DEC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrBufDescInUse);
|
|
|
|
return (cur_ix);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_RingBufQIxUpdate()
|
|
*
|
|
* Description : Update index.
|
|
*
|
|
* Argument(s) : p_as_if_settings Pointer to AudioStreaming interface settings.
|
|
*
|
|
* p_ix Pointer to index to increment.
|
|
*
|
|
* Return(s) : none.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static void USBD_Audio_AS_IF_RingBufQIxUpdate (USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings,
|
|
CPU_INT16U *p_ix)
|
|
{
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q = &p_as_if_settings->StreamRingBufQ;
|
|
#endif
|
|
CPU_SR_ALLOC();
|
|
|
|
|
|
CPU_CRITICAL_ENTER();
|
|
(*p_ix)++; /* Nxt avail ix. */
|
|
if (*p_ix == p_as_if_settings->BufTotalNbr) { /* Reset ix if end of ring q reached. */
|
|
*p_ix = 0u;
|
|
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
|
|
if (p_ix == &p_ring_buf_q->ProducerStartIx) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrProducerStartIxWrapAround);
|
|
} else if (p_ix == &p_ring_buf_q->ProducerEndIx) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrProducerEndIxWrapAround);
|
|
} else if (p_ix == &p_ring_buf_q->ConsumerStartIx) {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrConsumerStartIxWrapAround);
|
|
} else {
|
|
USBD_AUDIO_STAT_INC(p_as_if_settings->StatPtr->AudioProc_RingBufQ_NbrConsumerEndIxWrapAround);
|
|
}
|
|
#endif
|
|
}
|
|
CPU_CRITICAL_EXIT();
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_BufDiffGet()
|
|
*
|
|
* Description : Compute current difference of buffers between Producer End index and Consumer End index.
|
|
*
|
|
* Argument(s) : p_as_if_settings Pointer to AudioStreaming interface settings.
|
|
*
|
|
* Return(s) : Difference of buffers.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if ( (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) && \
|
|
((USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED))) || \
|
|
( (USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED) && \
|
|
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED) )
|
|
static CPU_INT08S USBD_Audio_BufDiffGet (USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings)
|
|
{
|
|
USBD_AUDIO_AS_IF_RING_BUF_Q *p_ring_buf_q = &p_as_if_settings->StreamRingBufQ;
|
|
CPU_INT08S buf_diff;
|
|
CPU_INT16U circular_distance;
|
|
|
|
if (p_ring_buf_q->ProducerEndIx >= p_ring_buf_q->ConsumerEndIx) {
|
|
circular_distance = p_ring_buf_q->ProducerEndIx - p_ring_buf_q->ConsumerEndIx;
|
|
} else {
|
|
circular_distance = (p_as_if_settings->BufTotalNbr + 1 + p_ring_buf_q->ProducerEndIx) - p_ring_buf_q->ConsumerEndIx;
|
|
}
|
|
|
|
buf_diff = (CPU_INT08S)(circular_distance - p_as_if_settings->StreamPreBufMax);
|
|
|
|
return (buf_diff);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
*********************************************************************************************************
|
|
* USBD_Audio_AS_IF_Get()
|
|
*
|
|
* Description : Retrieve an AudioStreaming interface structure.
|
|
*
|
|
* Argument(s) : as_handle AudioStreaming interface handle.
|
|
*
|
|
* Return(s) : Pointer to AudioStreaming interface structure.
|
|
*
|
|
* Note(s) : none.
|
|
*********************************************************************************************************
|
|
*/
|
|
|
|
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
|
|
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
|
|
static USBD_AUDIO_AS_IF *USBD_Audio_AS_IF_Get (USBD_AUDIO_AS_HANDLE as_handle)
|
|
{
|
|
USBD_AUDIO_AS_IF *p_as_if;
|
|
CPU_INT08U as_if_ix;
|
|
|
|
|
|
as_if_ix = USBD_AUDIO_AS_IF_HANDLE_IX_GET(as_handle);
|
|
|
|
p_as_if = &USBD_Audio_AS_Tbl[as_if_ix];
|
|
|
|
return (p_as_if);
|
|
}
|
|
#endif
|
|
|