Files
test/middleware/usb/uC-USBD/Class/Audio/usbd_audio.c
2025-11-07 20:19:23 +08:00

4127 lines
186 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.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* NOTICE
*
* Semidrive modified this file to adapt it for ssdk platform.
* The modifications are only intended for use with Semidrive chips.
* Copyright of all the modifications belongs to Semidrive Semiconductor.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* USB DEVICE AUDIO CLASS
*
* Filename : usbd_audio.c
* Version : V4.06.01
*********************************************************************************************************
* Note(s) : (1) This Audio class complies with the following specifications:
*
* (a) 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998'
* (b) 'USB Device Class Definition for Terminal Types, Release 1.0, March 18, 1998'
* (c) 'USB Device Class Definition for Audio Data Formats, Release 1.0, March 18,
* 1998'
* (d) 'USB Audio Device Class Specification for Basic Audio Devices, Release 1.0,
* November 24, 2009'
*
* (2) This Audio class does NOT support:
*
* (a) Processor Unit
* (b) Extension Unit
* (c) Data format Type II
* (d) Data format Type III
* (e) Associated interfaces
* (f) Class-specific requests: GET_STAT, GET_MEM, SET_MEM
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#define USBD_AUDIO_MODULE
#include "usbd_audio.h"
#include "usbd_audio_os.h"
#include "usbd_audio_processing.h"
#include "usbd_audio_internal.h"
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*********************************************************************************************************
*/
#define USBD_DBG_AUDIO_MSG(msg) USBD_DBG_GENERIC ((msg), \
USBD_EP_ADDR_NONE, \
USBD_IF_NBR_NONE)
#define USBD_DBG_AUDIO_ERR(msg, err) USBD_DBG_GENERIC_ERR((msg), \
USBD_EP_ADDR_NONE, \
USBD_IF_NBR_NONE, \
(err))
#define USBD_AUDIO_CTRL_REQ_TIMEOUT_mS 5000u
#define USBD_AUDIO_REQ_ENTITY_ID_MASK 0xFF00u
#define USBD_AUDIO_REQ_IF_MASK 0x00FFu
#define USBD_AUDIO_TYPE_IT 1u /* Input Terminal Type. */
#define USBD_AUDIO_TYPE_OT 2u /* Output Terminal Type. */
#define USBD_AUDIO_TYPE_FU 3u /* Function Unit Type. */
#define USBD_AUDIO_TYPE_MU 4u /* Mixer Unit Type. */
#define USBD_AUDIO_TYPE_SU 5u /* Selector Unit Type. */
#define USBD_AUDIO_TYPE_PU 6u /* Processor Unit Type. */
/*
*********************************************************************************************************
* AUDIO CLASS DESC
*
* Note(s): (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 3.2 and appendix A.2 for more details about audio subclass.
*
* (2) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* appendix A.4 for more details about Audio class-specific descriptor type.
*
* (3) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.3 for more details about AudioControl interface descriptors length.
*
* (a) See Table 4-2, 'bLength' field description for more details.
*
* (b) See Table 4-3, 'bLength' field description for more details.
*
* (c) See Table 4-4, 'bLength' field description for more details.
*
* (d) See Table 4-5, 'bLength' field description for more details.
*
* (e) See Table 4-6, 'bLength' field description for more details.
*
* (f) See Table 4-7, 'bLength' field description for more details.
*
* (g) See Table 4-8, 'bLength' field description for more details.
*
* (h) See Table 4-19, 'bLength' field description for more details.
*
* (g) See Table 4-21, 'bLength' field description for more details.
*
* (4) See 'USB Device Class Definition for Audio Data Formats, Release 1.0, March 18, 1998',
* Table 2-1, 'bLength' field description for more details.
*********************************************************************************************************
*/
/* Audio IF subclass (see Note #1). */
#define USBD_AUDIO_SUBCLASS_AUDIO_CTRL 0x01u
#define USBD_AUDIO_SUBCLASS_AUDIO_STREAMING 0x02u
#define USBD_AUDIO_SUBCLASS_MIDI_STREAMING 0x03u
/* Audio class-specific desc type (see Note #2). */
#define USBD_AUDIO_DESC_TYPE_CS_IF 0x24u
#define USBD_AUDIO_DESC_TYPE_CS_EP 0x25u
/* class-specific desc len (see Note #3). */
#define USBD_AUDIO_DESC_LEN_AC_HEADER_MIN 8u /* See Note #3a. */
#define USBD_AUDIO_DESC_LEN_AC_IT 12u /* See Note #3b. */
#define USBD_AUDIO_DESC_LEN_AC_OT 9u /* See Note #3c. */
#define USBD_AUDIO_DESC_LEN_AC_MU_MIN 10u /* See Note #3d. */
#define USBD_AUDIO_DESC_LEN_AC_SU_MIN 6u /* See Note #3e. */
#define USBD_AUDIO_DESC_LEN_AC_FU_MIN 7u /* See Note #3f. */
#define USBD_AUDIO_DESC_LEN_AC_PU_MIN 13u /* See Note #3g. */
#define USBD_AUDIO_DESC_LEN_AS_GENERAL 7u /* See Note #3h. */
#define USBD_AUDIO_DESC_LEN_EP_GENERAL 7u /* See Note #3g. */
#define USBD_AUDIO_DESC_LEN_TYPE_I_FMT_MIN 8u /* See Note #4. */
#define USBD_AUDIO_DESC_LEN_TYPE_I_SAM_FREQ 3u
/*
*********************************************************************************************************
* AUDIO CTRL IF
* Note(s): (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* appendix A.5 for more details about AudioControl class-specific interface descriptor
* subtype.
*********************************************************************************************************
*/
/* Desc subtype (see Note #1). */
#define USBD_AUDIO_DESC_SUBTYPE_AC_HEADER 0x01u
#define USBD_AUDIO_DESC_SUBTYPE_IT 0x02u
#define USBD_AUDIO_DESC_SUBTYPE_OT 0x03u
#define USBD_AUDIO_DESC_SUBTYPE_MU 0x04u
#define USBD_AUDIO_DESC_SUBTYPE_SU 0x05u
#define USBD_AUDIO_DESC_SUBTYPE_FU 0x06u
#define USBD_AUDIO_DESC_SUBTYPE_PU 0x07u
#define USBD_AUDIO_DESC_SUBTYPE_XU 0x08u
/*
*********************************************************************************************************
* AUDIO STREAMING IF
*
* Note(s): (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* appendix A.6 for more details about AudioStreaming class-specific interface descriptor
* subtype.
*
* (2) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* appendix A.8 for more details about AudioStreaming class-specific endpoint descriptor
* subtype.
*********************************************************************************************************
*/
/* AS IF desc subtype (see Note #1). */
#define USBD_AUDIO_DESC_SUBTYPE_AS_GENERAL 0x01u
#define USBD_AUDIO_DESC_SUBTYPE_FMT_TYPE 0x02u
#define USBD_AUDIO_DESC_SUBTYPE_FMT_SPECIFIC 0x03u
/* Audio EP desc subtype (see Note #2). */
#define USBD_AUDIO_DESC_SUBTYPE_EP_GENERAL 0x01u
/*
*********************************************************************************************************
* AUDIO DATA FMT
*
* Note(s): (1) See 'USB Device Class Definition for Audio Data Formats, Release 1.0, March 18, 1998',
* Appendix A.2 for more details about audio data format Type codes.
*********************************************************************************************************
*/
/* See Note #1. */
#define USBD_AUDIO_FMT_TYPE_UNDEFINED 0x00u
#define USBD_AUDIO_FMT_TYPE_I 0x01u
#define USBD_AUDIO_FMT_TYPE_II 0x02u
#define USBD_AUDIO_FMT_TYPE_III 0x03u
#define USBD_AUDIO_FMT_TYPE_I_CONT_FREQ_NBR 2u /* Continuous freq has a lower and upper freq. */
/*
*********************************************************************************************************
* AUDIO DATA STREAM
*
* Note(s): (1) The maximum throughput for Audio 1.0 is determined by the maximum packet size for
* an isochronous endpoint at full-speed, that is 1023 bytes/ms.
*
* (2) With stream correction enabled, it is required to allocate a certain number of buffers
* that takes into account the underrun and overrun boundaries around the pre-buffering
* value.
*
* (3) Without any stream correction enabled, it is required to allocate at least
* USBD_AUDIO_STREAM_BUF_QTY_MIN buffers per stream to ensure the proper functioning of the
* audio stream given a pre-buffering equal to half of the maximum number of buffers.
* The minimum pre-buffering is 2 buffers, that is 2 ms worth of audio data.
*********************************************************************************************************
*/
#define USBD_AUDIO_MAX_THROUGHPUT 1023u /* Max throughput in bytes/ms for Audio 1.0... */
/* ...(see Note #1). */
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
#define USBD_AUDIO_STREAM_BUF_QTY_MIN 6u /* See Note #2. */
#else
#define USBD_AUDIO_STREAM_BUF_QTY_MIN 4u /* See Note #3. */
#endif
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL CONSTANTS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL TABLES
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*
* Note(s): (1) This table contains one AIC (Audio Interface Collection) per element. The first element
* (index 0) corresponds to class number 0 and so on.
*********************************************************************************************************
*********************************************************************************************************
*/
/* Audio class instances tbl (see Note #1). */
static USBD_AUDIO_CTRL USBD_Audio_CtrlTbl[USBD_AUDIO_CFG_MAX_NBR_AIC];
static CPU_INT08U USBD_Audio_CtrlNbrNext;
/* Audio class comm tbl. */
static USBD_AUDIO_COMM USBD_Audio_CommTbl[USBD_AUDIO_MAX_NBR_COMM];
static CPU_INT08U USBD_Audio_CommNbrNext;
/* Input Terminal tbl. */
static USBD_AUDIO_IT USBD_Audio_IT_Tbl[USBD_AUDIO_CFG_MAX_NBR_IT];
static CPU_INT08U USBD_Audio_IT_NbrNext;
/* Output Terminal tbl. */
static USBD_AUDIO_OT USBD_Audio_OT_Tbl[USBD_AUDIO_CFG_MAX_NBR_OT];
static CPU_INT08U USBD_Audio_OT_NbrNext;
/* Feature Unit tbl. */
static USBD_AUDIO_FU USBD_Audio_FU_Tbl[USBD_AUDIO_CFG_MAX_NBR_FU];
static CPU_INT08U USBD_Audio_FU_NbrNext;
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
/* Mixer Unit tbl. */
static USBD_AUDIO_MU USBD_Audio_MU_Tbl[USBD_AUDIO_CFG_MAX_NBR_MU];
static CPU_INT08U USBD_Audio_MU_NbrNext;
#endif
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
/* Selector Unit tbl. */
static USBD_AUDIO_SU USBD_Audio_SU_Tbl[USBD_AUDIO_CFG_MAX_NBR_SU];
static CPU_INT08U USBD_Audio_SU_NbrNext;
#endif
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
/* Alternate Setting tbl. */
static USBD_AUDIO_AS_IF_ALT USBD_Audio_AS_IF_AltTbl[USBD_AUDIO_MAX_NBR_IF_ALT];
static CPU_INT08U USBD_Audio_AS_IF_AltNbrNext;
/* AS IF settings tbl. */
static USBD_AUDIO_AS_IF_SETTINGS USBD_Audio_AS_IF_SettingsTbl[USBD_AUDIO_MAX_NBR_AS_IF_SETTINGS];
static CPU_INT08U USBD_Audio_AS_IF_SettingsNbrNext;
#endif
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*********************************************************************************************************
*/
/* AudioControl IF Class callbacks. */
static void USBD_Audio_AC_Conn ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg);
static void USBD_Audio_AC_Disconn ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg);
static void USBD_Audio_AC_IF_Desc ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg);
static CPU_INT16U USBD_Audio_AC_IF_DescSizeGet ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg);
static CPU_BOOLEAN USBD_Audio_AC_ClassReq ( CPU_INT08U dev_nbr,
const USBD_SETUP_REQ *p_setup_req,
void *p_if_class_arg);
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
/* AudioStreaming IF Class callbacks. */
static void USBD_Audio_AS_Conn ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg);
static void USBD_Audio_AS_AltSettingUpdate( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg);
static void USBD_Audio_AS_IF_Desc ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg);
static CPU_INT16U USBD_Audio_AS_IF_DescSizeGet ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg);
static void USBD_Audio_AS_EP_Desc ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
CPU_INT08U ep_addr,
void *p_if_class_arg,
void *p_if_alt_class_arg);
static CPU_INT16U USBD_Audio_AS_EP_DescSizeGet ( CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
CPU_INT08U ep_addr,
void *p_if_class_arg,
void *p_if_alt_class_arg);
static CPU_BOOLEAN USBD_Audio_AS_ClassReq ( CPU_INT08U dev_nbr,
const USBD_SETUP_REQ *p_setup_req,
void *p_if_class_arg);
#endif
static void *USBD_Audio_AC_RecipientGet (const USBD_AUDIO_CTRL *p_ctrl,
CPU_INT08U entity_id,
USBD_AUDIO_ENTITY_TYPE *p_recipient_type);
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED) || \
(USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
static USBD_AUDIO_COMM *USBD_Audio_CommGet (const USBD_AUDIO_CTRL *p_ctrl,
CPU_INT08U cfg_nbr);
#endif
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
static CPU_INT16U USBD_Audio_MaxPktLenGet ( USBD_AUDIO_AS_ALT_CFG *p_as_cfg,
USBD_ERR *p_err);
#endif
/*
*********************************************************************************************************
* AUDIO CLASS DRIVER
*********************************************************************************************************
*/
static USBD_CLASS_DRV USBD_Audio_AC_Drv = { /* AudioControl IF class driver. */
USBD_Audio_AC_Conn,
USBD_Audio_AC_Disconn,
DEF_NULL,
DEF_NULL,
USBD_Audio_AC_IF_Desc,
USBD_Audio_AC_IF_DescSizeGet,
DEF_NULL,
DEF_NULL,
DEF_NULL,
USBD_Audio_AC_ClassReq,
DEF_NULL,
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
DEF_NULL,
DEF_NULL
#endif
};
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
static USBD_CLASS_DRV USBD_Audio_AS_Drv = { /* AudioStreaming IF class driver. */
USBD_Audio_AS_Conn,
DEF_NULL,
USBD_Audio_AS_AltSettingUpdate,
DEF_NULL,
USBD_Audio_AS_IF_Desc,
USBD_Audio_AS_IF_DescSizeGet,
USBD_Audio_AS_EP_Desc,
USBD_Audio_AS_EP_DescSizeGet,
DEF_NULL,
USBD_Audio_AS_ClassReq,
DEF_NULL,
#if (USBD_CFG_MS_OS_DESC_EN == DEF_ENABLED)
DEF_NULL,
DEF_NULL
#endif
};
#endif
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* GLOBAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_Audio_Init()
*
* Description : Initialize Audio class.
*
* 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.
*
* -RETURNED BY USBD_Audio_ProcessingInit()-
* See USBD_Audio_ProcessingInit() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
void USBD_Audio_Init (CPU_INT16U msg_qty,
USBD_ERR *p_err)
{
CPU_INT08U ix;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (msg_qty == 0u) { /* Validate msg qty. */
*p_err = USBD_ERR_INVALID_ARG;
return;
}
#endif
/* ------------ INITIALIZE CLASS STRUCTURES ----------- */
for (ix = 0u; ix < USBD_AUDIO_CFG_MAX_NBR_AIC; ix++) { /* Init audio class instances tbl. */
Mem_Clr((void *)&USBD_Audio_CtrlTbl[ix],
sizeof(USBD_AUDIO_CTRL));
USBD_Audio_CtrlTbl[ix].DevNbr = USBD_DEV_NBR_NONE;
}
Mem_Clr((void *)&USBD_Audio_CommTbl[0u], /* Init Audio class comm tbl. */
(USBD_AUDIO_MAX_NBR_COMM * sizeof(USBD_AUDIO_COMM)));
Mem_Clr((void *)&USBD_Audio_IT_Tbl[0u], /* Init Input Terminal tbl. */
(USBD_AUDIO_CFG_MAX_NBR_IT * sizeof(USBD_AUDIO_IT)));
Mem_Clr((void *)&USBD_Audio_OT_Tbl[0u], /* Init Output Terminal tbl. */
(USBD_AUDIO_CFG_MAX_NBR_OT * sizeof(USBD_AUDIO_OT)));
Mem_Clr((void *)&USBD_Audio_FU_Tbl[0u], /* Init Feature Unit tbl. */
(USBD_AUDIO_CFG_MAX_NBR_FU * sizeof(USBD_AUDIO_FU)));
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
Mem_Clr((void *)&USBD_Audio_MU_Tbl[0u], /* Init Mixer Unit tbl. */
(USBD_AUDIO_CFG_MAX_NBR_MU * sizeof(USBD_AUDIO_MU)));
#endif
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
Mem_Clr((void *)&USBD_Audio_SU_Tbl[0u], /* Init Selector Unit tbl. */
(USBD_AUDIO_CFG_MAX_NBR_SU * sizeof(USBD_AUDIO_SU)));
#endif
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
{
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
for (ix = 0u; ix < USBD_AUDIO_MAX_NBR_IF_ALT; ix++) { /* Init alternate setting tbl. */
p_as_if_alt = &USBD_Audio_AS_IF_AltTbl[ix];
p_as_if_alt->AS_CfgPtr = DEF_NULL;
p_as_if_alt->DataIsocAddr = USBD_EP_ADDR_NONE;
p_as_if_alt->SynchIsocAddr = USBD_EP_ADDR_NONE;
p_as_if_alt->MaxPktLen = 0u;
}
Mem_Clr((void *)&USBD_Audio_AS_IF_SettingsTbl[0u], /* Init AS IF settings tbl. */
(USBD_AUDIO_MAX_NBR_AS_IF_SETTINGS * sizeof(USBD_AUDIO_AS_IF_SETTINGS)));
USBD_Audio_AS_IF_AltNbrNext = 0u;
}
#endif
USBD_Audio_CtrlNbrNext = 0u;
USBD_Audio_CommNbrNext = 0u;
USBD_Audio_IT_NbrNext = 0u;
USBD_Audio_OT_NbrNext = 0u;
USBD_Audio_FU_NbrNext = 0u;
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
USBD_Audio_MU_NbrNext = 0u;
#endif
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
USBD_Audio_SU_NbrNext = 0u;
#endif
USBD_Audio_ProcessingInit(msg_qty, p_err); /* Init Audio Processing layer. */
}
/*
*********************************************************************************************************
* USBD_Audio_Add()
*
* Description : Create a new instance of the audio class (see Note #1).
*
* Argument(s) : entity_cnt Number of entities within the audio function.
*
* p_audio_drv_common_api Pointer to common audio codec driver API.
*
* p_audio_event_fnct Pointer to audio event callbacks.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Audio class instance successfully added.
* USBD_ERR_NULL_PTR Argument 'p_audio_drv_common_api' passed a
* NULL pointer.
* USBD_ERR_AUDIO_INSTANCE_ALLOC Audio class instances NOT available.
* USBD_ERR_ALLOC Cannot allocate request buffer from heap.
*
* -RETURNED BY p_audio_drv_common_api->Init()-
* See Audio Peripheral Driver Init() function for additional return error codes.
*
* Return(s) : Class instance number, if NO error(s).
*
* USBD_CLASS_NBR_NONE, otherwise.
*
* Note(s) : (1) One audio class instance = one audio function = one Audio Interface Collection (AIC).
*
* (2) In this version of the audio class, only the 1st form of data parameter is
* considered. Size of the request buffer should be therefore limited.
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* Table 5-20, for an example of the first form request.
*********************************************************************************************************
*/
CPU_INT08U USBD_Audio_Add ( CPU_INT08U entity_cnt,
const USBD_AUDIO_DRV_COMMON_API *p_audio_drv_common_api,
const USBD_AUDIO_EVENT_FNCTS *p_audio_event_fnct,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
CPU_INT08U class_nbr;
LIB_ERR err_lib;
CPU_SR_ALLOC();
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(USBD_CLASS_NBR_NONE);
}
if ((p_audio_drv_common_api == DEF_NULL) ||
(p_audio_drv_common_api->Init == DEF_NULL)) {
*p_err = USBD_ERR_NULL_PTR;
return (USBD_CLASS_NBR_NONE);
}
#endif
/* ---------------- ADD CLASS INSTANCE ---------------- */
CPU_CRITICAL_ENTER();
class_nbr = USBD_Audio_CtrlNbrNext; /* Alloc new audio class instance. */
if (class_nbr >= USBD_AUDIO_CFG_MAX_NBR_AIC) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_INSTANCE_ALLOC;
return (USBD_CLASS_NBR_NONE);
}
USBD_Audio_CtrlNbrNext++; /* Next avail audio class instance nbr. */
CPU_CRITICAL_EXIT();
/* ------------- STORE CLASS INSTANCE INFO ------------ */
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
p_ctrl->ClassNbr = class_nbr;
/* See Note #2. */
p_ctrl->ReqBufPtr = (CPU_INT08U *)Mem_HeapAlloc(USBD_AUDIO_CFG_CLASS_REQ_MAX_LEN,
USBD_CFG_BUF_ALIGN_OCTETS,
DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return (USBD_CLASS_NBR_NONE);
}
p_ctrl->EventFnctPtr = p_audio_event_fnct;
p_ctrl->EntityID_Nxt = 0u;
p_ctrl->EntityCnt = entity_cnt;
/* Alloc ptr to entity struct for ID management. */
p_ctrl->EntityID_TblPtr = (USBD_AUDIO_ENTITY **)Mem_HeapAlloc((entity_cnt * sizeof(USBD_AUDIO_ENTITY *)),
sizeof(CPU_ALIGN),
NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return (USBD_CLASS_NBR_NONE);
}
/* Init the audio driver associated to this AIC. */
p_audio_drv_common_api->Init(&p_ctrl->DrvInfo,
p_err);
return (class_nbr);
}
/*
*********************************************************************************************************
* USBD_Audio_CfgAdd()
*
* Description : Add an audio class instance into the specified configuration (see Note #1).
*
* Argument(s) : class_nbr Class instance number.
*
* dev_nbr Device number.
*
* cfg_nbr Configuration index to add Audio class instance to.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Audio class configuration successfully added.
* USBD_ERR_CFG_INVALID_NBR Invalid configuration number.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_INVALID_ARG Class number associated with a different device number.
* USBD_ERR_AUDIO_INSTANCE_ALLOC Audio class communication instances NOT available.
*
* -RETURNED BY USBD_IF_Add()-
* See USBD_IF_Add() for additional return error codes.
*
* Return(s) : None.
*
* Note(s) : (1) Called several times, it allows to create multiple instances and multiple configurations.
* For instance, the following architecture could be created :
*
* Audio Class 0 (device number 0)
* |-- Configuration 0
* |-- Configuration 1
*
* Audio Class 1 (device number 1)
* |-- Configuration 0
* |-- Configuration 1
*
* (2) Configuration Descriptor corresponding to an audio device has the following format:
*
* Configuration Descriptor
* |-- Interface Descriptor (AudioControl) [standard]
* |-- Header Descriptor [class-specific]
* |-- Unit Descriptor(s) [class-specific]
* |-- Terminal Descriptor(s) [class-specific]
* |-- Endpoint Descriptor (Interrupt IN) - optional [standard]
* |-- Interface Descriptor (AudioStreaming) [standard]
* |-- AS Interface Descriptor [class-specific]
* |-- AS Format Type Descriptor [class-specific]
* |-- AS Format-Specific Descriptor(s) [class-specific]
* |-- Endpoint Descriptor (Isochronous IN or OUT Data) [standard]
* |-- AS Isochronous Audio Data Endpoint Descriptor [class-specific]
* |-- Endpoint Descriptor (Isochronous OUT or IN Synch endpoint) [standard]
* |-- Interface Descriptor (AudioStreaming) [standard]
* |-- ...
* |-- Interface Descriptor (AudioStreaming) [standard]
* |-- ...
*
* The function USBD_Audio_CfgAdd() only adds the Interface Descriptor (AudioControl).
* Other standard and class-specific descriptors are added by functions:
* USBD_Audio_IT_Add()
* USBD_Audio_OT_Add()
* USBD_Audio_FU_Add()
* USBD_Audio_MU_Add()
* USBD_Audio_SU_Add()
* USBD_Audio_AS_IF_Add()
*********************************************************************************************************
*/
void USBD_Audio_CfgAdd (CPU_INT08U class_nbr,
CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_COMM *p_comm;
CPU_INT16U comm_ix;
CPU_SR_ALLOC();
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
if (cfg_nbr == USBD_CFG_NBR_NONE) { /* Check that config number is valid */
*p_err = USBD_ERR_CFG_INVALID_NBR;
return;
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check that class number is valid */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
CPU_CRITICAL_EXIT();
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if ((p_ctrl->DevNbr != USBD_DEV_NBR_NONE) && /* Chk if class is associated with a different dev. */
(p_ctrl->DevNbr != dev_nbr)) {
*p_err = USBD_ERR_INVALID_ARG;
return;
}
p_comm = USBD_Audio_CommGet(p_ctrl, cfg_nbr);
if(p_comm != DEF_NULL) {
*p_err = USBD_ERR_INVALID_ARG;
return;
}
#endif
/* --------------- ALLOC NEW COMM INFO ---------------- */
CPU_CRITICAL_ENTER();
comm_ix = USBD_Audio_CommNbrNext;
if (comm_ix >= USBD_AUDIO_MAX_NBR_COMM) { /* Check that comm index is within max range. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_INSTANCE_ALLOC;
return;
}
USBD_Audio_CommNbrNext++;
CPU_CRITICAL_EXIT();
p_comm = &USBD_Audio_CommTbl[comm_ix];
p_comm->CfgNbr = cfg_nbr;
/* ------- CFG DESC CONSTRUCTION (see Note #2) -------- */
(void)USBD_IF_Add( dev_nbr, /* Add AudioControl IF to cfg. */
cfg_nbr,
&USBD_Audio_AC_Drv,
(void *)p_comm,
DEF_NULL,
USBD_CLASS_CODE_AUDIO,
USBD_AUDIO_SUBCLASS_AUDIO_CTRL,
USBD_PROTOCOL_CODE_USE_IF_DESC,
"Audio Control Interface",
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
/* ---------- STORE AUDIO CLASS INSTANCE INFO ----------*/
p_ctrl->DevNbr = dev_nbr; /* Save device number */
p_comm->CtrlPtr = p_ctrl; /* Save pointer to control struct */
CPU_CRITICAL_ENTER();
p_ctrl->State = USBD_AUDIO_STATE_INIT;
CPU_CRITICAL_EXIT();
/* Add class instance info to audio comm list. */
if (p_ctrl->CommHeadPtr == DEF_NULL) { /* First element of list. */
p_ctrl->CommHeadPtr = p_comm;
} else {
p_ctrl->CommTailPtr->NextPtr = p_comm; /* Element at the end of list. */
}
p_ctrl->CommTailPtr = p_comm;
p_ctrl->CommCnt++; /* Increment nbr of audio comm info struct. */
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_Audio_IsConn()
*
* Description : Get the audio class connection state.
*
* Argument(s) : class_nbr Class instance number.
*
* Return(s) : DEF_YES, if Audio class is connected.
*
* DEF_NO, otherwise.
*
* Note(s) : None.
*********************************************************************************************************
*/
CPU_BOOLEAN USBD_Audio_IsConn (CPU_INT08U class_nbr)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_DEV_STATE state;
USBD_ERR err;
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check audio class instance nbr. */
CPU_CRITICAL_EXIT();
return (DEF_NO);
}
CPU_CRITICAL_EXIT();
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr];
state = USBD_DevStateGet(p_ctrl->DevNbr, &err); /* Get dev state. */
CPU_CRITICAL_ENTER();
if ((err == USBD_ERR_NONE ) &&
(state == USBD_DEV_STATE_CONFIGURED) &&
(p_ctrl->State == USBD_AUDIO_STATE_CFG )) {
CPU_CRITICAL_EXIT();
return (DEF_YES);
} else {
CPU_CRITICAL_EXIT();
return (DEF_NO);
}
}
/*
*********************************************************************************************************
* USBD_Audio_IT_Add()
*
* Description : Add an Input Terminal to the specified class instance (i.e. audio function).
*
* Argument(s) : class_nbr Class instance number.
*
* p_it_cfg Pointer to the Input Terminal configuration structure.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Input Terminal successfully added.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR Null pointer passed to 'p_it_cfg'.
* USBD_ERR_INVALID_ARG Wrong copy protection level OR
* invalid spatial locations.
* USBD_ERR_AUDIO_IT_ALLOC No Input Terminal structure available OR
* no entity structure available.
*
* -RETURNED BY USBD_StrAdd()-
* See USBD_StrAdd() for additional return error codes.
*
* Return(s) : Terminal ID assigned by audio class, if NO error(s).
*
* 0, otherwise (see Note #1).
*
* Note(s) : (1) Audio 1.0 specification indicates that ID #0 is reserved for undefined ID. Thus it
* indicates an error.
*********************************************************************************************************
*/
CPU_INT08U USBD_Audio_IT_Add ( CPU_INT08U class_nbr,
const USBD_AUDIO_IT_CFG *p_it_cfg,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_IT *p_it;
CPU_INT08U it_nbr;
CPU_INT08U ix;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
CPU_INT08U log_ch_cnt;
#endif
CPU_SR_ALLOC();
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(0u);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return (0u);
}
CPU_CRITICAL_EXIT();
if (p_it_cfg == DEF_NULL) { /* Check if IT config is null */
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
switch (p_it_cfg->CopyProtLevel) { /* Check copy protection level. */
case USBD_AUDIO_CPL_NONE:
case USBD_AUDIO_CPL0:
case USBD_AUDIO_CPL1:
case USBD_AUDIO_CPL2:
break;
default:
*p_err = USBD_ERR_INVALID_ARG;
return (0u);
}
if (p_it_cfg->LogChCfg == 0x00u) { /* Non-predefined spatial positions. */
if (p_it_cfg->LogChNbr > USBD_AUDIO_LOG_CH_NBR_MAX) { /* Check if nbr of log ch exceeds max allowed. */
*p_err = USBD_ERR_INVALID_ARG;
return (0u);
}
switch (p_it_cfg->LogChNbr) {
/* Default spatial location supported. */
case USBD_AUDIO_MONO:
case USBD_AUDIO_STEREO:
case 3u:
case 4u:
case USBD_AUDIO_5_1:
case USBD_AUDIO_7_1:
break;
default:
*p_err = USBD_ERR_INVALID_ARG;
return (0u);
}
} else { /* Predefined spatial positions. */
/* Cnt nbr of log ch from spatial locations bitmap. */
USBD_AUDIO_BIT_SET_CNT(log_ch_cnt, p_it_cfg->LogChCfg);
/* Check if nbr of log ch deduced from spatial... */
/* ...locations matches nbr of log ch declared. */
if ((p_it_cfg->LogChNbr != log_ch_cnt) ||
(p_it_cfg->LogChNbr > USBD_AUDIO_LOG_CH_NBR_MAX) ) {
*p_err = USBD_ERR_INVALID_ARG;
return (0u);
}
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ------------ GET INPUT TERMINAL STRUCT ------------- */
CPU_CRITICAL_ENTER();
it_nbr = USBD_Audio_IT_NbrNext;
if (it_nbr >= USBD_AUDIO_CFG_MAX_NBR_IT) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_IT_ALLOC;
return (0u);
}
USBD_Audio_IT_NbrNext++;
CPU_CRITICAL_EXIT();
p_it = &USBD_Audio_IT_Tbl[it_nbr];
/* ---------- ADD NEW IT TO CLASS CTRL INFO ----------- */
p_it->IT_CfgPtr = p_it_cfg; /* Save configuration for later use */
CPU_CRITICAL_ENTER();
if (p_ctrl->EntityID_Nxt > p_ctrl->EntityCnt) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_IT_ALLOC;
return (0u);
}
p_it->ID = p_ctrl->EntityID_Nxt + 1u; /* ID #0 reserved. Assigned ID needs +1 (see Note #1). */
ix = p_ctrl->EntityID_Nxt; /* ID used as a 0-index. */
p_ctrl->EntityID_Nxt++; /* Nxt avail ID. */
CPU_CRITICAL_EXIT();
p_it->EntityType = USBD_AUDIO_ENTITY_IT;
p_ctrl->EntityID_TblPtr[ix] = (USBD_AUDIO_ENTITY *)p_it;
USBD_StrAdd(p_ctrl->DevNbr, /* Add string describing this IT. */
p_it_cfg->StrPtr,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
*p_err = USBD_ERR_NONE;
return (p_it->ID);
}
/*
*********************************************************************************************************
* USBD_Audio_OT_Add()
*
* Description : Add an Output Terminal to the specified class instance (i.e. audio function).
*
* Argument(s) : class_nbr Class instance number.
*
* p_ot_cfg Pointer to the Output Terminal configuration structure.
*
* p_ot_api Pointer to the audio codec API associated to this Output Terminal.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Output Terminal successfully added.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR Null pointer passed to 'p_ot_cfg'/'p_ot_api'.
* USBD_ERR_AUDIO_OT_ALLOC No Output Terminal structure available OR
* no entity structure available.
*
* -RETURNED BY USBD_StrAdd()-
* See USBD_StrAdd() for additional return error codes.
*
* Return(s) : Terminal ID assigned by audio class, if NO error(s).
*
* 0, otherwise (see Note #1).
*
* Note(s) : (1) Audio 1.0 specification indicates that ID #0 is reserved for undefined ID. Thus it
* indicates an error.
*********************************************************************************************************
*/
CPU_INT08U USBD_Audio_OT_Add ( CPU_INT08U class_nbr,
const USBD_AUDIO_OT_CFG *p_ot_cfg,
const USBD_AUDIO_DRV_AC_OT_API *p_ot_api,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_OT *p_ot;
CPU_INT08U ot_nbr;
CPU_INT08U ix;
CPU_SR_ALLOC();
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(0u);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return (0u);
}
CPU_CRITICAL_EXIT();
if (p_ot_cfg == DEF_NULL) { /* Check pointer to OT configuration. */
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
/* If Copy Prot ctrl enabled, check if OT API present. */
if ((p_ot_cfg->CopyProtEn == DEF_ENABLED) &&
((p_ot_api == DEF_NULL) ||
(p_ot_api->OT_CopyProtSet == DEF_NULL))) {
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ------------ GET OUTPUT TERMINAL STRUCT ------------ */
CPU_CRITICAL_ENTER();
ot_nbr = USBD_Audio_OT_NbrNext;
if (ot_nbr >= USBD_AUDIO_CFG_MAX_NBR_OT) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_OT_ALLOC;
return (0u);
}
USBD_Audio_OT_NbrNext++;
CPU_CRITICAL_EXIT();
p_ot = &USBD_Audio_OT_Tbl[ot_nbr];
/* ---------- ADD NEW OT TO CLASS CTRL INFO ----------- */
p_ot->OT_CfgPtr = p_ot_cfg; /* Save configuration for later use */
p_ot->OT_API_Ptr = p_ot_api;
p_ot->DrvInfoPtr = &p_ctrl->DrvInfo;
CPU_CRITICAL_ENTER();
if (p_ctrl->EntityID_Nxt > p_ctrl->EntityCnt) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_OT_ALLOC;
return (0u);
}
p_ot->ID = p_ctrl->EntityID_Nxt + 1u; /* ID #0 reserved. Assigned ID needs +1 (see Note #1). */
ix = p_ctrl->EntityID_Nxt; /* ID used as a 0-index. */
p_ctrl->EntityID_Nxt++; /* Nxt avail ID. */
CPU_CRITICAL_EXIT();
p_ot->EntityType = USBD_AUDIO_ENTITY_OT;
p_ctrl->EntityID_TblPtr[ix] = (USBD_AUDIO_ENTITY *)p_ot;
USBD_StrAdd(p_ctrl->DevNbr, /* Add string describing this OT. */
p_ot_cfg->StrPtr,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
*p_err = USBD_ERR_NONE;
return (p_ot->ID);
}
/*
*********************************************************************************************************
* USBD_Audio_FU_Add()
*
* Description : Add a Feature Unit to the specified class instance (i.e. audio function).
*
* Argument(s) : class_nbr Class instance number.
*
* p_fu_cfg Pointer to the Feature Unit configuration structure.
*
* p_fu_api Pointer to the audio codec API associated to this Feature Unit.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Feature Unit successfully added.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR Null pointer passed to 'p_fu_cfg'/'p_fu_api'.
* USBD_ERR_AUDIO_FU_ALLOC No Feature Unit structure available OR
* no entity structure available.
*
* -RETURNED BY USBD_StrAdd()-
* See USBD_StrAdd() for additional return error codes.
*
* Return(s) : Unit ID assigned by audio class, if NO error(s).
*
* 0, otherwise (see Note #1).
*
* Note(s) : (1) Audio 1.0 specification indicates that ID #0 is reserved for undefined ID. Thus it
* indicates an error.
*********************************************************************************************************
*/
CPU_INT08U USBD_Audio_FU_Add ( CPU_INT08U class_nbr,
const USBD_AUDIO_FU_CFG *p_fu_cfg,
const USBD_AUDIO_DRV_AC_FU_API *p_fu_api,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_FU *p_fu;
CPU_INT08U fu_nbr;
CPU_INT08U ix;
CPU_SR_ALLOC();
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(0u);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return (0u);
}
CPU_CRITICAL_EXIT();
if (p_fu_cfg == DEF_NULL) { /* Check Pointer to FU configuration */
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
/* Any FU should supports minimally mute and vol ctrl. */
if ((p_fu_api == DEF_NULL) ||
(p_fu_api->FU_MuteManage == DEF_NULL) ||
(p_fu_api->FU_VolManage == DEF_NULL)) {
*p_err = USBD_ERR_NULL_PTR;
return (USBD_CLASS_NBR_NONE);
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ------------- GET FEATURE UNIT STRUCT -------------- */
CPU_CRITICAL_ENTER();
fu_nbr = USBD_Audio_FU_NbrNext;
if (fu_nbr >= USBD_AUDIO_CFG_MAX_NBR_FU) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_FU_ALLOC;
return (0u);
}
USBD_Audio_FU_NbrNext++;
CPU_CRITICAL_EXIT();
p_fu = &USBD_Audio_FU_Tbl[fu_nbr];
/* ---------- ADD NEW FU TO CLASS CTRL INFO ----------- */
p_fu->FU_CfgPtr = p_fu_cfg; /* Save configuration for later use */
p_fu->FU_API_Ptr = p_fu_api;
p_fu->DrvInfoPtr = &p_ctrl->DrvInfo;
CPU_CRITICAL_ENTER();
if (p_ctrl->EntityID_Nxt > p_ctrl->EntityCnt) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_FU_ALLOC;
return (0u);
}
p_fu->ID = p_ctrl->EntityID_Nxt + 1u; /* ID #0 reserved. Assigned ID needs +1 (see Note #1). */
ix = p_ctrl->EntityID_Nxt; /* ID used as a 0-index. */
p_ctrl->EntityID_Nxt++; /* Nxt avail ID. */
CPU_CRITICAL_EXIT();
p_fu->EntityType = USBD_AUDIO_ENTITY_FU;
p_ctrl->EntityID_TblPtr[ix] = (USBD_AUDIO_ENTITY *)p_fu;
USBD_StrAdd(p_ctrl->DevNbr, /* Add string describing this FU. */
p_fu_cfg->StrPtr,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
*p_err = USBD_ERR_NONE;
return (p_fu->ID);
}
/*
*********************************************************************************************************
* USBD_Audio_MU_Add()
*
* Description : Add a Mixer Unit to the specified class instance (i.e. audio function).
*
* Argument(s) : class_nbr Class instance number
*
* p_mu_cfg Pointer to the Mixer Unit configuration structure.
*
* p_mu_api Pointer to the audio codec API associated to this Mixer Unit.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Mixer Unit successfully added.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR Null pointer passed to 'p_mu_cfg'.
* USBD_ERR_AUDIO_MU_ALLOC No Mixer Unit structure available OR
* no entity structure available.
* USBD_ERR_ALLOC No space on heap to allocate mixing controls bitmap.
*
* -RETURNED BY USBD_StrAdd()-
* See USBD_StrAdd() for additional return error codes.
*
* Return(s) : Unit ID assigned by audio class, if NO error(s).
*
* 0, otherwise (see Note #1).
*
* Note(s) : (1) Audio 1.0 specification indicates that ID #0 is reserved for undefined ID. Thus it
* indicates an error.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
CPU_INT08U USBD_Audio_MU_Add ( CPU_INT08U class_nbr,
const USBD_AUDIO_MU_CFG *p_mu_cfg,
const USBD_AUDIO_DRV_AC_MU_API *p_mu_api,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_MU *p_mu;
CPU_INT08U mu_nbr;
CPU_INT32U nbr_mixing_ctrl;
LIB_ERR err_lib;
CPU_INT08U ix;
CPU_SR_ALLOC();
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(0u);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return (0u);
}
CPU_CRITICAL_EXIT();
if (p_mu_cfg == DEF_NULL) { /* Check Pointer to MU configuration */
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* -------------- GET MIXER UNIT STRUCT --------------- */
CPU_CRITICAL_ENTER();
mu_nbr = USBD_Audio_MU_NbrNext;
if (mu_nbr >= USBD_AUDIO_CFG_MAX_NBR_MU) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_MU_ALLOC;
return (0u);
}
USBD_Audio_MU_NbrNext++;
CPU_CRITICAL_EXIT();
p_mu = &USBD_Audio_MU_Tbl[mu_nbr];
/* ---------- ADD NEW MU TO CLASS COMM INFO ----------- */
/* Alloc programmable ctrl bitmap tbl. */
nbr_mixing_ctrl = p_mu_cfg->LogInChNbr * p_mu_cfg->LogOutChNbr;
if ((nbr_mixing_ctrl % DEF_OCTET_NBR_BITS) == 0) {
p_mu->ControlsSize = nbr_mixing_ctrl / DEF_OCTET_NBR_BITS;
} else {
p_mu->ControlsSize = (nbr_mixing_ctrl / DEF_OCTET_NBR_BITS) + 1u;
}
p_mu->ControlsTblPtr = (CPU_INT08U *)Mem_HeapAlloc(p_mu->ControlsSize,
sizeof(CPU_ALIGN),
NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return (0u);
}
Mem_Clr((void *)p_mu->ControlsTblPtr,
p_mu->ControlsSize);
p_mu->MU_CfgPtr = p_mu_cfg; /* Save configuration for later use */
p_mu->MU_API_Ptr = p_mu_api;
p_mu->DrvInfoPtr = &p_ctrl->DrvInfo;
CPU_CRITICAL_ENTER();
if (p_ctrl->EntityID_Nxt > p_ctrl->EntityCnt) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_MU_ALLOC;
return (0u);
}
p_mu->ID = p_ctrl->EntityID_Nxt + 1u; /* ID #0 reserved. Assigned ID needs +1 (see Note #1). */
ix = p_ctrl->EntityID_Nxt; /* ID used as a 0-index. */
p_ctrl->EntityID_Nxt++; /* Nxt avail ID. */
CPU_CRITICAL_EXIT();
p_mu->EntityType = USBD_AUDIO_ENTITY_MU;
p_ctrl->EntityID_TblPtr[ix] = (USBD_AUDIO_ENTITY *)p_mu;
USBD_StrAdd(p_ctrl->DevNbr, /* Add string describing this MU. */
p_mu_cfg->StrPtr,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
*p_err = USBD_ERR_NONE;
return (p_mu->ID);
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_SU_Add()
*
* Description : Add a Selector Unit to the specified class instance (i.e. audio function).
*
* Argument(s) : class_nbr Class instance number.
*
* p_su_cfg Pointer to the Selector Unit configuration structure.
*
* p_su_api Pointer to the audio codec API associated to this Selector Unit.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Selector Unit successfully added.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR Null pointer passed to 'p_su_cfg'/'p_su_api'.
* USBD_ERR_AUDIO_SU_ALLOC No Selector Unit structure available OR
* no entity structure available.
*
* -RETURNED BY USBD_StrAdd()-
* See USBD_StrAdd() for additional return error codes.
*
* Return(s) : Unit ID assigned by audio class, if NO error(s).
*
* 0, otherwise (see Note #1).
*
* Note(s) : (1) Audio 1.0 specification indicates that ID #0 is reserved for undefined ID. Thus it
* indicates an error.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
CPU_INT08U USBD_Audio_SU_Add ( CPU_INT08U class_nbr,
const USBD_AUDIO_SU_CFG *p_su_cfg,
const USBD_AUDIO_DRV_AC_SU_API *p_su_api,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_SU *p_su;
CPU_INT08U su_nbr;
CPU_INT08U ix;
CPU_SR_ALLOC();
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(0u);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return (0u);
}
CPU_CRITICAL_EXIT();
if (p_su_cfg == DEF_NULL) { /* Check Pointer to SU configuration */
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
/* Check if SU API provided. */
if ((p_su_api == DEF_NULL) ||
(p_su_api->SU_InPinManage == DEF_NULL)) {
*p_err = USBD_ERR_NULL_PTR;
return (0u);
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ------------- GET SELECTOR UNIT STRUCT ------------- */
CPU_CRITICAL_ENTER();
su_nbr = USBD_Audio_SU_NbrNext;
if (su_nbr >= USBD_AUDIO_CFG_MAX_NBR_SU) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_SU_ALLOC;
return (0u);
}
USBD_Audio_SU_NbrNext++;
CPU_CRITICAL_EXIT();
p_su = &USBD_Audio_SU_Tbl[su_nbr];
/* ---------- ADD NEW MU TO CLASS COMM INFO ----------- */
p_su->SU_CfgPtr = p_su_cfg; /* Save configuration for later use */
p_su->SU_API_Ptr = p_su_api;
p_su->DrvInfoPtr = &p_ctrl->DrvInfo;
CPU_CRITICAL_ENTER();
if (p_ctrl->EntityID_Nxt > p_ctrl->EntityCnt) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_SU_ALLOC;
return (0u);
}
p_su->ID = p_ctrl->EntityID_Nxt + 1u; /* ID #0 reserved. Assigned ID needs +1 (see Note #1). */
ix = p_ctrl->EntityID_Nxt; /* ID used as a 0-index. */
p_ctrl->EntityID_Nxt++; /* Nxt avail ID. */
CPU_CRITICAL_EXIT();
p_su->EntityType = USBD_AUDIO_ENTITY_SU;
p_ctrl->EntityID_TblPtr[ix] = (USBD_AUDIO_ENTITY *)p_su;
USBD_StrAdd(p_ctrl->DevNbr, /* Add string describing this SU. */
p_su_cfg->StrPtr,
p_err);
if (*p_err != USBD_ERR_NONE) {
return (0u);
}
*p_err = USBD_ERR_NONE;
return (p_su->ID);
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_IT_Assoc()
*
* Description : Associate the Input Terminal to an Output Terminal (see Note #1).
*
* Argument(s) : class_nbr Class instance number.
*
* it_id Input Terminal ID.
*
* assoc_terminal_id Output Terminal ID associated (see Note #2).
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Input Terminal successfully associated.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
*
* Return(s) : none.
*
* Note(s) : (1) The Input Terminal descriptor has the field 'bAssocTerminal'. It associates an
* Output Terminal to this Input Terminal, effectively implementing a bi-directional
* Terminal pair.
*
* (2) If there is no associated Output Terminal, you can specify
* USBD_AUDIO_TERMINAL_NO_ASSOCIATION.
*********************************************************************************************************
*/
void USBD_Audio_IT_Assoc (CPU_INT08U class_nbr,
CPU_INT08U it_id,
CPU_INT08U assoc_terminal_id,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_IT *p_it;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
CPU_CRITICAL_EXIT();
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ID used as a 0-index. */
p_it = (USBD_AUDIO_IT *)p_ctrl->EntityID_TblPtr[(it_id - 1u)];
p_it->AssociatedOT_ID = assoc_terminal_id; /* Save associated OT ID. */
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_Audio_OT_Assoc()
*
* Description : Specify the entity ID connected to this Output Terminal and associate the Output Terminal
* to an Input Terminal (see Note #1).
*
* Argument(s) : class_nbr Class instance number.
*
* ot_id Output Terminal ID.
*
* src_entity_id ID of the Unit or Terminal to which this Terminal is connected.
*
* assoc_terminal_id Input Terminal ID associated (see Note #2).
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Output Terminal successfully associated.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
*
* Return(s) : none.
*
* Note(s) : (1) The Output Terminal descriptor has the field 'bAssocTerminal'. It associates an
* Input Terminal to this Output Terminal, effectively implementing a bi-directional
* Terminal pair.
*
* (2) If there is no associated Input Terminal, you can specify
* USBD_AUDIO_TERMINAL_NO_ASSOCIATION.
*********************************************************************************************************
*/
void USBD_Audio_OT_Assoc (CPU_INT08U class_nbr,
CPU_INT08U ot_id,
CPU_INT08U src_entity_id,
CPU_INT08U assoc_terminal_id,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_OT *p_ot;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
CPU_CRITICAL_EXIT();
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ID used as a 0-index. */
p_ot = (USBD_AUDIO_OT *)p_ctrl->EntityID_TblPtr[(ot_id - 1u)];
p_ot->AssociatedIT_ID = assoc_terminal_id; /* Save associated IT ID. */
p_ot->SourceID = src_entity_id; /* Save unit or terminal ID. */
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_Audio_FU_Assoc()
*
* Description : Specify the entity ID connected to this Feature Unit.
*
* Argument(s) : class_nbr Class instance number.
*
* fu_id Feature Unit ID.
*
* entity_id_src ID of the Unit or Terminal to which this Unit is connected.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Feature Unit successfully associated.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_INVALID_ARG Output Terminal cannot be connected as input of
* a Feature Unit.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
void USBD_Audio_FU_Assoc (CPU_INT08U class_nbr,
CPU_INT08U fu_id,
CPU_INT08U src_entity_id,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_FU *p_fu;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
CPU_CRITICAL_EXIT();
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ID used as a 0-index. */
p_fu = (USBD_AUDIO_FU *)p_ctrl->EntityID_TblPtr[(fu_id - 1u)];
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
USBD_AUDIO_ENTITY *p_entity_src;
p_entity_src = p_ctrl->EntityID_TblPtr[(src_entity_id - 1u)];
if (p_entity_src->EntityType == USBD_AUDIO_ENTITY_OT) { /* No OT can be connected as an input of FU. */
*p_err = USBD_ERR_INVALID_ARG;
return;
}
}
#endif
p_fu->SourceID = src_entity_id; /* Save unit or terminal ID. */
*p_err = USBD_ERR_NONE;
}
/*
*********************************************************************************************************
* USBD_Audio_MU_Assoc()
*
* Description : Specify the entities ID connected to this Mixer Unit.
*
* Argument(s) : class_nbr Class instance number.
*
* mu_id Mixer Unit ID.
*
* p_src_entity_id Pointer to table containing IDs of Units and/or Terminals to which
* Input Pins of this Mixer Unit are connected.
*
* nbr_input_pins Number of input pins.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Mixer Unit successfully associated.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR Null pointer passed to 'p_src_entity_id'.
* USBD_ERR_INVALID_ARG Output Terminal cannot be connected as input of
* a Mixer Unit.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
void USBD_Audio_MU_Assoc (CPU_INT08U class_nbr,
CPU_INT08U mu_id,
CPU_INT08U *p_src_entity_id,
CPU_INT08U nbr_input_pins,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_MU *p_mu;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
CPU_CRITICAL_EXIT();
if (p_src_entity_id == DEF_NULL) { /* Check src IDs ptr. */
*p_err = USBD_ERR_NULL_PTR;
return;
}
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ID used as a 0-index. */
p_mu = (USBD_AUDIO_MU *)p_ctrl->EntityID_TblPtr[(mu_id - 1u)];
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_INT08U ix;
CPU_INT08U entity_id_src;
USBD_AUDIO_ENTITY *p_entity_src;
for (ix = 0; ix < nbr_input_pins; ix++) {
entity_id_src = p_src_entity_id[ix];
p_entity_src = p_ctrl->EntityID_TblPtr[(entity_id_src - 1u)];
/* No OT can be connected as an input of MU. */
if (p_entity_src->EntityType == USBD_AUDIO_ENTITY_OT) {
*p_err = USBD_ERR_INVALID_ARG;
return;
}
}
}
#else
(void)nbr_input_pins;
#endif
p_mu->SourceID_TblPtr = p_src_entity_id; /* Save unit and/or terminal IDs. */
*p_err = USBD_ERR_NONE;
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_SU_Assoc()
*
* Description : Specify the entities ID connected to this Selector Unit.
*
* Argument(s) : class_nbr Class instance number.
*
* su_id Selector Unit ID.
*
* p_src_entity_id Pointer to table containing IDs of Units and/or Terminals to which
* Input Pins of this Selector Unit are connected.
*
* nbr_input_pins Number of input pins.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Selector Unit successfully associated.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR Null pointer passed to 'p_src_entity_id'.
* USBD_ERR_INVALID_ARG Output Terminal cannot be connected as input of
* a Selector Unit.
*
* Return(s) : none.
*
* Note(s) : none.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_MAX_NBR_SU > 0u)
void USBD_Audio_SU_Assoc (CPU_INT08U class_nbr,
CPU_INT08U su_id,
CPU_INT08U *p_src_entity_id,
CPU_INT08U nbr_input_pins,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_SU *p_su;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
CPU_CRITICAL_EXIT();
if (p_src_entity_id == DEF_NULL) { /* Check src IDs ptr. */
*p_err = USBD_ERR_NULL_PTR;
return;
}
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ID used as a 0-index. */
p_su = (USBD_AUDIO_SU *)p_ctrl->EntityID_TblPtr[(su_id - 1u)];
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_INT08U ix;
CPU_INT08U entity_id_src;
USBD_AUDIO_ENTITY *p_entity_src;
for (ix = 0; ix < nbr_input_pins; ix++) {
entity_id_src = p_src_entity_id[ix];
p_entity_src = p_ctrl->EntityID_TblPtr[(entity_id_src - 1u)];
/* No OT can be connected as an input of SU. */
if (p_entity_src->EntityType == USBD_AUDIO_ENTITY_OT) {
*p_err = USBD_ERR_INVALID_ARG;
return;
}
}
}
#else
(void)nbr_input_pins;
#endif
p_su->SourceID_TblPtr = p_src_entity_id; /* Save unit and/or terminal IDs. */
*p_err = USBD_ERR_NONE;
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_MU_MixingCtrlSet()
*
* Description : Set programmable mixing control.
*
* Argument(s) : class_nbr Class instance number.
*
* mu_id Mixer Unit ID.
*
* log_in_ch_nbr Logical input channel number.
*
* log_out_ch_nbr Logical output channel number.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Mixing control successfully set.
* USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR Mixer Unit API not provided.
*
* Return(s) : none.
*
* Note(s) : (1) See function USBD_Audio_AC_UnitCtrl() note #3.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_MAX_NBR_MU > 0u)
void USBD_Audio_MU_MixingCtrlSet (CPU_INT08U class_nbr,
CPU_INT08U mu_id,
CPU_INT08U log_in_ch_nbr,
CPU_INT08U log_out_ch_nbr,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_MU *p_mu;
CPU_INT08U byte_ix;
CPU_INT08U bit_shift;
CPU_INT32U bit_nbr;
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
{
CPU_SR_ALLOC();
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
CPU_CRITICAL_EXIT();
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
/* ID used as a 0-index. */
p_mu = (USBD_AUDIO_MU *)p_ctrl->EntityID_TblPtr[(mu_id - 1u)];
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
/* If MU has programmable ctrls, API must be provided. */
if ((p_mu->MU_API_Ptr == DEF_NULL) &&
(p_mu->MU_API_Ptr->MU_CtrlManage == DEF_NULL)) {
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
/* Set this mixing ctrl as programmable within... */
/* ...bitmap (see Note #1). */
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;
DEF_BIT_SET(p_mu->ControlsTblPtr[byte_ix], ((DEF_BIT_07) >> bit_shift));
*p_err = USBD_ERR_NONE;
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_AS_IF_Cfg()
*
* Description : Configure AudioStreaming interface settings by:
*
* 1) Allocating an AudioStreaming Settings structure.
* 2) Initializing this structure with the stream information.
* 3) Allocating buffers that will be used by the given AudioStreaming interface.
*
* Argument(s) : p_stream_cfg Pointer to general stream configuration.
*
* p_as_if_cfg Pointer to AudioStreaming interface configuration structure.
*
* p_as_api Pointer to AudioStreaming interface API.
*
* p_seg Pointer to memory segment used for buffers allocation. If null pointer,
* buffers are allocated from the heap. Otherwise, buffers are allocated
* from a memory region created by the application.
*
* terminal_ID Terminal ID associated to this AudioStreaming interface.
*
* corr_callback Application callback for user-defined correction algorithm.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Buffers successfully allocated.
* USBD_ERR_NULL_PTR NULL pointer passed to 'p_as_if_cfg'/'p_as_api'/
* 'p_stream_cfg'.
* USBD_ERR_INVALID_ARG Invalid 'p_stream_cfg' or 'p_as_if_cfg' field.
* USBD_ERR_ALLOC AS IF Settings structure, buffers, events or audio
* statistics structure allocation failed.
*
* -RETURNED BY USBD_Audio_MaxPktLenGet()-
* See USBD_Audio_MaxPktLenGet() for additional return error codes.
*
* Return(s) : Handle to AudioStreaming interface, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) Streams can be corrected using the following corrections: built-in (for playback
* and record) and feedback (for playback only).
* Playback and record streams correction rely on a buffers difference monitoring.
* The difference involves the rate at which the buffers are produced (by USB or codec)
* versus the rate at which they are consumed (by codec or USB). The buffers total
* number allocated for a stream is divided by 6. The quotient of this division
* represents an interval used to set different boundaries used for the stream
* correction. The following figure illustrates what the boundaries are depending
* of the correction method:
*
* Buffers Number 0 1 2 3 4 5 6 7 8 9 10 11 12
* Buffers Diff -6 -5 -4 -3 -2 -1 0 +1 +2 +3 +4 +5 +6
* | | | | | | |
* Boundary | heavy light light heavy |
* Built-In Corr | corr | safe zone | corr |
* Feedback Corr | correction | safe zone | correction |
*
* In the figure, the stream has 12 buffers. Thus, the quotient is 2. The heavy
* boundaries will be -4 and +4 for built-in and feedback corrections. The feedback
* correction uses two more light boundaries (here -2 and +2) to apply a progressive
* correction.
*
* (2) Allocation of audio buffers is done from a memory segment. This memory segment
* will be organized when the stream is open. When allocating the memory segment
* for the stream's audio buffers, the buffer's alignment must take into account
* any alignment to DMA and cache. The user must properly configure the constant
* USBD_AUDIO_CFG_BUF_ALIGN_OCTETS given any DMA and cache alignment requirements.
*
* (3) Pre-buffering is equal to half of buffers total number allocated for this stream.
* This pre-bufferring value eases the stream safe zone monitoring for the correction.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
USBD_AUDIO_AS_IF_HANDLE USBD_Audio_AS_IF_Cfg (const USBD_AUDIO_STREAM_CFG *p_stream_cfg,
const USBD_AUDIO_AS_IF_CFG *p_as_if_cfg,
const USBD_AUDIO_DRV_AS_API *p_as_api,
MEM_SEG *p_seg,
CPU_INT08U terminal_ID,
USBD_AUDIO_PLAYBACK_CORR_FNCT corr_callback,
USBD_ERR *p_err)
{
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
USBD_AUDIO_AS_ALT_CFG *p_as_cfg;
USBD_AUDIO_AS_IF_HANDLE as_if_handle;
USBD_AUDIO_STREAM_DIR stream_dir;
CPU_INT08U as_if_settings_ix;
CPU_INT16U mem_blk_len;
CPU_INT16U max_mem_blk_len;
CPU_INT16U mem_blk_len_worst;
CPU_INT08U as_alt_ix;
CPU_INT16U max_pkt_len;
LIB_ERR err_lib;
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
CPU_INT08U audio_frame_len;
#endif
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
CPU_INT32U max_sam_freq;
CPU_INT08U sam_freq_ix;
CPU_INT16U cur_max_throughput;
#endif
CPU_SR_ALLOC();
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_DISABLED)
(void)corr_callback;
#endif
/* ------------------- VALIDATE ARG ------------------- */
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(DEF_NULL);
}
if (p_as_if_cfg == DEF_NULL) { /* Check ptr to AS IF cfg. */
*p_err = USBD_ERR_NULL_PTR;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
if (p_as_api == DEF_NULL) {
*p_err = USBD_ERR_NULL_PTR;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
if ((p_as_api->AS_SamplingFreqManage == DEF_NULL) || /* Any AS must provide minimally these fnct. */
(p_as_api->StreamStart == DEF_NULL) ||
(p_as_api->StreamStop == DEF_NULL)) {
*p_err = USBD_ERR_NULL_PTR;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
#endif
if ((USBD_AUDIO_CFG_BUF_ALIGN_OCTETS < USBD_CFG_BUF_ALIGN_OCTETS) ||
((USBD_AUDIO_CFG_BUF_ALIGN_OCTETS % USBD_CFG_BUF_ALIGN_OCTETS) != 0u)) {
*p_err = USBD_ERR_INVALID_ARG;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
p_as_cfg = p_as_if_cfg->AS_CfgPtrTbl[0u];
stream_dir = (p_as_cfg->EP_DirIn == DEF_YES) ? USBD_AUDIO_STREAM_IN : USBD_AUDIO_STREAM_OUT;
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
/* If playback stream, associated fnct must be provided.*/
if ((stream_dir == USBD_AUDIO_STREAM_OUT) &&
(p_as_api->StreamPlaybackTx == DEF_NULL)) {
*p_err = USBD_ERR_NULL_PTR;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
/* If record stream, associated fnct must be provided. */
if ((stream_dir == USBD_AUDIO_STREAM_IN) &&
(p_as_api->StreamRecordRx == DEF_NULL)) {
*p_err = USBD_ERR_NULL_PTR;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
/* General stream cfg content check. */
if (p_stream_cfg == DEF_NULL) { /* Check ptr to general stream cfg struct. */
*p_err = USBD_ERR_NULL_PTR;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
/* Ensure a min of buf w/o or w/ stream corr. */
if (p_stream_cfg->MaxBufNbr < USBD_AUDIO_STREAM_BUF_QTY_MIN) {
*p_err = USBD_ERR_INVALID_ARG;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
if (p_stream_cfg->CorrPeriodMs < 1u) { /* Corr period must be at least 1 ms. */
*p_err = USBD_ERR_INVALID_ARG;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
/* When stream corr en, max buf nbr must be multiple... */
/* ...of 6 to ensure proper corr oper (see Note #1). */
if ((p_stream_cfg->MaxBufNbr % USBD_AUDIO_STREAM_CORR_BOUNDARY_INTERVAL) != 0u) {
*p_err = USBD_ERR_INVALID_ARG;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
#endif
/* AS IF cfg content check. */
if (p_as_if_cfg->AS_CfgAltSettingNbr > USBD_AUDIO_CFG_MAX_NBR_IF_ALT) {
*p_err = USBD_ERR_INVALID_ARG;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
/* Check nbr of alt settings */
for (as_alt_ix = 0u; as_alt_ix < p_as_if_cfg->AS_CfgAltSettingNbr; as_alt_ix++) {
p_as_cfg = p_as_if_cfg->AS_CfgPtrTbl[as_alt_ix];
if (p_as_cfg == DEF_NULL) { /* Check ptr to AudioStreaming alt setting cfg. */
*p_err = USBD_ERR_NULL_PTR;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
if ((p_as_cfg->NbrSamplingFreq != 0u) &&
(p_as_cfg->SamplingFreqTblPtr == DEF_NULL)) {
*p_err = USBD_ERR_NULL_PTR;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
}
/* Verify that among all alt setting for given AS IF,...*/
/* ...max allowed Audio 1.0 throughput not exceeded. */
cur_max_throughput = 0u;
for (as_alt_ix = 0u; as_alt_ix < p_as_if_cfg->AS_CfgAltSettingNbr; as_alt_ix++) {
p_as_cfg = p_as_if_cfg->AS_CfgPtrTbl[as_alt_ix];
if (p_as_cfg->NbrSamplingFreq == 0u) {
max_sam_freq = p_as_cfg->UpperSamplingFreq;
} else {
max_sam_freq = 0u;
for(sam_freq_ix = 0u; sam_freq_ix < p_as_cfg->NbrSamplingFreq; sam_freq_ix++) {
if(p_as_cfg->SamplingFreqTblPtr[sam_freq_ix] > max_sam_freq) {
max_sam_freq = p_as_cfg->SamplingFreqTblPtr[sam_freq_ix];
}
}
}
cur_max_throughput = (max_sam_freq / DEF_TIME_NBR_mS_PER_SEC) * (p_as_cfg->BitRes / DEF_OCTET_NBR_BITS) * p_as_cfg->NbrCh;
if (cur_max_throughput > USBD_AUDIO_MAX_THROUGHPUT) {
*p_err = USBD_ERR_INVALID_ARG;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
}
#endif
/* --------------- AS IF SETTINGS ALLOC --------------- */
CPU_CRITICAL_ENTER();
as_if_settings_ix = USBD_Audio_AS_IF_SettingsNbrNext; /* Alloc new AS IF settings. */
if (as_if_settings_ix >= USBD_AUDIO_MAX_NBR_IF_ALT) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_ALLOC;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
USBD_Audio_AS_IF_SettingsNbrNext++; /* Next avail AS IF settings nbr. */
CPU_CRITICAL_EXIT();
p_as_if_settings = &USBD_Audio_AS_IF_SettingsTbl[as_if_settings_ix];
/* Returned to app for later use by audio class. */
as_if_handle = (USBD_AUDIO_AS_IF_HANDLE)p_as_if_settings;
/* ------------------ BUF POOL ALLOC ------------------ */
max_mem_blk_len = 0u;
/* Find largest buffer among all alt settings. */
for (as_alt_ix = 0u; as_alt_ix < p_as_if_cfg->AS_CfgAltSettingNbr; as_alt_ix++) {
p_as_cfg = p_as_if_cfg->AS_CfgPtrTbl[as_alt_ix];
max_pkt_len = USBD_Audio_MaxPktLenGet(p_as_cfg, /* Max pkt size. */
p_err);
if (*p_err != USBD_ERR_NONE) {
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
/* Max buf len. */
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
audio_frame_len = p_as_cfg->SubframeSize * p_as_cfg->NbrCh;
mem_blk_len = max_pkt_len + audio_frame_len;
#else
mem_blk_len = max_pkt_len;
#endif
if (mem_blk_len > max_mem_blk_len) { /* Keep largest buffer size among all alt settings. */
max_mem_blk_len = mem_blk_len;
}
}
/* See Note #2. */
mem_blk_len_worst = MATH_ROUND_INC_UP(max_mem_blk_len * (p_stream_cfg->ExtraPktTx + 1),
USBD_AUDIO_CFG_BUF_ALIGN_OCTETS);
p_as_if_settings->BufMemPtr = (CPU_INT08U *)Mem_HeapAlloc((mem_blk_len_worst * p_stream_cfg->MaxBufNbr),
USBD_AUDIO_CFG_BUF_ALIGN_OCTETS,
DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
/* Alloc buf desc from heap. */
p_as_if_settings->StreamRingBufQ.BufDescTblPtr = (USBD_AUDIO_BUF_DESC *)Mem_HeapAlloc((sizeof(USBD_AUDIO_BUF_DESC) * p_stream_cfg->MaxBufNbr),
sizeof(CPU_ALIGN),
DEF_NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
/* --------------- AS IF SETTINGS INIT ---------------- */
p_as_if_settings->AS_API_Ptr = p_as_api;
p_as_if_settings->Ix = as_if_settings_ix;
p_as_if_settings->TerminalID = terminal_ID;
p_as_if_settings->BufTotalNbr = p_stream_cfg->MaxBufNbr;
p_as_if_settings->BufTotalLen = max_mem_blk_len * (p_stream_cfg->ExtraPktTx + 1);
p_as_if_settings->StreamDir = stream_dir;
p_as_if_settings->StreamStarted = DEF_NO;
/* See Note #3. */
p_as_if_settings->StreamPreBufMax = p_stream_cfg->MaxBufNbr / 2u;
p_as_if_settings->StreamPrimingDone = DEF_NO;
USBD_Audio_OS_RingBufQLockCreate(as_if_settings_ix, p_err);
if (*p_err != USBD_ERR_NONE) {
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
p_as_if_settings->CorrPeriod = p_stream_cfg->CorrPeriodMs;
#endif
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_CORR_EN == DEF_ENABLED)
p_as_if_settings->CorrBoundaryHeavyPos = (p_stream_cfg->MaxBufNbr / USBD_AUDIO_STREAM_CORR_BOUNDARY_INTERVAL) * 2u;
p_as_if_settings->CorrBoundaryHeavyNeg = p_as_if_settings->CorrBoundaryHeavyPos * -1;
#endif
if (stream_dir == USBD_AUDIO_STREAM_OUT) {
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED)
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
p_as_if_settings->PlaybackSynch.FeedbackValUpdate = DEF_NO;
/* Alloc synch buffs for AS IF. */
if ((((p_as_cfg->EP_SynchType & USBD_EP_TYPE_SYNC_MASK) == USBD_EP_TYPE_SYNC_ASYNC ) &&
( stream_dir == USBD_AUDIO_STREAM_OUT )) ||
(((p_as_cfg->EP_SynchType & USBD_EP_TYPE_SYNC_MASK) == USBD_EP_TYPE_SYNC_ADAPTIVE) &&
( stream_dir == USBD_AUDIO_STREAM_IN) )) {
p_as_if_settings->PlaybackSynch.SynchBufFree = DEF_YES;
p_as_if_settings->PlaybackSynch.SynchBufPtr = (CPU_INT32U *)Mem_HeapAlloc(sizeof(CPU_INT32U),
USBD_AUDIO_CFG_BUF_ALIGN_OCTETS,
DEF_NULL,
&err_lib);
p_as_if_settings->PlaybackSynch.SynchBoundaryLightPos = p_stream_cfg->MaxBufNbr / USBD_AUDIO_STREAM_CORR_BOUNDARY_INTERVAL;
p_as_if_settings->PlaybackSynch.SynchBoundaryLightNeg = p_as_if_settings->PlaybackSynch.SynchBoundaryLightPos * -1;
}
#endif
#if (USBD_AUDIO_CFG_PLAYBACK_CORR_EN == DEF_ENABLED)
p_as_if_settings->CorrCallbackPtr = corr_callback;
#endif
#endif
}
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
/* Alloc a stat struct from the heap. */
p_as_if_settings->StatPtr = (USBD_AUDIO_STAT *)Mem_HeapAlloc(sizeof(USBD_AUDIO_STAT),
sizeof(CPU_ALIGN),
NULL,
&err_lib);
if (err_lib != LIB_MEM_ERR_NONE) {
*p_err = USBD_ERR_ALLOC;
return ((USBD_AUDIO_AS_IF_HANDLE)0);
}
#endif
*p_err = USBD_ERR_NONE;
return (as_if_handle);
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_AS_IF_Add()
*
* Description : Add the AudioStreaming interface to the given configuration.
*
* Argument(s) : class_nbr Class instance number.
*
* cfg_nbr Configuration number.
*
* as_if_handle Handle to AudioStreaming interface.
*
* p_as_if_cfg Pointer to AudioStreaming interface configuration.
*
* p_as_cfg_name Pointer to AudioStreaming interface name.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE AudioStreaming interface successfully added.
USBD_ERR_CLASS_INVALID_NBR Invalid class number.
* USBD_ERR_NULL_PTR NULL pointer passed to 'p_as_if_cfg'.
* USBD_ERR_INVALID_ARG Invalid 'p_comm' retrieved from 'class_nbr'
* USBD_ERR_AUDIO_AS_IF_ALLOC No more AudioStreaming interface alternate
* setting available.
*
* -RETURNED BY USBD_Audio_AS_IF_Alloc()-
* See USBD_Audio_AS_IF_Alloc() for additional return error codes.
*
* -RETURNED BY USBD_IF_Add()-
* See USBD_IF_Add() for additional return error codes.
*
* -RETURNED BY USBD_IF_AltAdd()-
* See USBD_IF_AltAdd() for additional return error codes.
*
* -RETURNED BY USBD_Audio_MaxPktLenGet()-
* See USBD_Audio_MaxPktLenGet() for additional return error codes.
*
* -RETURNED BY USBD_IsocAdd()-
* See USBD_IsocAdd() for additional return error codes.
*
* -RETURNED BY USBD_IsocSyncAddrSet()-
* See USBD_IsocSyncAddrSet() for additional return error codes.
*
* -RETURNED BY USBD_IsocSyncRefreshSet()-
* See USBD_IsocSyncRefreshSet() for additional return error codes.
**
* Return(s) : None.
*
* Note(s) : (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 3.7.2 for more details about AudioStreaming Interface.
*
* (2) For FS, 1 ms = 1 frame. For HS, 1 ms = 8 microframes = 1 frame.
*
* (3) Regardless of the isochronous endpoint speed (full- or high-speed), the polling
* interval is always 1 ms with Audio 1.0. Hence, the number of transactions per frame
* will always be 1.
*
* (4) Feedback is restricted to Adaptive Source endpoint or Asynchronous Sink endpoint.
* If explicit synchronization mechanism is needed to maintain synchronization during
* transfers, the information carried over the synchronization path must be available
* every 2 ^ (10 - P) frames, with P ranging from 1 to 9 (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_RECORD_EN == DEF_ENABLED)
void USBD_Audio_AS_IF_Add ( CPU_INT08U class_nbr,
CPU_INT08U cfg_nbr,
USBD_AUDIO_AS_IF_HANDLE as_if_handle,
const USBD_AUDIO_AS_IF_CFG *p_as_if_cfg,
const CPU_CHAR *p_as_cfg_name,
USBD_ERR *p_err)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_COMM *p_comm;
USBD_AUDIO_AS_IF *p_as_if;
USBD_AUDIO_AS_ALT_CFG *p_as_cfg;
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
CPU_INT08U if_nbr;
CPU_INT08U if_alt_nbr;
CPU_INT08U data_ep_addr;
CPU_INT08U as_alt_ix;
CPU_INT08U as_alt_ix_global;
CPU_INT16U interval;
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
USBD_AUDIO_STREAM_DIR stream_dir;
CPU_BOOLEAN synch_dir_in;
CPU_INT08U synch_ep_addr;
CPU_INT08U synch_ep_xfer_len;
#endif
CPU_SR_ALLOC();
#if (USBD_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
/* ------------------- VALIDATE ARG ------------------- */
if (p_err == DEF_NULL) { /* Validate error ptr. */
CPU_SW_EXCEPTION(;);
}
CPU_CRITICAL_ENTER();
if (class_nbr >= USBD_Audio_CtrlNbrNext) { /* Check Audio Class nbr. */
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_CLASS_INVALID_NBR;
return;
}
CPU_CRITICAL_EXIT();
if (p_as_if_cfg == DEF_NULL) { /* Check ptr to AS IF cfg. */
*p_err = USBD_ERR_NULL_PTR;
return;
}
#endif
p_ctrl = &USBD_Audio_CtrlTbl[class_nbr]; /* Get audio class instance. */
p_comm = USBD_Audio_CommGet(p_ctrl, cfg_nbr); /* Get comm struct. */
if (p_comm == DEF_NULL) { /* Check if the cfg exist. */
*p_err = USBD_ERR_INVALID_ARG;
return;
}
p_as_if = USBD_Audio_AS_IF_Alloc(p_err); /* Get AS IF struct. */
if (*p_err != USBD_ERR_NONE) {
return;
}
/* ---------------- AUDIO STREAMING IF ---------------- */
/* See Note #1. */
/* Add default AudioStreaming IF 0 to cfg. */
if_nbr = USBD_IF_Add( p_ctrl->DevNbr,
cfg_nbr,
&USBD_Audio_AS_Drv,
(void *)p_as_if,
DEF_NULL,
USBD_CLASS_CODE_AUDIO,
USBD_AUDIO_SUBCLASS_AUDIO_STREAMING,
0u,
"0-Bandwidth AudioStreaming Interface",
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
/* Store the required info for internal use. */
p_as_if->CommPtr = (USBD_AUDIO_COMM *)p_comm;
p_as_if->DevNbr = p_ctrl->DevNbr; /* Store dev nbr assigned by the core. */
p_as_if->ClassNbr = p_ctrl->ClassNbr; /* Store class instance nbr. */
p_as_if->AS_IF_Nbr = if_nbr; /* Store AudioStreaming IF nbr given by the core. */
p_as_if->AS_IF_SettingsPtr = (USBD_AUDIO_AS_IF_SETTINGS *)as_if_handle;
p_as_if->AS_IF_SettingsPtr->DrvInfoPtr = &p_ctrl->DrvInfo; /* Store audio drv info. */
/* Create a lock for this AudioStreaming IF. */
USBD_Audio_OS_AS_IF_LockCreate(USBD_AUDIO_AS_IF_HANDLE_IX_GET(p_as_if->Handle), p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
/* For Audio 1.0, isoc EP interval always 1 ms... */
/* ...(see Note #2). */
if (DEF_BIT_IS_CLR(cfg_nbr, USBD_CFG_NBR_SPD_BIT) == DEF_YES) {
interval = 1u; /* In FS, bInterval in frames. */
} else {
interval = 8u; /* In HS, bInterval in microframes. */
}
for (as_alt_ix = 0u; as_alt_ix < p_as_if_cfg->AS_CfgAltSettingNbr; as_alt_ix++) {
/* ------ AUDIO STREAMING IF ALTERNATIVE SETTING ------ */
/* Add operational alternate if to cfg. */
CPU_CRITICAL_ENTER();
as_alt_ix_global = USBD_Audio_AS_IF_AltNbrNext; /* Alloc new alt setting. */
if (as_alt_ix_global >= USBD_AUDIO_MAX_NBR_IF_ALT) {
CPU_CRITICAL_EXIT();
*p_err = USBD_ERR_AUDIO_AS_IF_ALLOC;
return;
}
USBD_Audio_AS_IF_AltNbrNext++; /* Next avail alt setting nbr. */
CPU_CRITICAL_EXIT();
p_as_if_alt = &USBD_Audio_AS_IF_AltTbl[as_alt_ix_global];
p_as_cfg = p_as_if_cfg->AS_CfgPtrTbl[as_alt_ix];
p_as_if_alt->AS_CfgPtr = p_as_cfg;
if_alt_nbr = USBD_IF_AltAdd( p_ctrl->DevNbr,
cfg_nbr,
if_nbr,
(void *)p_as_if_alt,
p_as_cfg_name,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
/* -------------- MAXIMUM PACKET LENGTH --------------- */
p_as_if_alt->MaxPktLen = USBD_Audio_MaxPktLenGet(p_as_cfg,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
/* ----------- ISOCHRONOUS ENDPOINT ADDRESS ----------- */
/* Add Isoc EP to alternate IF 1. */
data_ep_addr = USBD_IsocAdd(p_ctrl->DevNbr,
cfg_nbr,
if_nbr,
if_alt_nbr,
p_as_cfg->EP_DirIn,
p_as_cfg->EP_SynchType,
p_as_if_alt->MaxPktLen,
USBD_EP_TRANSACTION_PER_UFRAME_1, /* See Note #3. */
interval,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
p_as_if_alt->DataIsocAddr = data_ep_addr;
#if (USBD_AUDIO_CFG_PLAYBACK_FEEDBACK_EN == DEF_ENABLED)
stream_dir = (p_as_cfg->EP_DirIn == DEF_YES) ? USBD_AUDIO_STREAM_IN : USBD_AUDIO_STREAM_OUT;
/* See Note #4. */
if ((((p_as_cfg->EP_SynchType & USBD_EP_TYPE_SYNC_MASK) == USBD_EP_TYPE_SYNC_ASYNC ) &&
( stream_dir == USBD_AUDIO_STREAM_OUT )) ||
(((p_as_cfg->EP_SynchType & USBD_EP_TYPE_SYNC_MASK) == USBD_EP_TYPE_SYNC_ADAPTIVE) &&
( stream_dir == USBD_AUDIO_STREAM_IN) )) {
/* Dir of Synch EP is the opposite of Data EP. */
synch_dir_in = (stream_dir == USBD_AUDIO_STREAM_IN) ? DEF_NO : DEF_YES;
/* Add Synch EP to alternate IF 1. */
if (DEF_BIT_IS_CLR(cfg_nbr, USBD_CFG_NBR_SPD_BIT) == DEF_YES) {
synch_ep_xfer_len = USBD_AUDIO_FS_SYNCH_EP_XFER_LEN;
} else {
synch_ep_xfer_len = USBD_AUDIO_HS_SYNCH_EP_XFER_LEN;
}
synch_ep_addr = USBD_IsocAdd(p_ctrl->DevNbr,
cfg_nbr,
if_nbr,
if_alt_nbr,
synch_dir_in,
USBD_EP_TYPE_SYNC_NONE | USBD_EP_TYPE_USAGE_FEEDBACK,
synch_ep_xfer_len,
USBD_EP_TRANSACTION_PER_UFRAME_1, /* See Note #3. */
interval,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
/* Addr of Synch EP associated to Data EP. */
USBD_IsocSyncAddrSet(p_ctrl->DevNbr,
cfg_nbr,
if_nbr,
if_alt_nbr,
data_ep_addr,
synch_ep_addr,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
p_as_if_alt->SynchIsocAddr = synch_ep_addr;
/* Indicate synch feedback data rate. */
USBD_IsocSyncRefreshSet(p_ctrl->DevNbr,
cfg_nbr,
if_nbr,
if_alt_nbr,
synch_ep_addr,
p_as_cfg->EP_SynchRefresh,
p_err);
if (*p_err != USBD_ERR_NONE) {
return;
}
}
#endif
}
/* ----------- SAVE AS IF STRUCT IN A LIST ------------ */
if (p_comm->AS_IF_HeadPtr == DEF_NULL) { /* First element of list. */
p_comm->AS_IF_HeadPtr = p_as_if;
} else {
p_comm->AS_IF_TailPtr->NextPtr = p_as_if; /* Element at the end of list. */
}
p_comm->AS_IF_TailPtr = p_as_if;
p_comm->AS_IF_Cnt++; /* Increment nbr of AudioStreaming IF. */
*p_err = USBD_ERR_NONE;
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_AS_IF_StatGet()
*
* Description : Get the statistics structure associated to a given AudioStreaming interface.
*
* Argument(s) : as_handle AudioStreaming interface handle.
*
* Return(s) : Pointer to the audio stream statistics.
*
* Note(s) : None.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_STAT_EN == DEF_ENABLED)
USBD_AUDIO_STAT *USBD_Audio_AS_IF_StatGet (USBD_AUDIO_AS_HANDLE as_handle)
{
USBD_AUDIO_STAT *p_as_if_stat;
p_as_if_stat = USBD_Audio_StatGet(as_handle);
return (p_as_if_stat);
}
#endif
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* USBD_Audio_AC_Conn()
*
* Description : Notify class that configuration is active.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_Audio_AC_Conn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg)
{
USBD_AUDIO_COMM *p_comm;
USBD_AUDIO_CTRL *p_ctrl;
CPU_SR_ALLOC();
(void)dev_nbr;
(void)cfg_nbr;
p_comm = (USBD_AUDIO_COMM *)p_if_class_arg;
p_ctrl = p_comm->CtrlPtr;
CPU_CRITICAL_ENTER();
p_ctrl->CommPtr = p_comm;
p_ctrl->State = USBD_AUDIO_STATE_CFG;
CPU_CRITICAL_EXIT();
}
/*
*********************************************************************************************************
* USBD_Audio_AC_Disconn()
*
* Description : Notify class that configuration is not active.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* Return(s) : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
static void USBD_Audio_AC_Disconn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg)
{
USBD_AUDIO_COMM *p_comm;
USBD_AUDIO_CTRL *p_ctrl;
CPU_SR_ALLOC();
p_comm = (USBD_AUDIO_COMM *)p_if_class_arg;
p_ctrl = p_comm->CtrlPtr;
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
{
USBD_AUDIO_AS_IF *p_as_if;
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
CPU_INT08U i;
USBD_ERR err_usbd;
p_as_if = p_comm->AS_IF_HeadPtr;
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
for (i = 0u; i < p_comm->AS_IF_Cnt; i++) {
USBD_Audio_AS_IF_Stop(p_as_if,
&err_usbd);
if (err_usbd != USBD_ERR_NONE) {
USBD_DBG_AUDIO_ERR("AC_Disconn(): cannot stop stream on AS IF w/ err = %d\n", err_usbd);
}
p_as_if = p_as_if->NextPtr;
if (p_ctrl->EventFnctPtr->Disconn != DEF_NULL) { /* Notify app about cfg deactivation by host. */
p_ctrl->EventFnctPtr->Disconn(dev_nbr,
cfg_nbr,
p_as_if_settings->TerminalID,
p_as_if->Handle);
}
}
}
#else
(void)dev_nbr;
(void)cfg_nbr;
#endif
CPU_CRITICAL_ENTER();
p_ctrl->CommPtr = DEF_NULL;
p_ctrl->State = USBD_AUDIO_STATE_INIT;
CPU_CRITICAL_EXIT();
}
/*
*********************************************************************************************************
* USBD_Audio_AC_IF_Desc()
*
* Description : Class interface control descriptor callback for AudioControl interface.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* if_nbr Interface number.
*
* if_alt_nbr Interface alternate number.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* p_if_alt_class_arg Pointer to class argument specific to alternate interface.
*
* Return(s) : None.
*
* Note(s) : (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.3.2 for more details about Class-Specific AC Interface Descriptor.
*
* (2) Total number of bytes returned for the class-specific AudioControl interface
* descriptor. It includes the combined length of this descriptor header and all Unit
* and Terminal descriptors.
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* Table 4-2, 'wTotalLength' field description for more details.
*
* (3) Audio 1.0 specification does NOT define a specific order for the class-specific
* descriptors following the AudioControl interface.
*
* (4) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.3.2.1 for more details about Input Terminal Descriptor.
*
* (5) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.3.2.2 for more details about Output Terminal Descriptor.
*
* (6) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.3.2.5 for more details about Feature Unit Descriptor.
*
* (a) The size of the Feature Unit Descriptor corresponds to 7+(ch+1)*n where ch is
* number of logical channels and n is the size in bytes of each bmaControls.
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* Table 4-7, 'bLength' field description for more details.
*
* (7) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.3.2.3 for more details about Mixer Unit Descriptor.
*
* (a) The size of the Mixer Unit Descriptor corresponds to 10+p+N where p is the number
* of Input Pins and N is the number of bytes to use to store the bit Array.
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* Table 4-5, 'bLength' field description for more details.
*
* (8) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.3.2.4 for more details about Selector Unit Descriptor.
*
* (a) The size of the Selector Unit Descriptor corresponds to 6+p where p is number of
* Input Pins.
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* Table 4-6, 'bLength' field description for more details.
*********************************************************************************************************
*/
static void USBD_Audio_AC_IF_Desc (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_COMM *p_comm;
USBD_AUDIO_IT *p_it;
USBD_AUDIO_OT *p_ot;
USBD_AUDIO_FU *p_fu;
USBD_AUDIO_MU *p_mu;
USBD_AUDIO_SU *p_su;
USBD_AUDIO_AS_IF *p_as_if;
USBD_AUDIO_IT_CFG *p_it_cfg;
USBD_AUDIO_OT_CFG *p_ot_cfg;
USBD_AUDIO_FU_CFG *p_fu_cfg;
USBD_AUDIO_MU_CFG *p_mu_cfg;
USBD_AUDIO_SU_CFG *p_su_cfg;
CPU_INT08U desc_size;
CPU_INT16U total_len;
CPU_INT08U ix;
CPU_INT08U ctrl_ix;
CPU_INT08U src_ix;
CPU_INT08U str_ix;
USBD_AUDIO_ENTITY_TYPE entity_type;
(void)dev_nbr;
(void)cfg_nbr;
(void)if_nbr;
(void)if_alt_nbr;
(void)p_if_alt_class_arg;
p_comm = (USBD_AUDIO_COMM *)p_if_class_arg;
p_ctrl = p_comm->CtrlPtr;
/* ----------------- AC IF HEADER DESC ---------------- */
/* See Note #1. */
desc_size = USBD_AUDIO_DESC_LEN_AC_HEADER_MIN + p_comm->AS_IF_Cnt;
USBD_DescWr08(p_ctrl->DevNbr, desc_size);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_TYPE_CS_IF);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_SUBTYPE_AC_HEADER);
USBD_DescWr16(p_ctrl->DevNbr, 0x0100u);
total_len = USBD_Audio_AC_IF_DescSizeGet(dev_nbr,
cfg_nbr,
if_nbr,
if_alt_nbr,
p_if_class_arg,
p_if_alt_class_arg);
USBD_DescWr16(p_ctrl->DevNbr, total_len); /* See Note #2. */
USBD_DescWr08(p_ctrl->DevNbr, p_comm->AS_IF_Cnt);
p_as_if = p_comm->AS_IF_HeadPtr;
for (ix = 0u; ix < p_comm->AS_IF_Cnt; ix++) {
USBD_DescWr08(p_ctrl->DevNbr, p_as_if->AS_IF_Nbr);
p_as_if = p_as_if->NextPtr;
}
for (ix = 0u; ix < p_ctrl->EntityCnt; ix++) {
entity_type = p_ctrl->EntityID_TblPtr[ix]->EntityType;
switch (entity_type) { /* See Note #3. */
case USBD_AUDIO_ENTITY_IT: /* ---------------- INPUT TERMINAL DESC --------------- */
/* See Note #4. */
p_it = (USBD_AUDIO_IT *)p_ctrl->EntityID_TblPtr[ix];
p_it_cfg = p_it->IT_CfgPtr;
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_LEN_AC_IT);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_TYPE_CS_IF);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_SUBTYPE_IT);
USBD_DescWr08(p_ctrl->DevNbr, p_it->ID);
USBD_DescWr16(p_ctrl->DevNbr, p_it_cfg->TerminalType);
USBD_DescWr08(p_ctrl->DevNbr, p_it->AssociatedOT_ID);
USBD_DescWr08(p_ctrl->DevNbr, p_it_cfg->LogChNbr);
USBD_DescWr16(p_ctrl->DevNbr, p_it_cfg->LogChCfg);
USBD_DescWr08(p_ctrl->DevNbr, 0u);
str_ix = USBD_StrIxGet(p_ctrl->DevNbr, p_it_cfg->StrPtr);
USBD_DescWr08(p_ctrl->DevNbr, str_ix);
break;
case USBD_AUDIO_ENTITY_OT: /* ---------------- OUTPUT TERMINAL DESC -------------- */
/* See Note #5. */
p_ot = (USBD_AUDIO_OT *)p_ctrl->EntityID_TblPtr[ix];
p_ot_cfg = p_ot->OT_CfgPtr;
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_LEN_AC_OT);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_TYPE_CS_IF);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_SUBTYPE_OT);
USBD_DescWr08(p_ctrl->DevNbr, p_ot->ID);
USBD_DescWr16(p_ctrl->DevNbr, p_ot_cfg->TerminalType);
USBD_DescWr08(p_ctrl->DevNbr, p_ot->AssociatedIT_ID);
USBD_DescWr08(p_ctrl->DevNbr, p_ot->SourceID);
str_ix = USBD_StrIxGet(p_ctrl->DevNbr, p_ot_cfg->StrPtr);
USBD_DescWr08(p_ctrl->DevNbr, str_ix);
break;
case USBD_AUDIO_ENTITY_FU: /* ---------------- FEATURE UNIT DESC ----------------- */
/* See Note #6. */
p_fu = (USBD_AUDIO_FU *)p_ctrl->EntityID_TblPtr[ix];
p_fu_cfg = p_fu->FU_CfgPtr;
/* See Note #6a. */
desc_size = USBD_AUDIO_DESC_LEN_AC_FU_MIN + (p_fu_cfg->LogChNbr + 1) * 2u;
USBD_DescWr08(p_ctrl->DevNbr, desc_size);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_TYPE_CS_IF);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_SUBTYPE_FU);
USBD_DescWr08(p_ctrl->DevNbr, p_fu->ID);
USBD_DescWr08(p_ctrl->DevNbr, p_fu->SourceID);
USBD_DescWr08(p_ctrl->DevNbr, 2u);
for (ctrl_ix = 0; ctrl_ix < (p_fu_cfg->LogChNbr + 1); ctrl_ix++) {
USBD_DescWr16(p_ctrl->DevNbr, p_fu_cfg->LogChCtrlPtr[ctrl_ix]);
}
str_ix = USBD_StrIxGet(p_ctrl->DevNbr, p_fu_cfg->StrPtr);
USBD_DescWr08(p_ctrl->DevNbr, str_ix);
break;
case USBD_AUDIO_ENTITY_MU: /* ----------------- MIXER UNIT DESC ------------------ */
/* See Note #7. */
p_mu = (USBD_AUDIO_MU *)p_ctrl->EntityID_TblPtr[ix];
p_mu_cfg = p_mu->MU_CfgPtr;
/* See Note #7a. */
desc_size = USBD_AUDIO_DESC_LEN_AC_MU_MIN + p_mu_cfg->NbrInPins + p_mu->ControlsSize;
USBD_DescWr08(p_ctrl->DevNbr, desc_size);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_TYPE_CS_IF);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_SUBTYPE_MU);
USBD_DescWr08(p_ctrl->DevNbr, p_mu->ID);
USBD_DescWr08(p_ctrl->DevNbr, p_mu_cfg->NbrInPins);
for (src_ix = 0u; src_ix < p_mu_cfg->NbrInPins; src_ix++) {
USBD_DescWr08(p_ctrl->DevNbr, p_mu->SourceID_TblPtr[src_ix]);
}
USBD_DescWr08(p_ctrl->DevNbr, p_mu_cfg->LogOutChNbr);
USBD_DescWr16(p_ctrl->DevNbr, p_mu_cfg->LogOutChCfg);
USBD_DescWr08(p_ctrl->DevNbr, 0u);
for (ctrl_ix = 0u; ctrl_ix < p_mu->ControlsSize; ctrl_ix++) {
USBD_DescWr08(p_ctrl->DevNbr, p_mu->ControlsTblPtr[ctrl_ix]);
}
str_ix = USBD_StrIxGet(p_ctrl->DevNbr, p_mu_cfg->StrPtr);
USBD_DescWr08(p_ctrl->DevNbr, str_ix);
break;
case USBD_AUDIO_ENTITY_SU: /* ---------------- SELECTOR UNIT DESC ---------------- */
/* See Note #8. */
p_su = (USBD_AUDIO_SU *)p_ctrl->EntityID_TblPtr[ix];
p_su_cfg = p_su->SU_CfgPtr;
/* See Note #8a. */
desc_size = USBD_AUDIO_DESC_LEN_AC_SU_MIN + p_su_cfg->NbrInPins;
USBD_DescWr08(p_ctrl->DevNbr, desc_size);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_TYPE_CS_IF);
USBD_DescWr08(p_ctrl->DevNbr, USBD_AUDIO_DESC_SUBTYPE_SU);
USBD_DescWr08(p_ctrl->DevNbr, p_su->ID);
USBD_DescWr08(p_ctrl->DevNbr, p_su_cfg->NbrInPins);
for (src_ix = 0u; src_ix < p_su_cfg->NbrInPins; src_ix++) {
USBD_DescWr08(p_ctrl->DevNbr, p_su->SourceID_TblPtr[src_ix]);
}
str_ix = USBD_StrIxGet(p_ctrl->DevNbr, p_su_cfg->StrPtr);
USBD_DescWr08(p_ctrl->DevNbr, str_ix);
break;
default:
break;
}
}
}
/*
*********************************************************************************************************
* USBD_Audio_AC_IF_DescSizeGet()
*
* Description : Retrieve the size of the class interface descriptor for AudioControl interface.
* (See Note #1.)
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* if_nbr Interface number.
*
* if_alt_nbr Interface alternate number.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* p_if_alt_class_arg Pointer to class argument specific to alternate interface.
*
* Return(s) : Size of the class interface descriptor.
*
* Note(s) : (1) Total number of bytes returned for the class-specific AudioControl interface
* descriptor. It includes the combined length of this descriptor header and all Unit
* and Terminal descriptors.
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* Table 4-2, 'wTotalLength' field description for more details.
*********************************************************************************************************
*/
static CPU_INT16U USBD_Audio_AC_IF_DescSizeGet (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg)
{
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_COMM *p_comm;
USBD_AUDIO_FU *p_fu;
USBD_AUDIO_MU *p_mu;
USBD_AUDIO_SU *p_su;
CPU_INT16U desc_size;
CPU_INT08U ix;
USBD_AUDIO_ENTITY_TYPE entity_type;
(void)dev_nbr;
(void)cfg_nbr;
(void)if_nbr;
(void)if_alt_nbr;
(void)p_if_alt_class_arg;
p_comm = (USBD_AUDIO_COMM *)p_if_class_arg;
p_ctrl = p_comm->CtrlPtr;
desc_size = USBD_AUDIO_DESC_LEN_AC_HEADER_MIN + p_comm->AS_IF_Cnt;
for (ix = 0u; ix < p_ctrl->EntityCnt; ix++) {
entity_type = p_ctrl->EntityID_TblPtr[ix]->EntityType;
switch (entity_type) {
case USBD_AUDIO_ENTITY_IT:
desc_size += USBD_AUDIO_DESC_LEN_AC_IT;
break;
case USBD_AUDIO_ENTITY_OT:
desc_size += USBD_AUDIO_DESC_LEN_AC_OT;
break;
case USBD_AUDIO_ENTITY_FU:
p_fu = (USBD_AUDIO_FU *)p_ctrl->EntityID_TblPtr[ix];
desc_size += (USBD_AUDIO_DESC_LEN_AC_FU_MIN + (p_fu->FU_CfgPtr->LogChNbr + 1u) * 2u);
break;
case USBD_AUDIO_ENTITY_MU:
p_mu = (USBD_AUDIO_MU *)p_ctrl->EntityID_TblPtr[ix];
desc_size += (USBD_AUDIO_DESC_LEN_AC_MU_MIN + p_mu->MU_CfgPtr->NbrInPins + p_mu->ControlsSize);
break;
case USBD_AUDIO_ENTITY_SU:
p_su = (USBD_AUDIO_SU *)p_ctrl->EntityID_TblPtr[ix];
desc_size += (USBD_AUDIO_DESC_LEN_AC_SU_MIN + p_su->SU_CfgPtr->NbrInPins);
break;
default:
break;
}
}
return (desc_size);
}
/*
*********************************************************************************************************
* USBD_Audio_AC_ClassReq()
*
* Description : Process class-specific requests for AudioControl interface. (see Note #1)
*
* Argument(s) : dev_nbr Device number.
*
* p_setup_req Pointer to setup request structure.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
*
* DEF_FAIL, otherwise.
*
* Note(s) : (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 5.2.2 for more details about AudioControl Requests.
*
* (2) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 5.2.1.1 for more details about Set Request.
*
* (3) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 5.2.1.2 for more details about Get Request.
*********************************************************************************************************
*/
static CPU_BOOLEAN USBD_Audio_AC_ClassReq ( CPU_INT08U dev_nbr,
const USBD_SETUP_REQ *p_setup_req,
void *p_if_class_arg)
{
USBD_AUDIO_COMM *p_comm;
USBD_AUDIO_CTRL *p_ctrl;
USBD_ERR err;
USBD_AUDIO_ENTITY_TYPE recipient;
void *p_recipient_info;
CPU_INT08U entity_id;
CPU_INT16U req_len;
CPU_BOOLEAN dev_to_host;
switch (p_setup_req->bRequest) { /* Discard class req not supported by this class. */
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:
case USBD_AUDIO_REQ_SET_MIN:
case USBD_AUDIO_REQ_SET_MAX:
case USBD_AUDIO_REQ_SET_RES:
break;
case USBD_AUDIO_REQ_GET_STAT:
case USBD_AUDIO_REQ_GET_MEM:
case USBD_AUDIO_REQ_SET_MEM:
default:
return (DEF_FAIL); /* Class req not supported. Ctrl EP will be stalled. */
}
req_len = p_setup_req->wLength; /* Length of param block. */
if (req_len > USBD_AUDIO_CFG_CLASS_REQ_MAX_LEN) { /* Check if enough room to process class req data. */
return (DEF_FAIL);
}
p_comm = (USBD_AUDIO_COMM *)p_if_class_arg;
p_ctrl = p_comm->CtrlPtr;
dev_to_host = DEF_BIT_IS_SET(p_setup_req->bmRequestType, USBD_REQ_DIR_BIT);
entity_id = (p_setup_req->wIndex & USBD_AUDIO_REQ_ENTITY_ID_MASK) >> 8u;
p_recipient_info = USBD_Audio_AC_RecipientGet(p_ctrl, entity_id, &recipient);
if (recipient == USBD_AUDIO_ENTITY_UNKNOWN) {
return (DEF_FAIL);
}
/* -------------------- SET REQUEST ------------------- */
/* See Note #2. */
if (dev_to_host == DEF_NO) {
USBD_CtrlRx( dev_nbr, /* Receive param block from host. */
(void *)p_ctrl->ReqBufPtr,
req_len,
USBD_AUDIO_CTRL_REQ_TIMEOUT_mS,
&err);
if (err != USBD_ERR_NONE) {
USBD_DBG_AUDIO_ERR("AC (SET req): Ctrl Rx fails w/ err = %d\n", err);
return (DEF_FAIL);
}
}
/* ------------------ REQ EXECUTION ------------------- */
switch (recipient) {
case USBD_AUDIO_ENTITY_IT:
case USBD_AUDIO_ENTITY_OT:
USBD_Audio_AC_TerminalCtrl(entity_id,
recipient,
p_recipient_info,
p_setup_req->bRequest,
p_setup_req->wValue,
p_ctrl->ReqBufPtr,
req_len,
&err);
break;
case USBD_AUDIO_ENTITY_FU:
case USBD_AUDIO_ENTITY_MU:
case USBD_AUDIO_ENTITY_SU:
USBD_Audio_AC_UnitCtrl(entity_id,
recipient,
p_recipient_info,
p_setup_req->bRequest,
p_setup_req->wValue,
p_ctrl->ReqBufPtr,
req_len,
&err);
break;
default:
err = USBD_ERR_FAIL;
break;
}
if (err != USBD_ERR_NONE) {
return (DEF_FAIL);
}
/* -------------------- GET REQUESTS ------------------ */
/* See Note #3. */
if (dev_to_host == DEF_YES) {
USBD_CtrlTx( dev_nbr, /* Send param block to host. */
(void *)p_ctrl->ReqBufPtr,
req_len,
USBD_AUDIO_CTRL_REQ_TIMEOUT_mS,
DEF_NO,
&err);
if (err != USBD_ERR_NONE) {
return (DEF_FAIL);
}
}
return (DEF_OK);
}
/*
*********************************************************************************************************
* USBD_Audio_AS_Conn()
*
* Description : Notify class that configuration is active.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* 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_Conn (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
void *p_if_class_arg)
{
USBD_AUDIO_AS_IF *p_as_if;
USBD_AUDIO_CTRL *p_ctrl;
p_as_if = (USBD_AUDIO_AS_IF *)p_if_class_arg;
p_ctrl = p_as_if->CommPtr->CtrlPtr;
/* Notify app about cfg activation by host. */
if (p_ctrl->EventFnctPtr->Conn != DEF_NULL) {
p_ctrl->EventFnctPtr->Conn(dev_nbr,
cfg_nbr,
p_as_if->AS_IF_SettingsPtr->TerminalID,
p_as_if->Handle);
}
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_AS_AltSettingUpdate()
*
* Description : Notify class that interface alternate setting has been updated.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* if_nbr Interface number.
*
* if_alt_nbr Interface alternate setting number.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* p_if_alt_class_arg Pointer to class argument specific to alternate interface.
*
* 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_AltSettingUpdate (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg)
{
USBD_AUDIO_AS_IF *p_as_if;
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
USBD_AUDIO_AS_IF_SETTINGS *p_as_if_settings;
USBD_ERR err_usbd;
(void)dev_nbr;
(void)cfg_nbr;
(void)if_nbr;
p_as_if = (USBD_AUDIO_AS_IF *)p_if_class_arg;
p_as_if_alt = (USBD_AUDIO_AS_IF_ALT *)p_if_alt_class_arg;
p_as_if_settings = p_as_if->AS_IF_SettingsPtr;
if (p_as_if->AS_IF_Nbr == 0u) { /* Should not be an AudioControl interface. */
return;
}
/* Stop current streaming on specified IF nbr if a ... */
/* ...previous one was already started. */
if (p_as_if->AS_IF_AltCurPtr != DEF_NULL) {
USBD_Audio_AS_IF_Stop(p_as_if,
&err_usbd);
if (err_usbd != USBD_ERR_NONE) {
USBD_DBG_AUDIO_ERR("AS_UpdateAltSetting(): cannot stop stream on AS IF w/ err = %d\n", err_usbd);
return;
}
}
if (if_alt_nbr == 0u) { /* Default IF has NO EP. */
p_as_if->AS_IF_AltCurPtr = DEF_NULL; /* Reset the current alt setting. */
} else { /* Operational IF. */
p_as_if->AS_IF_AltCurPtr = p_as_if_alt; /* Select Isoc EP associated with specified alt nbr. */
/* Start streaming on new alt setting. */
if (p_as_if_settings->StreamDir == USBD_AUDIO_STREAM_OUT) {
USBD_Audio_AS_IF_Start(p_as_if,
&err_usbd);
if (err_usbd != USBD_ERR_NONE) {
USBD_DBG_AUDIO_ERR("AS_UpdateAltSetting(): cannot start stream on AS IF w/ err = %d\n", err_usbd);
}
}
}
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_AS_IF_Desc()
*
* Description : Class interface descriptor callback for AudioStreaming interface.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* if_nbr Interface number.
*
* if_alt_nbr Interface alternate setting number.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* p_if_alt_class_arg Pointer to class argument specific to alternate interface.
*
* Return(s) : None.
*
* Note(s) : (1) AudioStreaming interface alternate setting 0 is a zero-bandwidth interface. It is not
* followed by class-specific descriptors.
* See 'USB Audio Device Class Specification for Basic Audio Devices, Release 1.0,
* November 24, 2009', section 5.3.3.3.1 for more details.
*
* (2) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.5.2 for more details about Class-Specific AS Interface Descriptor.
*
* (3) See 'USB Device Class Definition for Audio Data Formats, Release 1.0, March 18, 1998',
* section 2.2.5 for more details about Type I Format Type Descriptor.
*
* (a) Size of the descriptor correspond to 8+(ns*3) where ns is the number of sampling
* frequencies.
* See 'USB Device Class Definition for Audio Data Formats, Release 1.0, March 18, 1998',
* Table 2-1, 'bLength' field description for more details.
*
* (4) A sampling frequency value holds on 3 bytes according to Audio 1.0 specification.
* The use of a temporary buffer ensures to correctly write the sampling frequency value
* into the descriptor Type I Format descriptor regardless of the CPU endianness.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
static void USBD_Audio_AS_IF_Desc (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg)
{
USBD_AUDIO_AS_IF *p_as_if;
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
USBD_AUDIO_AS_ALT_CFG *p_as_cfg;
CPU_INT08U desc_size;
CPU_INT08U sam_freq_ix;
(void)dev_nbr;
(void)cfg_nbr;
(void)if_nbr;
if (if_alt_nbr == 0u) {
return; /* See Note #1. */
}
p_as_if = (USBD_AUDIO_AS_IF *)p_if_class_arg;
p_as_if_alt = (USBD_AUDIO_AS_IF_ALT *)p_if_alt_class_arg;
p_as_cfg = p_as_if_alt->AS_CfgPtr;
/* ----------------- AS IF GENERAL DESC --------------- */
/* See Note #2. */
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_DESC_LEN_AS_GENERAL);
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_DESC_TYPE_CS_IF);
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_DESC_SUBTYPE_AS_GENERAL);
USBD_DescWr08(p_as_if->DevNbr, p_as_if->AS_IF_SettingsPtr->TerminalID);
USBD_DescWr08(p_as_if->DevNbr, p_as_cfg->Dly);
USBD_DescWr16(p_as_if->DevNbr, p_as_cfg->FmtTag);
/* ------------------ TYPE I FMT DESC ----------------- */
/* See Note #3 & 3a. */
if (p_as_cfg->NbrSamplingFreq == 0u) { /* Continuous sampling freq. */
desc_size = USBD_AUDIO_DESC_LEN_TYPE_I_FMT_MIN + (USBD_AUDIO_FMT_TYPE_I_CONT_FREQ_NBR * USBD_AUDIO_DESC_LEN_TYPE_I_SAM_FREQ);
} else { /* Discrete sampling frequencies. */
desc_size = USBD_AUDIO_DESC_LEN_TYPE_I_FMT_MIN + (p_as_cfg->NbrSamplingFreq * USBD_AUDIO_DESC_LEN_TYPE_I_SAM_FREQ);
}
USBD_DescWr08(p_as_if->DevNbr, desc_size);
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_DESC_TYPE_CS_IF);
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_DESC_SUBTYPE_FMT_TYPE);
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_FMT_TYPE_I);
USBD_DescWr08(p_as_if->DevNbr, p_as_cfg->NbrCh);
USBD_DescWr08(p_as_if->DevNbr, p_as_cfg->SubframeSize);
USBD_DescWr08(p_as_if->DevNbr, p_as_cfg->BitRes);
USBD_DescWr08(p_as_if->DevNbr, p_as_cfg->NbrSamplingFreq);
if (p_as_cfg->NbrSamplingFreq == 0u) { /* Continuous sampling freq (see Note #4). */
USBD_DescWr24(p_as_if->DevNbr, p_as_cfg->LowerSamplingFreq);
USBD_DescWr24(p_as_if->DevNbr, p_as_cfg->UpperSamplingFreq);
} else { /* Discrete sampling frequencies (see Note #4). */
for (sam_freq_ix = 0u; sam_freq_ix < p_as_cfg->NbrSamplingFreq; sam_freq_ix++) {
USBD_DescWr24(p_as_if->DevNbr, p_as_cfg->SamplingFreqTblPtr[sam_freq_ix]);
}
}
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_AS_IF_DescSizeGet()
*
* Description : Retrieve the size of the class interface descriptor for AudioStreaming interface.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* if_nbr Interface number.
*
* if_alt_nbr Interface alternate setting number.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* p_if_alt_class_arg Pointer to class argument specific to alternate interface.
*
* Return(s) : Size of the class interface descriptor.
*
* Note(s) : (1) See 'USB Device Class Definition for Audio Data Formats, Release 1.0, March 18, 1998',
* section 2.2.5 for more details about Type I Format Type Descriptor.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
static CPU_INT16U USBD_Audio_AS_IF_DescSizeGet (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
void *p_if_class_arg,
void *p_if_alt_class_arg)
{
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
USBD_AUDIO_AS_ALT_CFG *p_as_cfg;
CPU_INT16U desc_size;
(void)dev_nbr;
(void)cfg_nbr;
(void)if_nbr;
(void)p_if_class_arg;
if (if_alt_nbr == 0u) { /* Alt setting 0 has no class-specific desc. */
return (0u);
}
p_as_if_alt = (USBD_AUDIO_AS_IF_ALT *)p_if_alt_class_arg;
p_as_cfg = p_as_if_alt->AS_CfgPtr;
desc_size = USBD_AUDIO_DESC_LEN_AS_GENERAL;
if (p_as_cfg->NbrSamplingFreq == 0u) { /* Continuous sampling freq. */
desc_size += (USBD_AUDIO_DESC_LEN_TYPE_I_FMT_MIN + (USBD_AUDIO_FMT_TYPE_I_CONT_FREQ_NBR * USBD_AUDIO_DESC_LEN_TYPE_I_SAM_FREQ));
} else { /* Discrete sampling frequencies. */
/* See Note #1. */
desc_size += (USBD_AUDIO_DESC_LEN_TYPE_I_FMT_MIN + (p_as_cfg->NbrSamplingFreq * USBD_AUDIO_DESC_LEN_TYPE_I_SAM_FREQ));
}
return (desc_size);
}
#endif
/*
**********************************************************************************************************
* USBD_Audio_AS_EP_Desc()
*
* Description : Write class-specific endpoint descriptor for AudioStreaming interface.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* if_nbr Interface number.
*
* if_alt_nbr Interface alternate setting number.
*
* ep_addr Endpoint address.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* p_if_alt_class_arg Pointer to class argument specific to alternate interface.
*
* Return(s) : None.
*
* Note(s) : (1) AudioStreaming interface alternate setting 0 has NO endpoints.
* See 'USB Audio Device Class Specification for Basic Audio Devices, Release 1.0,
* November 24, 2009', section 5.3.3.3.1 for more details.
*
* (2) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 4.6.1.2 for more details about Class-Specific AS Isochronous Audio Data
* Endpoint Descriptor.
**********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
static void USBD_Audio_AS_EP_Desc (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
CPU_INT08U ep_addr,
void *p_if_class_arg,
void *p_if_alt_class_arg)
{
USBD_AUDIO_AS_IF *p_as_if;
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
USBD_AUDIO_AS_ALT_CFG *p_as_cfg;
(void)dev_nbr;
(void)cfg_nbr;
(void)if_nbr;
if (if_alt_nbr == 0u) {
return; /* See Note #1. */
}
p_as_if = (USBD_AUDIO_AS_IF *)p_if_class_arg;
p_as_if_alt = (USBD_AUDIO_AS_IF_ALT *)p_if_alt_class_arg;
if (p_as_if_alt->SynchIsocAddr == ep_addr) { /* Synch EP does NOT have class-specific desc. */
return;
}
p_as_cfg = p_as_if_alt->AS_CfgPtr;
/* -------------- DATA EP GENERAL DESC ---------------- */
/* See Note #2. */
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_DESC_LEN_EP_GENERAL);
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_DESC_TYPE_CS_EP);
USBD_DescWr08(p_as_if->DevNbr, USBD_AUDIO_DESC_SUBTYPE_EP_GENERAL);
USBD_DescWr08(p_as_if->DevNbr, p_as_cfg->EP_Attrib);
USBD_DescWr08(p_as_if->DevNbr, p_as_cfg->EP_LockDlyUnits);
USBD_DescWr16(p_as_if->DevNbr, p_as_cfg->EP_LockDly);
}
#endif
/*
**********************************************************************************************************
* USBD_Audio_AS_EP_DescSizeGet()
*
* Description : Return size of class-specific endpoint descriptor for AudioStreaming interface.
*
* Argument(s) : dev_nbr Device number.
*
* cfg_nbr Configuration number.
*
* if_nbr Interface number.
*
* if_alt_nbr Interface alternate setting number.
*
* ep_addr Endpoint address.
*
* p_if_class_arg Pointer to class argument specific to interface.
*
* p_if_alt_class_arg Pointer to class argument specific to alternate interface.
*
* Return(s) : Size of class-specific endpoint descriptor in octets.
*
* Note(s) : None.
**********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
static CPU_INT16U USBD_Audio_AS_EP_DescSizeGet (CPU_INT08U dev_nbr,
CPU_INT08U cfg_nbr,
CPU_INT08U if_nbr,
CPU_INT08U if_alt_nbr,
CPU_INT08U ep_addr,
void *p_if_class_arg,
void *p_if_alt_class_arg)
{
CPU_INT16U desc_size;
USBD_AUDIO_AS_IF_ALT *p_as_if_alt;
(void)dev_nbr;
(void)cfg_nbr;
(void)if_nbr;
(void)p_if_class_arg;
if (if_alt_nbr == 0u) { /* Alt setting 0 has no class-specific desc. */
return (0u);
}
p_as_if_alt = (USBD_AUDIO_AS_IF_ALT *)p_if_alt_class_arg;
if (p_as_if_alt->SynchIsocAddr == ep_addr) { /* Synch EP does NOT have class-specific desc. */
return (0u);
}
desc_size = USBD_AUDIO_DESC_LEN_EP_GENERAL;
return (desc_size);
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_AS_ClassReq()
*
* Description : Process class-specific requests for AudioStreaming interface (see Note #1).
*
* Argument(s) : dev_nbr Device number.
*
* p_setup_req Pointer to setup request structure.
*
* p_if_class_arg Pointer to class argument.
*
* Return(s) : DEF_OK, if NO error(s) occurred and request is supported.
*
* DEF_FAIL, otherwise.
*
* Note(s) : (1) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 5.2.1.1 for more details about Set Request.
*
* (2) Requests intended for the AudioStreaming interface deals ONLY with audio data format-
* specific requests. Depending on the Audio Data Format used by the interface
* (reflected in the wFormatTag field of the class-specific AS interface descriptor),
* the set of requests to control the interface may vary.
* According to 'USB Device Class Definition for Audio Data Formats, Release 1.0,
* March 18, 1998' specification, Type I data format does not define any AudioStreaming
* interface requests. Since the current Audio class implementation supports only Type I
* data format , all the requests will be stalled.
*
* See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 5.2.3.1 for more details about AudioStreaming interface Control requests.
*
* (3) See 'USB Device Class Definition for Audio Devices, Release 1.0, March 18, 1998',
* section 5.2.1.2 for more details about Get Request.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
static CPU_BOOLEAN USBD_Audio_AS_ClassReq ( CPU_INT08U dev_nbr,
const USBD_SETUP_REQ *p_setup_req,
void *p_if_class_arg)
{
USBD_AUDIO_COMM *p_comm;
USBD_AUDIO_CTRL *p_ctrl;
USBD_AUDIO_AS_ALT_CFG *p_as_cfg;
USBD_AUDIO_AS_IF *p_as_if;
USBD_AUDIO_AS_IF_ALT *p_as_alt;
USBD_ERR err = USBD_ERR_NONE;
CPU_INT08U recipient;
CPU_INT16U req_len;
CPU_BOOLEAN dev_to_host;
CPU_INT16U ep_addr;
switch (p_setup_req->bRequest) { /* Discard class req not supported by this class. */
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:
case USBD_AUDIO_REQ_SET_MIN:
case USBD_AUDIO_REQ_SET_MAX:
case USBD_AUDIO_REQ_SET_RES:
break;
case USBD_AUDIO_REQ_GET_STAT:
case USBD_AUDIO_REQ_GET_MEM:
case USBD_AUDIO_REQ_SET_MEM:
default:
return (DEF_FAIL); /* Class req not supported. Ctrl EP will be stalled. */
}
req_len = p_setup_req->wLength; /* Length of param block. */
if (req_len > USBD_AUDIO_CFG_CLASS_REQ_MAX_LEN) { /* Check if enough room to process class req data. */
return (DEF_FAIL);
}
p_as_if = (USBD_AUDIO_AS_IF *)p_if_class_arg;
p_comm = p_as_if->CommPtr;
p_ctrl = p_comm->CtrlPtr;
dev_to_host = DEF_BIT_IS_SET(p_setup_req->bmRequestType, USBD_REQ_DIR_BIT);
recipient = p_setup_req->bmRequestType & USBD_REQ_RECIPIENT_MASK;
/* -------------------- SET REQUEST ------------------- */
/* See Note #1. */
if (dev_to_host == DEF_NO) {
/* Receive param block from host. */
USBD_CtrlRx( dev_nbr,
(void *)p_ctrl->ReqBufPtr,
req_len,
USBD_AUDIO_CTRL_REQ_TIMEOUT_mS,
&err);
if (err != USBD_ERR_NONE) {
return (DEF_FAIL);
}
}
/* ------------------ REQ EXECUTION ------------------- */
switch (recipient) {
case USBD_REQ_RECIPIENT_ENDPOINT:
ep_addr = p_setup_req->wIndex;
p_as_alt = p_as_if->AS_IF_AltCurPtr;
if ((p_as_alt == DEF_NULL) &&
(p_as_alt->DataIsocAddr != ep_addr)) {
return (DEF_FAIL);
}
p_as_cfg = p_as_alt->AS_CfgPtr;
USBD_Audio_AS_EP_CtrlProcess(p_as_if,
p_as_cfg,
ep_addr,
p_setup_req->bRequest,
p_setup_req->wValue,
p_ctrl->ReqBufPtr,
req_len,
&err);
break;
case USBD_REQ_RECIPIENT_INTERFACE:
default:
err = USBD_ERR_AUDIO_REQ_INVALID_CTRL; /* See Note #2. */
break;
}
if (err != USBD_ERR_NONE) {
return (DEF_FAIL);
}
/* -------------------- GET REQUEST ------------------- */
/* See Note #3. */
if (dev_to_host == DEF_YES) {
USBD_CtrlTx( dev_nbr, /* Send param block to host. */
(void *)p_ctrl->ReqBufPtr,
req_len,
USBD_AUDIO_CTRL_REQ_TIMEOUT_mS,
DEF_NO,
&err);
if (err != USBD_ERR_NONE) {
return (DEF_FAIL);
}
}
return (DEF_OK);
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_AC_RecipientGet()
*
* Description : Get the entity recipient of the class request (see Note #1).
*
* Argument(s) : p_ctrl Pointer to the class instance control structure.
*
* entity_id Terminal/Unit ID.
*
* p_recipient_type Pointer to the recipient Entity type.
*
* Return(s) : Pointer to the recipient information structure, if NO error(s).
*
* Null Pointer, otherwise.
*
* Note(s) : (1) The entity can be:
*
* (a) a Terminal (Input or Output).
*
* (b) an Unit (Feature, Mixer or Selector).
*
* (2) A 'virtual' Entity IF represents the entire global AudioControl interface. According
* to Audio 1.0 specification. This can be used to report global AudioControl
* interface changes to the host. This type of entity is not supported by the current
* audio class.
*
* (3) The audio class assigns IDs to terminals and units between 1 and the total number of
* entities composing the audio function. Any entity ID greater than the total number of
* entities is unknown.
*********************************************************************************************************
*/
static void *USBD_Audio_AC_RecipientGet (const USBD_AUDIO_CTRL *p_ctrl,
CPU_INT08U entity_id,
USBD_AUDIO_ENTITY_TYPE *p_recipient_type)
{
void *p_recipient_info;
USBD_AUDIO_IT *p_it;
USBD_AUDIO_OT *p_ot;
USBD_AUDIO_FU *p_fu;
USBD_AUDIO_MU *p_mu;
USBD_AUDIO_SU *p_su;
if (entity_id == 0u) {
*p_recipient_type = USBD_AUDIO_ENTITY_UNKNOWN; /* Entity is a 'virtual' Entity IF (see Note #2). */
return (DEF_NULL);
}
if (entity_id > p_ctrl->EntityCnt) { /* Verify if entity exists (see Note #3). */
*p_recipient_type = USBD_AUDIO_ENTITY_UNKNOWN;
return (DEF_NULL);
}
*p_recipient_type = p_ctrl->EntityID_TblPtr[(entity_id - 1u)]->EntityType;
switch(*p_recipient_type) {
case USBD_AUDIO_ENTITY_IT:
p_it = (USBD_AUDIO_IT *)p_ctrl->EntityID_TblPtr[(entity_id - 1u)];
p_recipient_info = (void *)p_it;
break;
case USBD_AUDIO_ENTITY_OT:
p_ot = (USBD_AUDIO_OT *)p_ctrl->EntityID_TblPtr[(entity_id - 1u)];
p_recipient_info = (void *)p_ot;
break;
case USBD_AUDIO_ENTITY_FU:
p_fu = (USBD_AUDIO_FU *)p_ctrl->EntityID_TblPtr[(entity_id - 1u)];
p_recipient_info = (void *)p_fu;
break;
case USBD_AUDIO_ENTITY_MU:
p_mu = (USBD_AUDIO_MU *)p_ctrl->EntityID_TblPtr[(entity_id - 1u)];
p_recipient_info = (void *)p_mu;
break;
case USBD_AUDIO_ENTITY_SU:
p_su = (USBD_AUDIO_SU *)p_ctrl->EntityID_TblPtr[(entity_id - 1u)];
p_recipient_info = (void *)p_su;
break;
default:
*p_recipient_type = USBD_AUDIO_ENTITY_UNKNOWN;
p_recipient_info = DEF_NULL;
break;
}
return (p_recipient_info);
}
/*
*********************************************************************************************************
* USBD_Audio_CommGet()
*
* Description : Get the Comm structure with the specified configuration number.
*
* Argument(s) : p_ctrl Pointer to the class instance control structure.
*
* cfg_nbr Configuration number to look for.
*
* Return(s) : Pointer to the audio communication structure, 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_CFG_ERR_ARG_CHK_EXT_EN == DEF_ENABLED)
static USBD_AUDIO_COMM *USBD_Audio_CommGet (const USBD_AUDIO_CTRL *p_ctrl,
CPU_INT08U cfg_nbr)
{
USBD_AUDIO_COMM *p_comm;
CPU_INT08U ix;
p_comm = p_ctrl->CommHeadPtr;
for(ix = 0u; ix < p_ctrl->CommCnt; ix++) {
if (p_comm->CfgNbr == cfg_nbr) {
return (p_comm);
} else {
p_comm = p_comm->NextPtr;
}
}
return (DEF_NULL);
}
#endif
/*
*********************************************************************************************************
* USBD_Audio_MaxPktLenGet()
*
* Description : Compute maximum isochronous packet length.
*
* Argument(s) : p_as_cfg Pointer to the AudioStreaming interface structure.
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* USBD_ERR_NONE Maximum packet length successfully computed.
* USBD_ERR_FAIL Wrong synchronization type.
*
* Return(s) : Maximum isochronous packet length, if NO error(s).
*
* 0, otherwise.
*
* Note(s) : (1) If the endpoint uses adaptive or asynchronous synchronization, one additional sample
* frame should be added to accommodate adjustments in the data rate. Section "2.2.1
* USB Packets" of Audio Data Formats 1.0 Specification limits variation in the packet
* size by +/- 1 sample frame.
*********************************************************************************************************
*/
#if (USBD_AUDIO_CFG_PLAYBACK_EN == DEF_ENABLED) || \
(USBD_AUDIO_CFG_RECORD_EN == DEF_ENABLED)
static CPU_INT16U USBD_Audio_MaxPktLenGet (USBD_AUDIO_AS_ALT_CFG *p_as_cfg,
USBD_ERR *p_err)
{
CPU_INT16U audio_frame_per_pkt;
CPU_INT08U audio_frame_len;
CPU_INT32U max_sam_freq;
CPU_INT08U sync_type;
CPU_INT08U sam_freq_ix;
CPU_INT16U max_pkt_len;
audio_frame_len = p_as_cfg->SubframeSize * p_as_cfg->NbrCh;
if (p_as_cfg->NbrSamplingFreq == 0u) {
max_sam_freq = p_as_cfg->UpperSamplingFreq;
} else {
max_sam_freq = 0u;
for(sam_freq_ix = 0u; sam_freq_ix < p_as_cfg->NbrSamplingFreq; sam_freq_ix++) {
if(p_as_cfg->SamplingFreqTblPtr[sam_freq_ix] > max_sam_freq) {
max_sam_freq = p_as_cfg->SamplingFreqTblPtr[sam_freq_ix];
}
}
}
/* Find the max number of audio frame per pkt (1ms)... */
audio_frame_per_pkt = max_sam_freq / 1000u; /* ...i.e. nbr of audio samples per frame. */
if ((max_sam_freq % 1000u) > 0u) {
audio_frame_per_pkt += 1u;
}
/* Find the max pkt len. */
sync_type = p_as_cfg->EP_SynchType & USBD_EP_TYPE_SYNC_MASK;
switch (sync_type) {
case USBD_EP_TYPE_SYNC_SYNC:
max_pkt_len = audio_frame_per_pkt * audio_frame_len;
*p_err = USBD_ERR_NONE;
break;
case USBD_EP_TYPE_SYNC_ASYNC:
case USBD_EP_TYPE_SYNC_ADAPTIVE:
/* See Note #1. */
max_pkt_len = (audio_frame_per_pkt + 1u) * audio_frame_len;
*p_err = USBD_ERR_NONE;
break;
default:
*p_err = USBD_ERR_FAIL;
max_pkt_len = 0u;
break;
}
return (max_pkt_len);
}
#endif