/*************************************************************************** * Copyright (c) 2024 Microsoft Corporation * Copyright (c) 2026-present Eclipse ThreadX contributors * * This program and the accompanying materials are made available under the * terms of the MIT License which is available at * https://opensource.org/licenses/MIT. * * SPDX-License-Identifier: MIT **************************************************************************/ /**************************************************************************/ /**************************************************************************/ /** */ /** USBX Component */ /** */ /** Device Audio Class */ /** */ /**************************************************************************/ /**************************************************************************/ #define UX_SOURCE_CODE /* Include necessary system files. */ #include "ux_api.h" #include "ux_device_class_audio.h" #include "ux_device_class_audio20.h" #include "ux_device_stack.h" /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _ux_device_class_audio20_control_process PORTABLE C */ /* 6.2.1 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function manages the based sent by the host on the control */ /* endpoints with a CLASS or VENDOR SPECIFIC type. */ /* */ /* INPUT */ /* */ /* audio Address of audio class */ /* instance */ /* transfer Address of transfer request */ /* instance */ /* group Request process data */ /* */ /* OUTPUT */ /* */ /* Completion Status */ /* */ /* CALLS */ /* */ /* _ux_utility_short_get Get 2-byte value from buffer */ /* _ux_utility_short_put Put 2-byte value to buffer */ /* _ux_utility_long_get Get 4-byte value from buffer */ /* _ux_utility_long_put Put 4-byte value to buffer */ /* _ux_utility_memory_copy Copy memory */ /* _ux_device_stack_transfer_request Issue a transfer request */ /* _ux_device_stack_endpoint_stall Endpoint stall */ /* */ /* CALLED BY */ /* */ /* Application */ /* */ /**************************************************************************/ UINT _ux_device_class_audio20_control_process(UX_DEVICE_CLASS_AUDIO *audio, UX_SLAVE_TRANSFER *transfer, UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP *group) { UX_SLAVE_ENDPOINT *endpoint; UX_DEVICE_CLASS_AUDIO20_CONTROL *control; UCHAR request; UCHAR request_type; UCHAR unit_id; UCHAR control_selector; UCHAR channel_number; ULONG request_length; ULONG data_length; ULONG i; ULONG n_sub, pos, min, max, res, freq; /* Get instances. */ endpoint = &audio -> ux_device_class_audio_device -> ux_slave_device_control_endpoint; transfer = &endpoint -> ux_slave_endpoint_transfer_request; /* Extract all necessary fields of the request. */ request = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST); request_type = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_REQUEST_TYPE); unit_id = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_ENEITY_ID); control_selector = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CONTROL_SELECTOR); channel_number = *(transfer -> ux_slave_transfer_request_setup + UX_DEVICE_CLASS_AUDIO_REQUEST_CHANNEL_NUMBER); request_length = _ux_utility_short_get(transfer -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH); for (i = 0; i < group -> ux_device_class_audio20_control_group_controls_nb; i ++) { control = &group -> ux_device_class_audio20_control_group_controls[i]; /* Reset change map. */ control -> ux_device_class_audio20_control_changed = 0; /* Is this request a clock unit request? */ if (unit_id == control -> ux_device_class_audio20_control_cs_id) { /* Clock Source request. * We only support Sampling Frequency Control here. * The Sampling Frequency Control must support the CUR and RANGE(MIN, MAX, RES) attributes. */ /* Sampling frequency control, SET request. */ if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_OUT && (control_selector == UX_DEVICE_CLASS_AUDIO20_CS_SAM_FREQ_CONTROL)) { switch(request) { case UX_DEVICE_CLASS_AUDIO20_CUR: /* Check request parameter. */ if (request_length != 4) break; /* Check if multiple frequency supported. */ if (control -> ux_device_class_audio20_control_sampling_frequency != 0) break; /* Sanity check. */ UX_ASSERT(control -> ux_device_class_audio20_control_sampling_frequency_range != UX_NULL); /* Get wNumSubRanges. */ n_sub = _ux_utility_short_get(control -> ux_device_class_audio20_control_sampling_frequency_range); /* Get first RES. */ res = _ux_utility_long_get(control -> ux_device_class_audio20_control_sampling_frequency_range + 2 + 8); /* Check if it's fixed single frequency. */ if (n_sub <= 1 && res == 0) break; /* Get frequency to set. */ freq = _ux_utility_long_get(transfer -> ux_slave_transfer_request_data_pointer); /* Check if frequency to set is inside range. */ for (pos = 2; pos < (2 + n_sub * 12); pos += 12) { min = _ux_utility_long_get(control -> ux_device_class_audio20_control_sampling_frequency_range + pos); max = _ux_utility_long_get(control -> ux_device_class_audio20_control_sampling_frequency_range + pos + 4); if (freq >= min && freq <= max) { /* SET_CUR is accepted. */ if (control -> ux_device_class_audio20_control_sampling_frequency_cur != freq) { control -> ux_device_class_audio20_control_sampling_frequency_cur = freq; control -> ux_device_class_audio20_control_changed = UX_DEVICE_CLASS_AUDIO20_CONTROL_FREQUENCY_CHANGED; } return(UX_SUCCESS); } } break; default: break; } } /* We just support sampling frequency control, GET request. */ if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN && (control_selector == UX_DEVICE_CLASS_AUDIO20_CS_SAM_FREQ_CONTROL)) { switch(request) { case UX_DEVICE_CLASS_AUDIO20_CUR: /* Check request parameter. */ if (request_length < 4) break; /* Send sampling frequency. */ if (control -> ux_device_class_audio20_control_sampling_frequency) _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer, control -> ux_device_class_audio20_control_sampling_frequency); else _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer, control -> ux_device_class_audio20_control_sampling_frequency_cur); _ux_device_stack_transfer_request(transfer, 4, request_length); return(UX_SUCCESS); case UX_DEVICE_CLASS_AUDIO20_RANGE: /* Check request parameter. */ if (request_length < 2) break; if (control -> ux_device_class_audio20_control_sampling_frequency == 0) { /* Send range parameters, RANGE is customized. */ UX_ASSERT(control -> ux_device_class_audio20_control_sampling_frequency_range != UX_NULL); /* Get wNumSubRanges. */ n_sub = _ux_utility_short_get(control -> ux_device_class_audio20_control_sampling_frequency_range); UX_ASSERT(n_sub > 0); /* Calculate length, n_sub is 16-bit width, result not overflows ULONG. */ data_length = 2 + n_sub * 12; UX_ASSERT(data_length <= UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH); /* Copy data. */ data_length = UX_MIN(data_length, request_length); _ux_utility_memory_copy(transfer -> ux_slave_transfer_request_data_pointer, control -> ux_device_class_audio20_control_sampling_frequency_range, data_length); /* Use case of memcpy is verified. */ } else { /* Send range parameters. * We only support one here (from extension data). * wNumSubRanges : 1 * dMIN : sampling frequency * dMAX : sampling frequency * dRES : 1 */ _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer, 1); _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 2, control -> ux_device_class_audio20_control_sampling_frequency); _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 6, control -> ux_device_class_audio20_control_sampling_frequency); _ux_utility_long_put(transfer -> ux_slave_transfer_request_data_pointer + 10, 0); data_length = UX_MIN(14, request_length); } /* Send data. */ _ux_device_stack_transfer_request(transfer, data_length, request_length); return(UX_SUCCESS); default: break; } } /* We are here when there is error on handling CU, break. */ break; } /* if (unit_id == control -> ux_device_class_audio20_control_cs_id) */ /* Is this request a feature unit request? */ if (unit_id == control -> ux_device_class_audio20_control_fu_id) { /* Feature Unit request. * We only support master channel, mute and volume here. * Mute have only the CUR. * Volume must support CUR and RANGE(MIN,MAX,RES). */ /* Check control selector. */ switch(control_selector) { case UX_DEVICE_CLASS_AUDIO20_FU_MUTE_CONTROL: /* Check channel number, we support master channel only. */ if (channel_number != 0) break; /* Handle request. */ switch(request) { case UX_DEVICE_CLASS_AUDIO20_CUR: /* Check buffer size. */ if (request_length < 1) break; /* GET_CUR. */ if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) { /* Send mute. */ transfer -> ux_slave_transfer_request_data_pointer[0] = (UCHAR)control -> ux_device_class_audio20_control_mute[0]; _ux_device_stack_transfer_request(transfer, 1, request_length); } else if (control -> ux_device_class_audio20_control_mute[0] != transfer -> ux_slave_transfer_request_data_pointer[0]) { /* Update mute. */ control -> ux_device_class_audio20_control_changed = UX_DEVICE_CLASS_AUDIO20_CONTROL_MUTE_CHANGED; control -> ux_device_class_audio20_control_mute[0] = transfer -> ux_slave_transfer_request_data_pointer[0]; } /* Done success. */ return(UX_SUCCESS); default: break; } break; case UX_DEVICE_CLASS_AUDIO20_FU_VOLUME_CONTROL: /* Check channel number, we support master channel only. */ if (channel_number != 0) break; /* Handle request. */ switch(request) { case UX_DEVICE_CLASS_AUDIO20_CUR: /* Check buffer size. */ if (request_length < 2) break; /* GET_CUR. */ if ((request_type & UX_REQUEST_DIRECTION) == UX_REQUEST_IN) { /* Send volume. */ _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer, (USHORT)control -> ux_device_class_audio20_control_volume[0]); _ux_device_stack_transfer_request(transfer, 2, request_length); } else if (control -> ux_device_class_audio20_control_volume[0] != (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer)) { /* Update volume. */ control -> ux_device_class_audio20_control_changed = UX_DEVICE_CLASS_AUDIO20_CONTROL_VOLUME_CHANGED; control -> ux_device_class_audio20_control_volume[0] = (SHORT)_ux_utility_short_get(transfer -> ux_slave_transfer_request_data_pointer); } /* Done success. */ return(UX_SUCCESS); case UX_DEVICE_CLASS_AUDIO20_RANGE: /* Only support GET_CUR. */ if ((request_type & UX_REQUEST_DIRECTION) != UX_REQUEST_IN) break; /* Check buffer size. */ if (request_length < 8) break; /* Send wNumSubRanges,wMIN,wMAX,wRES. */ _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 0, 1); _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 2, (USHORT)control -> ux_device_class_audio20_control_volume_min[0]); _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 4, (USHORT)control -> ux_device_class_audio20_control_volume_max[0]); _ux_utility_short_put(transfer -> ux_slave_transfer_request_data_pointer + 6, (USHORT)UX_MAX(1, control -> ux_device_class_audio20_control_volume_res[0])); _ux_device_stack_transfer_request(transfer, 8, request_length); /* Done success. */ return(UX_SUCCESS); default: break; } break; default: break; } /* We are here when there is error on handling FU, break. */ break; } /* if (unit_id == control -> ux_device_class_audio20_control_fu_id) */ } /* Request or parameter not supported. */ _ux_device_stack_endpoint_stall(endpoint); return(UX_ERROR); } /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _uxe_device_class_audio20_control_process PORTABLE C */ /* 6.2.1 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function checks errors in control request processing function */ /* call. */ /* */ /* INPUT */ /* */ /* audio Address of audio class */ /* instance */ /* transfer Address of transfer request */ /* instance */ /* group Request process data */ /* */ /* OUTPUT */ /* */ /* Completion Status */ /* */ /* CALLS */ /* */ /* _ux_device_class_audio20_control_process */ /* Process control requests */ /* */ /* CALLED BY */ /* */ /* Application */ /* */ /**************************************************************************/ UINT _uxe_device_class_audio20_control_process(UX_DEVICE_CLASS_AUDIO *audio, UX_SLAVE_TRANSFER *transfer, UX_DEVICE_CLASS_AUDIO20_CONTROL_GROUP *group) { /* Sanity checks. */ if (audio == UX_NULL || transfer == UX_NULL || group == UX_NULL) return(UX_INVALID_PARAMETER); /* Process control requests. */ return(_ux_device_class_audio20_control_process(audio, transfer, group)); }