/** ****************************************************************************** * @file usbh_audio.c * @author MCD Application Team * @brief This file is the AC Layer Handlers for USB Host AC class. * * @verbatim * * =================================================================== * AUDIO Class Description * =================================================================== * This driver manages the Audio Class 1.0 following the "USB Device * Class Definition for Audio Devices V1.0 Mar 18, 98". * * @endverbatim * ****************************************************************************** * @attention * *

© Copyright (c) 2015 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under Ultimate Liberty license * SLA0044, the "License"; You may not use this file except in compliance with * the License. You may obtain a copy of the License at: * www.st.com/SLA0044 * ****************************************************************************** */ /* BSPDependencies - "stm32xxxxx_{eval}{discovery}{nucleo_144}.c" - "stm32xxxxx_{eval}{discovery}_io.c" - "stm32xxxxx_{eval}{discovery}{adafruit}_sd.c" - "stm32xxxxx_{eval}{discovery}{adafruit}_lcd.c" - "stm32xxxxx_{eval}{discovery}_sdram.c" EndBSPDependencies */ /* Includes ------------------------------------------------------------------*/ #include "usbh_audio.h" /** @addtogroup USBH_LIB * @{ */ /** @addtogroup USBH_CLASS * @{ */ /** @addtogroup USBH_AUDIO_CLASS * @{ */ /** @defgroup USBH_AUDIO_CORE * @brief This file includes HID Layer Handlers for USB Host HID class. * @{ */ /** @defgroup USBH_AUDIO_CORE_Private_TypesDefinitions * @{ */ /** * @} */ /** @defgroup USBH_AUDIO_CORE_Private_Defines * @{ */ /** * @} */ /** @defgroup USBH_AUDIO_CORE_Private_Macros * @{ */ /** * @} */ /** @defgroup USBH_AUDIO_CORE_Private_Variables * @{ */ /** * @} */ /** @defgroup USBH_AUDIO_CORE_Private_FunctionPrototypes * @{ */ static USBH_StatusTypeDef USBH_AUDIO_InterfaceInit(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_InterfaceDeInit(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_Process(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_SOFProcess(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_ClassRequest(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_CSRequest(USBH_HandleTypeDef *phost, uint8_t feature, uint8_t channel); static USBH_StatusTypeDef USBH_AUDIO_HandleCSRequest(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_FindAudioStreamingIN(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_FindAudioStreamingOUT(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_FindHIDControl(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_ParseCSDescriptors(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_BuildHeadphonePath(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_BuildMicrophonePath(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef ParseCSDescriptors(AUDIO_ClassSpecificDescTypedef *class_desc, uint8_t ac_subclass, uint8_t *pdesc); static USBH_StatusTypeDef USBH_AUDIO_Transmit(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AC_SetCur(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length); static USBH_StatusTypeDef USBH_AC_GetCur(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length); static USBH_StatusTypeDef USBH_AC_GetMin(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length); static USBH_StatusTypeDef USBH_AC_GetMax(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length); static USBH_StatusTypeDef USBH_AC_GetRes(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length); static USBH_StatusTypeDef USBH_AUDIO_SetEndpointControls(USBH_HandleTypeDef *phost, uint8_t Ep, uint8_t *buff); static USBH_StatusTypeDef AUDIO_SetVolume(USBH_HandleTypeDef *phost, uint8_t feature, uint8_t channel, uint16_t volume); static USBH_StatusTypeDef USBH_AUDIO_InputStream(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_OutputStream(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_Control(USBH_HandleTypeDef *phost); static USBH_StatusTypeDef USBH_AUDIO_SetControlAttribute(USBH_HandleTypeDef *phost, uint8_t attrib); static uint32_t USBH_AUDIO_FindLinkedUnit(USBH_HandleTypeDef *phost, uint8_t UnitID); USBH_ClassTypeDef AUDIO_Class = { "AUDIO", AC_CLASS, USBH_AUDIO_InterfaceInit, USBH_AUDIO_InterfaceDeInit, USBH_AUDIO_ClassRequest, USBH_AUDIO_Process, USBH_AUDIO_SOFProcess, NULL, }; /** * @} */ /** @defgroup USBH_AUDIO_CORE_Private_Functions * @{ */ /** * @brief USBH_AUDIO_InterfaceInit * The function init the Audio class. * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_InterfaceInit(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef out_status, in_status; AUDIO_HandleTypeDef *AUDIO_Handle; uint8_t interface, index; uint16_t ep_size_out = 0U; uint16_t ep_size_in = 0U; interface = USBH_FindInterface(phost, AC_CLASS, USB_SUBCLASS_AUDIOCONTROL, 0x00U); if (interface == 0xFFU) /* Not Valid Interface */ { USBH_DbgLog("Cannot Find the interface for %s class.", phost->pActiveClass->Name); return USBH_FAIL; } phost->pActiveClass->pData = (AUDIO_HandleTypeDef *)USBH_malloc(sizeof(AUDIO_HandleTypeDef)); AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle == NULL) { USBH_DbgLog("Cannot allocate memory for AUDIO Handle"); return USBH_FAIL; } /* Initialize audio handler */ USBH_memset(AUDIO_Handle, 0, sizeof(AUDIO_HandleTypeDef)); /* 1st Step: Find Audio Interfaces */ out_status = USBH_AUDIO_FindAudioStreamingIN(phost); in_status = USBH_AUDIO_FindAudioStreamingOUT(phost); if ((out_status == USBH_FAIL) && (in_status == USBH_FAIL)) { USBH_DbgLog("%s class configuration not supported.", phost->pActiveClass->Name); return USBH_FAIL; } /* 2nd Step: Select Audio Streaming Interfaces with largest endpoint size : default behavior */ for (index = 0U; index < AUDIO_MAX_AUDIO_STD_INTERFACE; index ++) { if (AUDIO_Handle->stream_out[index].valid == 1U) { if (ep_size_out < AUDIO_Handle->stream_out[index].EpSize) { ep_size_out = AUDIO_Handle->stream_out[index].EpSize; AUDIO_Handle->headphone.interface = AUDIO_Handle->stream_out[index].interface; AUDIO_Handle->headphone.AltSettings = AUDIO_Handle->stream_out[index].AltSettings; AUDIO_Handle->headphone.Ep = AUDIO_Handle->stream_out[index].Ep; AUDIO_Handle->headphone.EpSize = AUDIO_Handle->stream_out[index].EpSize; AUDIO_Handle->headphone.Poll = (uint8_t)AUDIO_Handle->stream_out[index].Poll; AUDIO_Handle->headphone.supported = 1U; } } if (AUDIO_Handle->stream_in[index].valid == 1U) { if (ep_size_in < AUDIO_Handle->stream_in[index].EpSize) { ep_size_in = AUDIO_Handle->stream_in[index].EpSize; AUDIO_Handle->microphone.interface = AUDIO_Handle->stream_in[index].interface; AUDIO_Handle->microphone.AltSettings = AUDIO_Handle->stream_in[index].AltSettings; AUDIO_Handle->microphone.Ep = AUDIO_Handle->stream_in[index].Ep; AUDIO_Handle->microphone.EpSize = AUDIO_Handle->stream_in[index].EpSize; AUDIO_Handle->microphone.Poll = (uint8_t)AUDIO_Handle->stream_out[index].Poll; AUDIO_Handle->microphone.supported = 1U; } } } if (USBH_AUDIO_FindHIDControl(phost) == USBH_OK) { AUDIO_Handle->control.supported = 1U; } /* 3rd Step: Find and Parse Audio interfaces */ USBH_AUDIO_ParseCSDescriptors(phost); /* 4th Step: Open the Audio streaming pipes*/ if (AUDIO_Handle->headphone.supported == 1U) { USBH_AUDIO_BuildHeadphonePath(phost); AUDIO_Handle->headphone.Pipe = USBH_AllocPipe(phost, AUDIO_Handle->headphone.Ep); /* Open pipe for IN endpoint */ USBH_OpenPipe(phost, AUDIO_Handle->headphone.Pipe, AUDIO_Handle->headphone.Ep, phost->device.address, phost->device.speed, USB_EP_TYPE_ISOC, AUDIO_Handle->headphone.EpSize); USBH_LL_SetToggle(phost, AUDIO_Handle->headphone.Pipe, 0U); } if (AUDIO_Handle->microphone.supported == 1U) { USBH_AUDIO_BuildMicrophonePath(phost); AUDIO_Handle->microphone.Pipe = USBH_AllocPipe(phost, AUDIO_Handle->microphone.Ep); /* Open pipe for IN endpoint */ USBH_OpenPipe(phost, AUDIO_Handle->microphone.Pipe, AUDIO_Handle->microphone.Ep, phost->device.address, phost->device.speed, USB_EP_TYPE_ISOC, AUDIO_Handle->microphone.EpSize); USBH_LL_SetToggle(phost, AUDIO_Handle->microphone.Pipe, 0U); } if (AUDIO_Handle->control.supported == 1U) { AUDIO_Handle->control.Pipe = USBH_AllocPipe(phost, AUDIO_Handle->control.Ep); /* Open pipe for IN endpoint */ USBH_OpenPipe(phost, AUDIO_Handle->control.Pipe, AUDIO_Handle->control.Ep, phost->device.address, phost->device.speed, USB_EP_TYPE_INTR, AUDIO_Handle->control.EpSize); USBH_LL_SetToggle(phost, AUDIO_Handle->control.Pipe, 0U); } AUDIO_Handle->req_state = AUDIO_REQ_INIT; AUDIO_Handle->control_state = AUDIO_CONTROL_INIT; return USBH_OK; } /** * @brief USBH_AUDIO_InterfaceDeInit * The function DeInit the Pipes used for the Audio class. * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_InterfaceDeInit(USBH_HandleTypeDef *phost) { AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->microphone.Pipe != 0x00U) { USBH_ClosePipe(phost, AUDIO_Handle->microphone.Pipe); USBH_FreePipe(phost, AUDIO_Handle->microphone.Pipe); AUDIO_Handle->microphone.Pipe = 0U; /* Reset the pipe as Free */ } if (AUDIO_Handle->headphone.Pipe != 0x00U) { USBH_ClosePipe(phost, AUDIO_Handle->headphone.Pipe); USBH_FreePipe(phost, AUDIO_Handle->headphone.Pipe); AUDIO_Handle->headphone.Pipe = 0U; /* Reset the pipe as Free */ } if (AUDIO_Handle->control.Pipe != 0x00U) { USBH_ClosePipe(phost, AUDIO_Handle->control.Pipe); USBH_FreePipe(phost, AUDIO_Handle->control.Pipe); AUDIO_Handle->control.Pipe = 0U; /* Reset the pipe as Free */ } if (phost->pActiveClass->pData) { USBH_free(phost->pActiveClass->pData); phost->pActiveClass->pData = 0U; } return USBH_OK ; } /** * @brief USBH_AUDIO_ClassRequest * The function is responsible for handling Standard requests * for Audio class. * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_ClassRequest(USBH_HandleTypeDef *phost) { AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; USBH_StatusTypeDef status = USBH_BUSY; USBH_StatusTypeDef req_status = USBH_BUSY; /* Switch AUDIO REQ state machine */ switch (AUDIO_Handle->req_state) { case AUDIO_REQ_INIT: case AUDIO_REQ_SET_DEFAULT_IN_INTERFACE: if (AUDIO_Handle->microphone.supported == 1U) { req_status = USBH_SetInterface(phost, AUDIO_Handle->microphone.interface, 0U); if (req_status == USBH_OK) { AUDIO_Handle->req_state = AUDIO_REQ_SET_DEFAULT_OUT_INTERFACE; } else if (req_status == USBH_NOT_SUPPORTED) { USBH_ErrLog("Control error: AUDIO: Device Set interface request failed"); status = USBH_FAIL; } else { /* .. */ } } else { AUDIO_Handle->req_state = AUDIO_REQ_SET_DEFAULT_OUT_INTERFACE; #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_URB_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif } break; case AUDIO_REQ_SET_DEFAULT_OUT_INTERFACE: if (AUDIO_Handle->headphone.supported == 1U) { req_status = USBH_SetInterface(phost, AUDIO_Handle->headphone.interface, 0U); if (req_status == USBH_OK) { AUDIO_Handle->req_state = AUDIO_REQ_CS_REQUESTS; AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_VOLUME; AUDIO_Handle->temp_feature = AUDIO_Handle->headphone.asociated_feature; AUDIO_Handle->temp_channels = AUDIO_Handle->headphone.asociated_channels; } else if (req_status == USBH_NOT_SUPPORTED) { USBH_ErrLog("Control error: AUDIO: Device Set interface request failed"); status = USBH_FAIL; } else { /* .. */ } } else { AUDIO_Handle->req_state = AUDIO_REQ_CS_REQUESTS; AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_VOLUME; #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_URB_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif } break; case AUDIO_REQ_CS_REQUESTS: if (USBH_AUDIO_HandleCSRequest(phost) == USBH_OK) { AUDIO_Handle->req_state = AUDIO_REQ_SET_IN_INTERFACE; } break; case AUDIO_REQ_SET_IN_INTERFACE: if (AUDIO_Handle->microphone.supported == 1U) { req_status = USBH_SetInterface(phost, AUDIO_Handle->microphone.interface, AUDIO_Handle->microphone.AltSettings); if (req_status == USBH_OK) { AUDIO_Handle->req_state = AUDIO_REQ_SET_OUT_INTERFACE; } else if (req_status == USBH_NOT_SUPPORTED) { USBH_ErrLog("Control error: AUDIO: Device Set interface request failed"); status = USBH_FAIL; } else { /* .. */ } } else { AUDIO_Handle->req_state = AUDIO_REQ_SET_OUT_INTERFACE; #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_URB_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif } break; case AUDIO_REQ_SET_OUT_INTERFACE: if (AUDIO_Handle->headphone.supported == 1U) { req_status = USBH_SetInterface(phost, AUDIO_Handle->headphone.interface, AUDIO_Handle->headphone.AltSettings); if (req_status == USBH_OK) { AUDIO_Handle->req_state = AUDIO_REQ_IDLE; } else if (req_status == USBH_NOT_SUPPORTED) { USBH_ErrLog("Control error: AUDIO: Device Set interface request failed"); status = USBH_FAIL; } else { /* .. */ } } else { AUDIO_Handle->req_state = AUDIO_REQ_IDLE; #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_URB_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif } break; case AUDIO_REQ_IDLE: AUDIO_Handle->play_state = AUDIO_PLAYBACK_INIT; phost->pUser(phost, HOST_USER_CLASS_ACTIVE); status = USBH_OK; #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_CLASS_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif break; default: break; } return status; } /** * @brief USBH_AUDIO_CSRequest * The function is responsible for handling AC Specific requests for a specific feature and channel * for Audio class. * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_CSRequest(USBH_HandleTypeDef *phost, uint8_t feature, uint8_t channel) { AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; USBH_StatusTypeDef status = USBH_BUSY; USBH_StatusTypeDef req_status = USBH_BUSY; uint16_t VolumeCtl, ResolutionCtl; /* Switch AUDIO REQ state machine */ switch (AUDIO_Handle->cs_req_state) { case AUDIO_REQ_GET_VOLUME: req_status = USBH_AC_GetCur(phost, UAC_FEATURE_UNIT, /* subtype */ feature, /* feature */ VOLUME_CONTROL, /* Selector */ channel, /* channel */ 0x02U); /* length */ if (req_status != USBH_BUSY) { AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_MIN_VOLUME; VolumeCtl = LE16(&(AUDIO_Handle->mem[0])); AUDIO_Handle->headphone.attribute.volume = (uint32_t)VolumeCtl; } break; case AUDIO_REQ_GET_MIN_VOLUME: req_status = USBH_AC_GetMin(phost, UAC_FEATURE_UNIT, /* subtype */ feature, /* feature */ VOLUME_CONTROL, /* Selector */ channel, /* channel */ 0x02U); /* length */ if (req_status != USBH_BUSY) { AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_MAX_VOLUME; VolumeCtl = LE16(&(AUDIO_Handle->mem[0])); AUDIO_Handle->headphone.attribute.volumeMin = (uint32_t)VolumeCtl; } break; case AUDIO_REQ_GET_MAX_VOLUME: req_status = USBH_AC_GetMax(phost, UAC_FEATURE_UNIT, /* subtype */ feature, /* feature */ VOLUME_CONTROL, /* Selector */ channel, /* channel */ 0x02U); /* length */ if (req_status != USBH_BUSY) { AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_RESOLUTION; VolumeCtl = LE16(&(AUDIO_Handle->mem[0])); AUDIO_Handle->headphone.attribute.volumeMax = (uint32_t)VolumeCtl; if (AUDIO_Handle->headphone.attribute.volumeMax < AUDIO_Handle->headphone.attribute.volumeMin) { AUDIO_Handle->headphone.attribute.volumeMax = 0xFF00U; } } break; case AUDIO_REQ_GET_RESOLUTION: req_status = USBH_AC_GetRes(phost, UAC_FEATURE_UNIT, /* subtype */ feature, /* feature */ VOLUME_CONTROL, /* Selector */ channel, /* channel */ 0x02U); /* length */ if (req_status != USBH_BUSY) { AUDIO_Handle->cs_req_state = AUDIO_REQ_CS_IDLE; ResolutionCtl = LE16(&AUDIO_Handle->mem[0]); AUDIO_Handle->headphone.attribute.resolution = (uint32_t)ResolutionCtl; } break; case AUDIO_REQ_CS_IDLE: status = USBH_OK; break; default: break; } return status; } /** * @brief USBH_AUDIO_HandleCSRequest * The function is responsible for handling AC Specific requests for a all features * and associated channels for Audio class. * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_HandleCSRequest(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status = USBH_BUSY; USBH_StatusTypeDef cs_status = USBH_BUSY; AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; cs_status = USBH_AUDIO_CSRequest(phost, AUDIO_Handle->temp_feature, AUDIO_Handle->temp_channels); if (cs_status != USBH_BUSY) { if (AUDIO_Handle->temp_channels == 1U) { AUDIO_Handle->temp_feature = AUDIO_Handle->headphone.asociated_feature; AUDIO_Handle->temp_channels = 0U; status = USBH_OK; } else { AUDIO_Handle->temp_channels--; } AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_VOLUME; #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_URB_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif } return status; } /** * @brief USBH_AUDIO_Process * The function is for managing state machine for Audio data transfers * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_Process(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status = USBH_BUSY; AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->headphone.supported == 1U) { USBH_AUDIO_OutputStream(phost); } if (AUDIO_Handle->microphone.supported == 1U) { USBH_AUDIO_InputStream(phost); } return status; } /** * @brief USBH_AUDIO_SOFProcess * The function is for managing the SOF callback * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_SOFProcess(USBH_HandleTypeDef *phost) { /* Prevent unused argument(s) compilation warning */ UNUSED(phost); return USBH_OK; } /** * @brief Find IN Audio Streaming interfaces * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_FindAudioStreamingIN(USBH_HandleTypeDef *phost) { uint8_t interface, alt_settings; USBH_StatusTypeDef status = USBH_FAIL ; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; /* Look For AUDIOSTREAMING IN interface */ alt_settings = 0U; for (interface = 0U; interface < USBH_MAX_NUM_INTERFACES; interface++) { if ((phost->device.CfgDesc.Itf_Desc[interface].bInterfaceClass == AC_CLASS) && (phost->device.CfgDesc.Itf_Desc[interface].bInterfaceSubClass == USB_SUBCLASS_AUDIOSTREAMING)) { if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80U) && (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize > 0U)) { AUDIO_Handle->stream_in[alt_settings].Ep = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress; AUDIO_Handle->stream_in[alt_settings].EpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize; AUDIO_Handle->stream_in[alt_settings].interface = phost->device.CfgDesc.Itf_Desc[interface].bInterfaceNumber; AUDIO_Handle->stream_in[alt_settings].AltSettings = phost->device.CfgDesc.Itf_Desc[interface].bAlternateSetting; AUDIO_Handle->stream_in[alt_settings].Poll = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bInterval; AUDIO_Handle->stream_in[alt_settings].valid = 1U; alt_settings++; } } } if (alt_settings > 0U) { status = USBH_OK; } return status; } /** * @brief Find OUT Audio Streaming interfaces * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_FindAudioStreamingOUT(USBH_HandleTypeDef *phost) { uint8_t interface, alt_settings; USBH_StatusTypeDef status = USBH_FAIL ; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; /* Look For AUDIOSTREAMING IN interface */ alt_settings = 0U; for (interface = 0U; interface < USBH_MAX_NUM_INTERFACES; interface++) { if ((phost->device.CfgDesc.Itf_Desc[interface].bInterfaceClass == AC_CLASS) && (phost->device.CfgDesc.Itf_Desc[interface].bInterfaceSubClass == USB_SUBCLASS_AUDIOSTREAMING)) { if (((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80U) == 0x00U) && (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize > 0U)) { AUDIO_Handle->stream_out[alt_settings].Ep = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress; AUDIO_Handle->stream_out[alt_settings].EpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize; AUDIO_Handle->stream_out[alt_settings].interface = phost->device.CfgDesc.Itf_Desc[interface].bInterfaceNumber; AUDIO_Handle->stream_out[alt_settings].AltSettings = phost->device.CfgDesc.Itf_Desc[interface].bAlternateSetting; AUDIO_Handle->stream_out[alt_settings].Poll = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bInterval; AUDIO_Handle->stream_out[alt_settings].valid = 1U; alt_settings++; } } } if (alt_settings > 0U) { status = USBH_OK; } return status; } /** * @brief Find HID Control interfaces * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_FindHIDControl(USBH_HandleTypeDef *phost) { uint8_t interface; USBH_StatusTypeDef status = USBH_FAIL; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; /* Look For AUDIOCONTROL interface */ interface = USBH_FindInterface(phost, AC_CLASS, USB_SUBCLASS_AUDIOCONTROL, 0xFFU); if ((interface == 0xFFU) || (interface >= USBH_MAX_NUM_INTERFACES)) { return USBH_FAIL; } for (interface = 0U; interface < USBH_MAX_NUM_INTERFACES; interface++) { if ((phost->device.CfgDesc.Itf_Desc[interface].bInterfaceClass == 0x03U) && /*HID*/ (phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize > 0U)) { if ((phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress & 0x80U) == 0x80U) { AUDIO_Handle->control.Ep = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bEndpointAddress; AUDIO_Handle->control.EpSize = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].wMaxPacketSize; AUDIO_Handle->control.interface = phost->device.CfgDesc.Itf_Desc[interface].bInterfaceNumber; AUDIO_Handle->control.Poll = phost->device.CfgDesc.Itf_Desc[interface].Ep_Desc[0].bInterval; AUDIO_Handle->control.supported = 1U; status = USBH_OK; break; } } } return status; } /** * @brief Parse AC and interfaces Descriptors * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_ParseCSDescriptors(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status = USBH_OK; USBH_DescHeader_t *pdesc; uint16_t ptr; uint8_t itf_index = 0U; uint8_t itf_number = 0U; uint8_t alt_setting; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; pdesc = (USBH_DescHeader_t *)(void *)(phost->device.CfgDesc_Raw); ptr = USB_LEN_CFG_DESC; AUDIO_Handle->class_desc.FeatureUnitNum = 0U; AUDIO_Handle->class_desc.InputTerminalNum = 0U; AUDIO_Handle->class_desc.OutputTerminalNum = 0U; AUDIO_Handle->class_desc.ASNum = 0U; while (ptr < phost->device.CfgDesc.wTotalLength) { pdesc = USBH_GetNextDesc((uint8_t *)(void *)pdesc, &ptr); switch (pdesc->bDescriptorType) { case USB_DESC_TYPE_INTERFACE: itf_number = *((uint8_t *)(void *)pdesc + 2U); alt_setting = *((uint8_t *)(void *)pdesc + 3U); itf_index = USBH_FindInterfaceIndex(phost, itf_number, alt_setting); break; case USB_DESC_TYPE_CS_INTERFACE: if (itf_number <= phost->device.CfgDesc.bNumInterfaces) { if ((itf_index == 0xFFU) || (itf_index >= USBH_MAX_NUM_INTERFACES)) /* No Valid Interface */ { USBH_DbgLog("Cannot Find the audio interface index for %s class.", phost->pActiveClass->Name); status = USBH_FAIL; } else { ParseCSDescriptors(&AUDIO_Handle->class_desc, phost->device.CfgDesc.Itf_Desc[itf_index].bInterfaceSubClass, (uint8_t *)pdesc); } } break; default: break; } } return status; } /** * @brief Parse AC interfaces * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef ParseCSDescriptors(AUDIO_ClassSpecificDescTypedef *class_desc, uint8_t ac_subclass, uint8_t *pdesc) { if (ac_subclass == USB_SUBCLASS_AUDIOCONTROL) { switch (pdesc[2]) { case UAC_HEADER: class_desc->cs_desc.HeaderDesc = (AUDIO_HeaderDescTypeDef *)(void *)pdesc; break; case UAC_INPUT_TERMINAL: class_desc->cs_desc.InputTerminalDesc[class_desc->InputTerminalNum++] = (AUDIO_ITDescTypeDef *)(void *)pdesc; break; case UAC_OUTPUT_TERMINAL: class_desc->cs_desc.OutputTerminalDesc[class_desc->OutputTerminalNum++] = (AUDIO_OTDescTypeDef *)(void *)pdesc; break; case UAC_FEATURE_UNIT: class_desc->cs_desc.FeatureUnitDesc[class_desc->FeatureUnitNum++] = (AUDIO_FeatureDescTypeDef *)(void *)pdesc; break; case UAC_SELECTOR_UNIT: class_desc->cs_desc.SelectorUnitDesc[class_desc->SelectorUnitNum++] = (AUDIO_SelectorDescTypeDef *)(void *)pdesc; break; case UAC_MIXER_UNIT: class_desc->cs_desc.MixerUnitDesc[class_desc->MixerUnitNum++] = (AUDIO_MixerDescTypeDef *)(void *)pdesc; break; default: break; } } else { if (ac_subclass == USB_SUBCLASS_AUDIOSTREAMING) { switch (pdesc[2]) { case UAC_AS_GENERAL: class_desc->as_desc[class_desc->ASNum].GeneralDesc = (AUDIO_ASGeneralDescTypeDef *)(void *)pdesc; break; case UAC_FORMAT_TYPE: class_desc->as_desc[class_desc->ASNum++].FormatTypeDesc = (AUDIO_ASFormatTypeDescTypeDef *)(void *)pdesc; break; default: break; } } } return USBH_OK; } /** * @brief Link a Unit to next associated one * @param phost: Host handle * @param UnitID: Unit identifer * @retval UnitID, Index and Type of the associated Unit */ static uint32_t USBH_AUDIO_FindLinkedUnit(USBH_HandleTypeDef *phost, uint8_t UnitID) { uint8_t Index; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; /* Find Feature Unit */ for (Index = 0U; Index < AUDIO_Handle->class_desc.FeatureUnitNum; Index ++) { if (AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[Index]->bSourceID == UnitID) { UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[Index]->bUnitID; return (((uint32_t)UnitID << 16U) | (UAC_FEATURE_UNIT << 8U) | (uint32_t)Index); } } /* Find Mixer Unit */ for (Index = 0U; Index < AUDIO_Handle->class_desc.MixerUnitNum; Index ++) { if ((AUDIO_Handle->class_desc.cs_desc.MixerUnitDesc[Index]->bSourceID0 == UnitID) || (AUDIO_Handle->class_desc.cs_desc.MixerUnitDesc[Index]->bSourceID1 == UnitID)) { UnitID = AUDIO_Handle->class_desc.cs_desc.MixerUnitDesc[Index]->bUnitID; return ((UnitID << 16U) | (UAC_MIXER_UNIT << 8U) | Index); } } /* Find Selector Unit */ for (Index = 0U; Index < AUDIO_Handle->class_desc.SelectorUnitNum; Index ++) { if (AUDIO_Handle->class_desc.cs_desc.SelectorUnitDesc[Index]->bSourceID0 == UnitID) { UnitID = AUDIO_Handle->class_desc.cs_desc.SelectorUnitDesc[Index]->bUnitID; return ((UnitID << 16U) | (UAC_SELECTOR_UNIT << 8U) | Index); } } /* Find OT Unit */ for (Index = 0U; Index < AUDIO_Handle->class_desc.OutputTerminalNum; Index ++) { if (AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[Index]->bSourceID == UnitID) { UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[Index]->bTerminalID; return ((UnitID << 16U) | (UAC_OUTPUT_TERMINAL << 8U) | Index); } } /* No associated Unit found return undefined ID 0x00*/ return 0U; } /** * @brief Build full path for Microphone device * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_BuildMicrophonePath(USBH_HandleTypeDef *phost) { uint8_t UnitID = 0U, Type, Index; uint32_t value; uint8_t terminalIndex; AUDIO_HandleTypeDef *AUDIO_Handle; USBH_StatusTypeDef ret = USBH_OK; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; /*Find microphone IT*/ for (terminalIndex = 0U; terminalIndex < AUDIO_Handle->class_desc.InputTerminalNum; terminalIndex++) { if (LE16(AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->wTerminalType) == 0x201) { UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->bTerminalID; AUDIO_Handle->microphone.asociated_channels = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->bNrChannels; break; } } do { value = USBH_AUDIO_FindLinkedUnit(phost, UnitID); if (!value) { return USBH_FAIL; } Index = (uint8_t)(value & 0xFFU); Type = (uint8_t)((value >> 8U) & 0xFFU); UnitID = (uint8_t)((value >> 16U) & 0xFFU); switch (Type) { case UAC_FEATURE_UNIT: AUDIO_Handle->microphone.asociated_feature = Index; break; case UAC_MIXER_UNIT: AUDIO_Handle->microphone.asociated_mixer = Index; break; case UAC_SELECTOR_UNIT: AUDIO_Handle->microphone.asociated_selector = Index; break; case UAC_OUTPUT_TERMINAL: AUDIO_Handle->microphone.asociated_terminal = Index; break; default: ret = USBH_FAIL; break; } } while ((Type != UAC_OUTPUT_TERMINAL) && (value > 0U)); return ret; } /** * @brief Build full path for Headphone device * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_BuildHeadphonePath(USBH_HandleTypeDef *phost) { uint8_t UnitID = 0U, Type, Index; uint32_t value; uint8_t terminalIndex; AUDIO_HandleTypeDef *AUDIO_Handle; USBH_StatusTypeDef ret = USBH_OK; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; /* Find association between audio streaming and microphone */ for (terminalIndex = 0U; terminalIndex < AUDIO_Handle->class_desc.InputTerminalNum; terminalIndex++) { if (LE16(AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->wTerminalType) == 0x101) { UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->bTerminalID; AUDIO_Handle->headphone.asociated_channels = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[terminalIndex]->bNrChannels; break; } } for (Index = 0U; Index < AUDIO_Handle->class_desc.ASNum; Index++) { if (AUDIO_Handle->class_desc.as_desc[Index].GeneralDesc->bTerminalLink == UnitID) { AUDIO_Handle->headphone.asociated_as = Index; break; } } do { value = USBH_AUDIO_FindLinkedUnit(phost, UnitID); if (!value) { return USBH_FAIL; } Index = (uint8_t)(value & 0xFFU); Type = (uint8_t)((value >> 8U) & 0xFFU); UnitID = (uint8_t)((value >> 16U) & 0xFFU); switch (Type) { case UAC_FEATURE_UNIT: AUDIO_Handle->headphone.asociated_feature = Index; break; case UAC_MIXER_UNIT: AUDIO_Handle->headphone.asociated_mixer = Index; break; case UAC_SELECTOR_UNIT: AUDIO_Handle->headphone.asociated_selector = Index; break; case UAC_OUTPUT_TERMINAL: AUDIO_Handle->headphone.asociated_terminal = Index; if (Index < AUDIO_MAX_NUM_OUT_TERMINAL) { if (LE16(AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[Index]->wTerminalType) != 0x103) { return USBH_OK; } } else { ret = USBH_FAIL; } break; default: ret = USBH_FAIL; break; } } while ((Type != UAC_OUTPUT_TERMINAL) && (value > 0U)); return ret; } /** * @brief Handle Set Cur request * @param phost: Host handle * @param subtype: subtype index * @param feature: feature index * @param controlSelector: control code * @param channel: channel index * @param length: Command length * @retval USBH Status */ static USBH_StatusTypeDef USBH_AC_SetCur(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length) { uint16_t wValue = 0U, wIndex = 0U, wLength = 0U; uint8_t UnitID, InterfaceNum; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; USBH_StatusTypeDef ret = USBH_OK; switch (subtype) { case UAC_INPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; wValue = (COPY_PROTECT_CONTROL << 8U); AUDIO_Handle->mem[0] = 0x00U; wLength = 1U; break; case UAC_FEATURE_UNIT: UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum ; /*holds the CS(control selector ) and CN (channel number)*/ wValue = (uint16_t)((uint32_t)controlSelector << 8U) | (uint16_t)channel; wLength = length; break; default: ret = USBH_FAIL; break; } if (ret != USBH_OK) { return ret; } phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_INTERFACE | USB_REQ_TYPE_CLASS; phost->Control.setup.b.bRequest = UAC_SET_CUR; phost->Control.setup.b.wValue.w = wValue; phost->Control.setup.b.wIndex.w = wIndex; phost->Control.setup.b.wLength.w = wLength; return (USBH_CtlReq(phost, (uint8_t *)(void *)(AUDIO_Handle->mem), wLength)); } /** * @brief Handle Get Cur request * @param phost: Host handle * @param subtype: subtype index * @param feature: feature index * @param controlSelector: control code * @param channel: channel index * @param length: Command length * @retval USBH Status */ static USBH_StatusTypeDef USBH_AC_GetCur(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length) { uint16_t wValue = 0U, wIndex = 0U, wLength = 0U; uint8_t UnitID = 0U, InterfaceNum = 0U; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; USBH_StatusTypeDef ret = USBH_OK; switch (subtype) { case UAC_INPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; wValue = (COPY_PROTECT_CONTROL << 8U); AUDIO_Handle->mem[0] = 0x00U; wLength = 1U; break; case UAC_FEATURE_UNIT: UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; /*holds the CS(control selector ) and CN (channel number)*/ wValue = (uint16_t)((uint32_t)controlSelector << 8U) | (uint16_t)channel; wLength = length; break; case UAC_OUTPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; wValue = (COPY_PROTECT_CONTROL << 8U); wLength = 1U; break; default: ret = USBH_FAIL; break; } if (ret != USBH_OK) { return ret; } phost->Control.setup.b.bmRequestType = USB_D2H | USB_REQ_RECIPIENT_INTERFACE | \ USB_REQ_TYPE_CLASS; phost->Control.setup.b.bRequest = UAC_GET_CUR; phost->Control.setup.b.wValue.w = wValue; phost->Control.setup.b.wIndex.w = wIndex; phost->Control.setup.b.wLength.w = wLength; return (USBH_CtlReq(phost, (uint8_t *)(void *)(AUDIO_Handle->mem), wLength)); } /** * @brief Handle Get Max request * @param phost: Host handle * @param subtype: subtype index * @param feature: feature index * @param controlSelector: control code * @param channel: channel index * @param length: Command length * @retval USBH Status */ static USBH_StatusTypeDef USBH_AC_GetMax(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length) { uint16_t wValue = 0U, wIndex = 0U, wLength = 0U; uint8_t UnitID = 0U, InterfaceNum = 0U; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; USBH_StatusTypeDef ret = USBH_OK; switch (subtype) { case UAC_INPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; wValue = (COPY_PROTECT_CONTROL << 8U); AUDIO_Handle->mem[0] = 0x00U; wLength = 1U; break; case UAC_FEATURE_UNIT: UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum ; /*holds the CS(control selector ) and CN (channel number)*/ wValue = (uint16_t)((uint32_t)controlSelector << 8U) | (uint16_t)channel; wLength = length; break; case UAC_OUTPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum ; wValue = (COPY_PROTECT_CONTROL << 8U) ; wLength = 1U; break; default: ret = USBH_FAIL; break; } if (ret != USBH_OK) { return ret; } phost->Control.setup.b.bmRequestType = USB_D2H | USB_REQ_RECIPIENT_INTERFACE | \ USB_REQ_TYPE_CLASS; phost->Control.setup.b.bRequest = UAC_GET_MAX; phost->Control.setup.b.wValue.w = wValue; phost->Control.setup.b.wIndex.w = wIndex; phost->Control.setup.b.wLength.w = wLength; return (USBH_CtlReq(phost, (uint8_t *)(void *)(AUDIO_Handle->mem), wLength)); } /** * @brief Handle Get Res request * @param phost: Host handle * @param subtype: subtype index * @param feature: feature index * @param controlSelector: control code * @param channel: channel index * @param length: Command length * @retval USBH Status */ static USBH_StatusTypeDef USBH_AC_GetRes(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length) { uint16_t wValue = 0U, wIndex = 0U, wLength = 0U; uint8_t UnitID = 0U, InterfaceNum = 0U; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; USBH_StatusTypeDef ret = USBH_OK; switch (subtype) { case UAC_INPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; wValue = (COPY_PROTECT_CONTROL << 8U) ; AUDIO_Handle->mem[0] = 0x00U; wLength = 1U; break; case UAC_FEATURE_UNIT: UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; /*holds the CS(control selector ) and CN (channel number)*/ wValue = (uint16_t)((uint32_t)controlSelector << 8U) | (uint16_t)channel; wLength = length; break; case UAC_OUTPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; wValue = (COPY_PROTECT_CONTROL << 8U) ; wLength = 1U; break; default: ret = USBH_FAIL; break; } if (ret != USBH_OK) { return ret; } phost->Control.setup.b.bmRequestType = USB_D2H | USB_REQ_RECIPIENT_INTERFACE | USB_REQ_TYPE_CLASS; phost->Control.setup.b.bRequest = UAC_GET_RES; phost->Control.setup.b.wValue.w = wValue; phost->Control.setup.b.wIndex.w = wIndex; phost->Control.setup.b.wLength.w = wLength; return (USBH_CtlReq(phost, (uint8_t *)(void *)(AUDIO_Handle->mem), wLength)); } /** * @brief Handle Get Min request * @param phost: Host handle * @param subtype: subtype index * @param feature: feature index * @param controlSelector: control code * @param channel: channel index * @param length: Command length * @retval USBH Status */ static USBH_StatusTypeDef USBH_AC_GetMin(USBH_HandleTypeDef *phost, uint8_t subtype, uint8_t feature, uint8_t controlSelector, uint8_t channel, uint16_t length) { uint16_t wValue = 0U, wIndex = 0U, wLength = 0U; uint8_t UnitID = 0U, InterfaceNum = 0U; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; USBH_StatusTypeDef ret = USBH_OK; switch (subtype) { case UAC_INPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.InputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; wValue = (COPY_PROTECT_CONTROL << 8U); AUDIO_Handle->mem[0] = 0x00U; wLength = 1U; break; case UAC_FEATURE_UNIT: UnitID = AUDIO_Handle->class_desc.cs_desc.FeatureUnitDesc[feature]->bUnitID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; /*holds the CS(control selector ) and CN (channel number)*/ wValue = (uint16_t)((uint32_t)controlSelector << 8U) | (uint16_t)channel; wLength = length; break; case UAC_OUTPUT_TERMINAL: UnitID = AUDIO_Handle->class_desc.cs_desc.OutputTerminalDesc[0]->bTerminalID; InterfaceNum = 0U; /*Always zero Control Interface */ wIndex = (uint16_t)((uint32_t)UnitID << 8U) | (uint16_t)InterfaceNum; wValue = (COPY_PROTECT_CONTROL << 8U); wLength = 1U; break; default: ret = USBH_FAIL; break; } if (ret != USBH_OK) { return ret; } phost->Control.setup.b.bmRequestType = USB_D2H | USB_REQ_RECIPIENT_INTERFACE | \ USB_REQ_TYPE_CLASS; phost->Control.setup.b.bRequest = UAC_GET_MIN; phost->Control.setup.b.wValue.w = wValue; phost->Control.setup.b.wIndex.w = wIndex; phost->Control.setup.b.wLength.w = wLength; return (USBH_CtlReq(phost, (uint8_t *)(void *)(AUDIO_Handle->mem), wLength)); } /** * @brief Handle Set Endpoint Controls Request * @param phost: Host handle * @param Ep: Endpoint address * @param buf: pointer to data * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_SetEndpointControls(USBH_HandleTypeDef *phost, uint8_t Ep, uint8_t *buff) { uint16_t wValue, wIndex, wLength; wValue = SAMPLING_FREQ_CONTROL << 8U; wIndex = Ep; wLength = 3U; /*length of the frequency parameter*/ phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_ENDPOINT | \ USB_REQ_TYPE_CLASS; phost->Control.setup.b.bRequest = UAC_SET_CUR; phost->Control.setup.b.wValue.w = wValue; phost->Control.setup.b.wIndex.w = wIndex; phost->Control.setup.b.wLength.w = wLength; return (USBH_CtlReq(phost, (uint8_t *)buff, wLength)); } /** * @brief Handle Input stream process * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_InputStream(USBH_HandleTypeDef *phost) { /* Prevent unused argument(s) compilation warning */ UNUSED(phost); USBH_StatusTypeDef status = USBH_BUSY; return status; } /** * @brief Handle HID Control process * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_Control(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status = USBH_BUSY ; AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; uint16_t attribute = 0U; switch (AUDIO_Handle->control_state) { case AUDIO_CONTROL_INIT: if ((phost->Timer & 1U) == 0U) { AUDIO_Handle->control.timer = phost->Timer; USBH_InterruptReceiveData(phost, (uint8_t *)(void *)(AUDIO_Handle->mem), (uint8_t)AUDIO_Handle->control.EpSize, AUDIO_Handle->control.Pipe); AUDIO_Handle->temp_feature = AUDIO_Handle->headphone.asociated_feature; AUDIO_Handle->temp_channels = AUDIO_Handle->headphone.asociated_channels; AUDIO_Handle->control_state = AUDIO_CONTROL_CHANGE ; } break; case AUDIO_CONTROL_CHANGE: if (USBH_LL_GetURBState(phost, AUDIO_Handle->control.Pipe) == USBH_URB_DONE) { attribute = LE16(&AUDIO_Handle->mem[0]); if (USBH_AUDIO_SetControlAttribute(phost, (uint8_t)attribute) == USBH_BUSY) { break; } } if ((phost->Timer - AUDIO_Handle->control.timer) >= AUDIO_Handle->control.Poll) { AUDIO_Handle->control.timer = phost->Timer; USBH_InterruptReceiveData(phost, (uint8_t *)(void *)(AUDIO_Handle->mem), (uint8_t)AUDIO_Handle->control.EpSize, AUDIO_Handle->control.Pipe); } break; case AUDIO_CONTROL_VOLUME_UP: if (USBH_AUDIO_SetControlAttribute(phost, 1U) == USBH_OK) { AUDIO_Handle->control_state = AUDIO_CONTROL_INIT; status = USBH_OK; } break; case AUDIO_CONTROL_VOLUME_DOWN: if (USBH_AUDIO_SetControlAttribute(phost, 2U) == USBH_OK) { AUDIO_Handle->control_state = AUDIO_CONTROL_INIT; status = USBH_OK; } break; case AUDIO_CONTROL_IDLE: default: break; } return status; } /** * @brief Handle Output stream process * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_OutputStream(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status = USBH_BUSY ; AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; uint8_t *buff; switch (AUDIO_Handle->play_state) { case AUDIO_PLAYBACK_INIT: if (AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->bSamFreqType == 0U) { AUDIO_Handle->play_state = AUDIO_PLAYBACK_SET_EP_FREQ; } else { AUDIO_Handle->play_state = AUDIO_PLAYBACK_SET_EP; } #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_URB_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif break; case AUDIO_PLAYBACK_SET_EP_FREQ: buff = (uint8_t *)AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->tSamFreq[0]; status = USBH_AUDIO_SetEndpointControls(phost, AUDIO_Handle->headphone.Ep, buff); if (status == USBH_OK) { AUDIO_Handle->play_state = AUDIO_PLAYBACK_IDLE; } break; case AUDIO_PLAYBACK_SET_EP: buff = (uint8_t *)(void *)&AUDIO_Handle->headphone.frequency; status = USBH_AUDIO_SetEndpointControls(phost, AUDIO_Handle->headphone.Ep, buff); if (status == USBH_OK) { AUDIO_Handle->play_state = AUDIO_PLAYBACK_IDLE; USBH_AUDIO_FrequencySet(phost); } break; case AUDIO_PLAYBACK_IDLE: status = USBH_OK; #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_CLASS_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif break; case AUDIO_PLAYBACK_PLAY: USBH_AUDIO_Transmit(phost); status = USBH_OK; break; default: break; } return status; } /** * @brief Handle Transmission process * @param phost: Host handle * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_Transmit(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef status = USBH_BUSY ; AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; switch (AUDIO_Handle->processing_state) { case AUDIO_DATA_START_OUT: /* Sync with start of Even Frame */ if ((phost->Timer & 1U) == 0U) { AUDIO_Handle->headphone.timer = phost->Timer; AUDIO_Handle->processing_state = AUDIO_DATA_OUT; USBH_IsocSendData(phost, AUDIO_Handle->headphone.buf, (uint32_t)AUDIO_Handle->headphone.frame_length, AUDIO_Handle->headphone.Pipe); AUDIO_Handle->headphone.partial_ptr = AUDIO_Handle->headphone.frame_length; AUDIO_Handle->headphone.global_ptr = AUDIO_Handle->headphone.frame_length; AUDIO_Handle->headphone.cbuf = AUDIO_Handle->headphone.buf; } else { #if (USBH_USE_OS == 1U) osDelay(1); phost->os_msg = (uint32_t)USBH_CLASS_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif } break; case AUDIO_DATA_OUT: if ((USBH_LL_GetURBState(phost, AUDIO_Handle->headphone.Pipe) == USBH_URB_DONE) && ((phost->Timer - AUDIO_Handle->headphone.timer) >= AUDIO_Handle->headphone.Poll)) { AUDIO_Handle->headphone.timer = phost->Timer; if (AUDIO_Handle->control.supported == 1U) { USBH_AUDIO_Control(phost); } if (AUDIO_Handle->headphone.global_ptr <= AUDIO_Handle->headphone.total_length) { USBH_IsocSendData(phost, AUDIO_Handle->headphone.cbuf, (uint32_t)AUDIO_Handle->headphone.frame_length, AUDIO_Handle->headphone.Pipe); AUDIO_Handle->headphone.cbuf += AUDIO_Handle->headphone.frame_length; AUDIO_Handle->headphone.partial_ptr += AUDIO_Handle->headphone.frame_length; AUDIO_Handle->headphone.global_ptr += AUDIO_Handle->headphone.frame_length; } else { AUDIO_Handle->headphone.partial_ptr = 0xFFFFFFFFU; AUDIO_Handle->play_state = AUDIO_PLAYBACK_IDLE; USBH_AUDIO_BufferEmptyCallback(phost); } } break; default: status = USBH_FAIL; break; } return status; } /** * @brief USBH_AUDIO_SetFrequency * Set Audio sampling parameters * @param phost: Host handle * @param SampleRate: Sample Rate * @param NbrChannels: Number of Channels * @param BitPerSample: Bit Per Sample * @retval USBH Status */ USBH_StatusTypeDef USBH_AUDIO_SetFrequency(USBH_HandleTypeDef *phost, uint16_t SampleRate, uint8_t NbrChannels, uint8_t BitPerSample) { USBH_StatusTypeDef Status = USBH_BUSY; AUDIO_HandleTypeDef *AUDIO_Handle; uint8_t index; uint8_t change_freq = FALSE; uint32_t freq_min, freq_max; uint8_t num_supported_freq; if (phost->gState == HOST_CLASS) { AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->play_state == AUDIO_PLAYBACK_IDLE) { if (AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->bSamFreqType == 0U) { freq_min = LE24(AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->tSamFreq[0]); freq_max = LE24(AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->tSamFreq[1]); if ((SampleRate >= freq_min) && (SampleRate <= freq_max)) { change_freq = TRUE; } } else { num_supported_freq = (AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->bLength - 8U) / 3U; for (index = 0U; index < num_supported_freq; index++) { if (SampleRate == LE24(AUDIO_Handle->class_desc.as_desc[AUDIO_Handle->headphone.asociated_as].FormatTypeDesc->tSamFreq[index])) { change_freq = TRUE; break; } } } if (change_freq == TRUE) { AUDIO_Handle->headphone.frequency = SampleRate; AUDIO_Handle->headphone.frame_length = (SampleRate * BitPerSample * NbrChannels) / 8000U; AUDIO_Handle->play_state = AUDIO_PLAYBACK_SET_EP; Status = USBH_OK; } } } return Status; } /** * @brief USBH_AUDIO_Play * Start playback process * @param phost: Host handle * @param buf: pointer to raw audio data * @param length: total length of the audio data * @retval USBH Status */ USBH_StatusTypeDef USBH_AUDIO_Play(USBH_HandleTypeDef *phost, uint8_t *buf, uint32_t length) { USBH_StatusTypeDef Status = USBH_FAIL; AUDIO_HandleTypeDef *AUDIO_Handle; if (phost->gState == HOST_CLASS) { AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->play_state == AUDIO_PLAYBACK_IDLE) { AUDIO_Handle->headphone.buf = buf; AUDIO_Handle->headphone.total_length = length; AUDIO_Handle->play_state = AUDIO_PLAYBACK_PLAY; AUDIO_Handle->control_state = AUDIO_CONTROL_INIT; AUDIO_Handle->processing_state = AUDIO_DATA_START_OUT; Status = USBH_OK; #if (USBH_USE_OS == 1U) phost->os_msg = (uint32_t)USBH_CLASS_EVENT; #if (osCMSIS < 0x20000U) (void)osMessagePut(phost->os_event, phost->os_msg, 0U); #else (void)osMessageQueuePut(phost->os_event, &phost->os_msg, 0U, NULL); #endif #endif } } return Status; } /** * @brief USBH_AUDIO_Pause * Stop the playback process * @param phost: Host handle * @retval USBH Status */ USBH_StatusTypeDef USBH_AUDIO_Stop(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef Status = USBH_FAIL; Status = USBH_AUDIO_Suspend(phost); return Status; } /** * @brief USBH_AUDIO_Suspend * Suspend the playback process * @param phost: Host handle * @retval USBH Status */ USBH_StatusTypeDef USBH_AUDIO_Suspend(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef Status = USBH_FAIL; AUDIO_HandleTypeDef *AUDIO_Handle; if (phost->gState == HOST_CLASS) { AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->play_state == AUDIO_PLAYBACK_PLAY) { AUDIO_Handle->control_state = AUDIO_CONTROL_IDLE; AUDIO_Handle->play_state = AUDIO_PLAYBACK_IDLE; Status = USBH_OK; } } return Status; } /** * @brief USBH_AUDIO_Resume * Resume the playback process * @param phost: Host handle * @retval USBH Status */ USBH_StatusTypeDef USBH_AUDIO_Resume(USBH_HandleTypeDef *phost) { USBH_StatusTypeDef Status = USBH_FAIL; AUDIO_HandleTypeDef *AUDIO_Handle; if (phost->gState == HOST_CLASS) { AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->play_state == AUDIO_PLAYBACK_IDLE) { AUDIO_Handle->control_state = AUDIO_CONTROL_INIT; AUDIO_Handle->play_state = AUDIO_PLAYBACK_PLAY; } } return Status; } /** * @brief USBH_AUDIO_GetOutOffset * return the current buffer pointer for OUT process * @param phost: Host handle * @retval USBH Status */ int32_t USBH_AUDIO_GetOutOffset(USBH_HandleTypeDef *phost) { AUDIO_HandleTypeDef *AUDIO_Handle; if (phost->gState == HOST_CLASS) { AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->play_state == AUDIO_PLAYBACK_PLAY) { return (int32_t)AUDIO_Handle->headphone.partial_ptr; } } return -1; } /** * @brief USBH_AUDIO_ChangeOutBuffer * Change audio data buffer address * @param phost: Host handle * @param buf: buffer address * @retval USBH Status */ USBH_StatusTypeDef USBH_AUDIO_ChangeOutBuffer(USBH_HandleTypeDef *phost, uint8_t *buf) { USBH_StatusTypeDef Status = USBH_FAIL; AUDIO_HandleTypeDef *AUDIO_Handle; if (phost->gState == HOST_CLASS) { AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->play_state == AUDIO_PLAYBACK_PLAY) { if (AUDIO_Handle->headphone.buf <= buf) { AUDIO_Handle->headphone.cbuf = buf; if (AUDIO_Handle->headphone.buf == buf) { AUDIO_Handle->headphone.partial_ptr = 0U; } Status = USBH_OK; } } } return Status; } /** * @brief USBH_AUDIO_SetControlAttribute * Set Control Attribute * @param phost: Host handle * @param attrib: control attribute * @retval USBH Status */ static USBH_StatusTypeDef USBH_AUDIO_SetControlAttribute(USBH_HandleTypeDef *phost, uint8_t attrib) { USBH_StatusTypeDef status = USBH_BUSY; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; switch (attrib) { case 0x01: AUDIO_Handle->headphone.attribute.volume += AUDIO_Handle->headphone.attribute.resolution; break; case 0x02: AUDIO_Handle->headphone.attribute.volume -= AUDIO_Handle->headphone.attribute.resolution; break; default : status = USBH_FAIL; break; } if (AUDIO_Handle->headphone.attribute.volume > AUDIO_Handle->headphone.attribute.volumeMax) { AUDIO_Handle->headphone.attribute.volume = AUDIO_Handle->headphone.attribute.volumeMax; } if (AUDIO_Handle->headphone.attribute.volume < AUDIO_Handle->headphone.attribute.volumeMin) { AUDIO_Handle->headphone.attribute.volume = AUDIO_Handle->headphone.attribute.volumeMin; } if (AUDIO_SetVolume(phost, AUDIO_Handle->temp_feature, (uint8_t)AUDIO_Handle->temp_channels, (uint16_t)AUDIO_Handle->headphone.attribute.volume) != USBH_BUSY) { if (AUDIO_Handle->temp_channels == 1U) { AUDIO_Handle->temp_feature = AUDIO_Handle->headphone.asociated_feature; AUDIO_Handle->temp_channels = AUDIO_Handle->headphone.asociated_channels; status = USBH_OK; } else { AUDIO_Handle->temp_channels--; } AUDIO_Handle->cs_req_state = AUDIO_REQ_GET_VOLUME; } return status; } /** * @brief USBH_AUDIO_SetVolume * Set Volume * @param phost: Host handle * @param volume: VOLUME_UP/ VOLUME_DOWN * @retval USBH Status */ USBH_StatusTypeDef USBH_AUDIO_SetVolume(USBH_HandleTypeDef *phost, AUDIO_VolumeCtrlTypeDef volume_ctl) { AUDIO_HandleTypeDef *AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if ((volume_ctl == VOLUME_UP) || (volume_ctl == VOLUME_DOWN)) { if (phost->gState == HOST_CLASS) { AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; if (AUDIO_Handle->play_state == AUDIO_PLAYBACK_PLAY) { AUDIO_Handle->control_state = (volume_ctl == VOLUME_UP) ? AUDIO_CONTROL_VOLUME_UP : AUDIO_CONTROL_VOLUME_DOWN; return USBH_OK; } } } return USBH_FAIL; } /** * @brief AUDIO_SetVolume * Set Volume * @param phost: Host handle * @param feature: feature Unit index * @param channel: channel index * @param volume: new volume * @retval USBH Status */ static USBH_StatusTypeDef AUDIO_SetVolume(USBH_HandleTypeDef *phost, uint8_t feature, uint8_t channel, uint16_t volume) { USBH_StatusTypeDef status = USBH_BUSY ; AUDIO_HandleTypeDef *AUDIO_Handle; AUDIO_Handle = (AUDIO_HandleTypeDef *) phost->pActiveClass->pData; AUDIO_Handle->mem[0] = volume; status = USBH_AC_SetCur(phost, UAC_FEATURE_UNIT, feature, VOLUME_CONTROL, channel, 2U); return status; } /** * @brief The function informs user that Settings have been changed * @param phost: Selected device * @retval None */ __weak void USBH_AUDIO_FrequencySet(USBH_HandleTypeDef *phost) { /* Prevent unused argument(s) compilation warning */ UNUSED(phost); } /** * @brief The function informs user that User data are processed * @param phost: Selected device * @retval None */ __weak void USBH_AUDIO_BufferEmptyCallback(USBH_HandleTypeDef *phost) { /* Prevent unused argument(s) compilation warning */ UNUSED(phost); } /** * @} */ /** * @} */ /** * @} */ /** * @} */ /** * @} */ /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/