/*************************************************************************** * 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 Stack */ /** */ /**************************************************************************/ /**************************************************************************/ #define UX_SOURCE_CODE /* Include necessary system files. */ #include "ux_api.h" #include "ux_device_stack.h" #if (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH < UX_DEVICE_DESCRIPTOR_LENGTH) || \ (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH < UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH) || \ (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH < UX_OTG_DESCRIPTOR_LENGTH) /* #error UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH too small, please check */ /* Build option checked runtime by UX_ASSERT */ #endif /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _ux_device_stack_descriptor_send PORTABLE C */ /* 6.3.0 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function sends back the device descriptor required by the host.*/ /* */ /* INPUT */ /* */ /* descriptor_type Descriptor type */ /* descriptor_index Index of descriptor */ /* host_length Length requested by host */ /* */ /* OUTPUT */ /* */ /* Completion Status */ /* */ /* CALLS */ /* */ /* (ux_slave_dcd_function) DCD dispatch function */ /* _ux_device_stack_transfer_request Process transfer request */ /* _ux_utility_descriptor_parse Parse descriptor */ /* _ux_utility_memory_copy Memory copy */ /* _ux_utility_short_get Get short value */ /* */ /* CALLED BY */ /* */ /* Application */ /* Device Stack */ /* */ /**************************************************************************/ UINT _ux_device_stack_descriptor_send(ULONG descriptor_type, ULONG request_index, ULONG host_length) { UX_SLAVE_DCD *dcd; UX_SLAVE_DEVICE *device; ULONG descriptor_index; ULONG parsed_descriptor_index; UX_SLAVE_TRANSFER *transfer_request; UX_CONFIGURATION_DESCRIPTOR configuration_descriptor; #ifndef UX_BOS_SUPPORT_DISABLE UX_BOS_DESCRIPTOR bos_descriptor; #endif UX_SLAVE_ENDPOINT *endpoint; UCHAR *device_framework; UCHAR *device_framework_end; ULONG device_framework_length; ULONG descriptor_length; ULONG target_descriptor_length = 0; UINT status = UX_ERROR; ULONG length; UCHAR *string_memory; UCHAR *string_framework; ULONG string_framework_length; ULONG string_length; /* Build option check. */ UX_ASSERT((UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH >= UX_DEVICE_DESCRIPTOR_LENGTH) && (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH >= UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH) && (UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH >= UX_OTG_DESCRIPTOR_LENGTH)); /* If trace is enabled, insert this event into the trace buffer. */ UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_STACK_DESCRIPTOR_SEND, descriptor_type, request_index, 0, 0, UX_TRACE_DEVICE_STACK_EVENTS, 0, 0) /* Get the pointer to the DCD. */ dcd = &_ux_system_slave -> ux_system_slave_dcd; /* Get the pointer to the device. */ device = &_ux_system_slave -> ux_system_slave_device; /* Get the control endpoint associated with the device. */ endpoint = &device -> ux_slave_device_control_endpoint; /* Get the pointer to the transfer request associated with the endpoint. */ transfer_request = &endpoint -> ux_slave_endpoint_transfer_request; /* Set the direction to OUT. */ transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT; /* Isolate the descriptor index. */ descriptor_index = descriptor_type & 0xff; /* Reset the parsed index. */ parsed_descriptor_index = 0; /* Shift the descriptor type in the low byte field. */ descriptor_type = (UCHAR) ((descriptor_type >> 8) & 0xff); /* Default descriptor length is host length. */ length = host_length; /* What type of descriptor do we need to return? */ switch (descriptor_type) { case UX_DEVICE_DESCRIPTOR_ITEM: /* Setup device descriptor length. */ if (host_length > UX_DEVICE_DESCRIPTOR_LENGTH) length = UX_DEVICE_DESCRIPTOR_LENGTH; /* Fall through. */ case UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM: /* Setup qualifier descriptor length. */ if (descriptor_type == UX_DEVICE_QUALIFIER_DESCRIPTOR_ITEM && host_length > UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH) length = UX_DEVICE_QUALIFIER_DESCRIPTOR_LENGTH; /* Fall through. */ case UX_OTG_DESCRIPTOR_ITEM: /* Setup OTG descriptor length. */ if (descriptor_type == UX_OTG_DESCRIPTOR_ITEM && host_length > UX_OTG_DESCRIPTOR_LENGTH) length = UX_OTG_DESCRIPTOR_LENGTH; /* We may or may not have a device qualifier descriptor. */ device_framework = _ux_system_slave -> ux_system_slave_device_framework; device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length; device_framework_end = device_framework + device_framework_length; /* Parse the device framework and locate a device qualifier descriptor. */ while (device_framework < device_framework_end) { /* Get descriptor length. */ descriptor_length = (ULONG) *device_framework; /* Check if this is a descriptor expected. */ if (*(device_framework + 1) == descriptor_type) { /* Copy the device descriptor into the transfer request memory. */ _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, device_framework, length); /* Use case of memcpy is verified. */ /* Perform the data transfer. */ status = _ux_device_stack_transfer_request(transfer_request, length, host_length); break; } /* Adjust what is left of the device framework. */ device_framework_length -= descriptor_length; /* Point to the next descriptor. */ device_framework += descriptor_length; } break; #ifndef UX_BOS_SUPPORT_DISABLE case UX_BOS_DESCRIPTOR_ITEM: /* Fall through. */ #endif case UX_OTHER_SPEED_DESCRIPTOR_ITEM: /* Fall through. */ case UX_CONFIGURATION_DESCRIPTOR_ITEM: if (descriptor_type == UX_OTHER_SPEED_DESCRIPTOR_ITEM) { /* This request is used by the host to find out the capability of this device if it was running at full speed. The behavior is the same as in a GET_CONFIGURATIOn descriptor but we do not use the current device framework but rather the full speed framework. */ device_framework = _ux_system_slave -> ux_system_slave_device_framework_full_speed; device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length_full_speed; device_framework_end = device_framework + device_framework_length; } else { /* We may have multiple configurations !, the index will tell us what configuration descriptor we need to return. */ device_framework = _ux_system_slave -> ux_system_slave_device_framework; device_framework_length = _ux_system_slave -> ux_system_slave_device_framework_length; device_framework_end = device_framework + device_framework_length; } /* Parse the device framework and locate a configuration descriptor. */ while (device_framework < device_framework_end) { /* Get descriptor length. */ descriptor_length = (ULONG) *device_framework; #ifndef UX_BOS_SUPPORT_DISABLE /* Check if we are finding BOS descriptor. */ if (descriptor_type == UX_BOS_DESCRIPTOR_ITEM) { if (*(device_framework + 1) == UX_BOS_DESCRIPTOR_ITEM) { /* Parse the BOS descriptor. */ _ux_utility_descriptor_parse(device_framework, _ux_system_bos_descriptor_structure, UX_BOS_DESCRIPTOR_ENTRIES, (UCHAR *) &bos_descriptor); /* Get the length of entire BOS descriptor. */ target_descriptor_length = bos_descriptor.wTotalLength; /* Descriptor is found. */ status = UX_SUCCESS; break; } } else #endif { /* Check if this is a configuration descriptor. We are cheating here. Instead of creating a OTHER SPEED descriptor, we simply scan the configuration descriptor for the Full Speed framework and return this configuration after we manually changed the configuration descriptor item into a Other Speed Descriptor. */ if (*(device_framework + 1) == UX_CONFIGURATION_DESCRIPTOR_ITEM) { /* Check the index. It must be the same as the one requested. */ if (parsed_descriptor_index == descriptor_index) { /* Parse the configuration descriptor. */ _ux_utility_descriptor_parse(device_framework, _ux_system_configuration_descriptor_structure, UX_CONFIGURATION_DESCRIPTOR_ENTRIES, (UCHAR *) &configuration_descriptor); /* Get the length of entire configuration descriptor. */ target_descriptor_length = configuration_descriptor.wTotalLength; /* Descriptor is found. */ status = UX_SUCCESS; break; } else { /* There may be more configuration descriptors in this framework. */ parsed_descriptor_index++; } } } /* Adjust what is left of the device framework. */ device_framework_length -= descriptor_length; /* Point to the next descriptor. */ device_framework += descriptor_length; } /* Send the descriptor. */ if (status == UX_SUCCESS) { /* Ensure the host does not demand a length beyond our descriptor (Windows does that) and do not return more than what is allowed. */ if (target_descriptor_length < host_length) length = target_descriptor_length; else length = host_length; /* Check buffer length, since total descriptors length may exceed buffer... */ if (length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) { /* Error trap. */ _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT); /* If trace is enabled, insert this event into the trace buffer. */ UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0) /* Stall the endpoint. */ status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); break; } /* Copy the device descriptor into the transfer request memory. */ _ux_utility_memory_copy(transfer_request -> ux_slave_transfer_request_data_pointer, device_framework, length); /* Use case of memcpy is verified. */ /* Now we need to hack the found descriptor because this request expect a requested descriptor type instead of the regular descriptor. */ *(transfer_request -> ux_slave_transfer_request_data_pointer + 1) = (UCHAR)descriptor_type; /* We can return the configuration descriptor. */ status = _ux_device_stack_transfer_request(transfer_request, length, host_length); } break; case UX_STRING_DESCRIPTOR_ITEM: /* We need to filter for the index 0 which is the language ID string. */ if (descriptor_index == 0) { /* We need to check request buffer size in case it's possible exceed. */ if (_ux_system_slave -> ux_system_slave_language_id_framework_length + 2 > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) { /* Error trap. */ _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT); /* If trace is enabled, insert this event into the trace buffer. */ UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0) /* Stall the endpoint. */ status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); break; } /* We have a request to send back the language ID list. Use the transfer request buffer. */ string_memory = transfer_request -> ux_slave_transfer_request_data_pointer; /* Store the total length of the response. */ *string_memory = (UCHAR)(_ux_system_slave -> ux_system_slave_language_id_framework_length + 2); /* Store the descriptor type. */ *(string_memory +1) = UX_STRING_DESCRIPTOR_ITEM; /* Store the language ID into the buffer. */ _ux_utility_memory_copy(string_memory+2, _ux_system_slave -> ux_system_slave_language_id_framework, _ux_system_slave -> ux_system_slave_language_id_framework_length); /* Use case of memcpy is verified. */ /* Filter the length asked/required. */ if (host_length > _ux_system_slave -> ux_system_slave_language_id_framework_length + 2) length = _ux_system_slave -> ux_system_slave_language_id_framework_length + 2; else length = host_length; /* We can return the string language ID descriptor. */ status = _ux_device_stack_transfer_request(transfer_request, length, host_length); } else { #ifdef UX_DEVICE_ENABLE_GET_STRING_WITH_ZERO_LANGUAGE_ID /* Check if the language ID is zero. */ if (request_index == 0) { /* Get the first language ID in the language ID framework. */ request_index = _ux_utility_short_get(_ux_system_slave -> ux_system_slave_language_id_framework); } #endif /* The host wants a specific string index returned. Get the string framework pointer and length. */ string_framework = _ux_system_slave -> ux_system_slave_string_framework; string_framework_length = _ux_system_slave -> ux_system_slave_string_framework_length; /* We search through the string framework until we find the right index. The index is in the lower byte of the descriptor type. */ while (string_framework_length != 0) { /* Ensure we have the correct language page. */ if (_ux_utility_short_get(string_framework) == request_index) { /* Check the index. */ if (*(string_framework + 2) == descriptor_index) { /* We need to check request buffer size in case it's possible exceed. */ if (((*(string_framework + 3)*2) + 2) > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH) { /* Error trap. */ _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_DEVICE_STACK, UX_MEMORY_INSUFFICIENT); /* If trace is enabled, insert this event into the trace buffer. */ UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_MEMORY_INSUFFICIENT, device, 0, 0, UX_TRACE_ERRORS, 0, 0) /* Stall the endpoint. */ status = dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); break; } /* We have a request to send back a string. Use the transfer request buffer. */ string_memory = transfer_request -> ux_slave_transfer_request_data_pointer; /* Store the length in the string buffer. The length of the string descriptor is stored in the third byte, hence the ' + 3'. The encoding must be in 16-bit unicode, hence the '*2'. The length includes the size of the length itself as well as the descriptor type, hence the ' + 2'. */ *string_memory = (UCHAR)((*(string_framework + 3)*2) + 2); /* Store the Descriptor type. */ *(string_memory + 1) = UX_STRING_DESCRIPTOR_ITEM; /* Create the Unicode string. */ for (string_length = 0; string_length < *(string_framework + 3) ; string_length ++) { /* Insert a Unicode byte. */ *(string_memory + 2 + (string_length * 2)) = *(string_framework + 4 + string_length); /* Insert a zero after the Unicode byte. */ *(string_memory + 2 + (string_length * 2) + 1) = 0; } /* Filter the length asked/required. */ if (host_length > (UINT)((*(string_framework + 3)*2) + 2)) length = (ULONG)((*(string_framework + 3)*2) + 2); else length = host_length; /* We can return the string descriptor. */ status = _ux_device_stack_transfer_request(transfer_request, length, host_length); break; } } /* This is the wrong string descriptor, jump to the next. */ string_framework_length -= (ULONG) *(string_framework + 3) + 4; string_framework += (ULONG) *(string_framework + 3) + 4; } /* Have we exhausted all the string descriptors? */ if (string_framework_length == 0) { /* Could not find the required string index. Stall the endpoint. */ dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); return(UX_ERROR); } } break; default: /* Stall the endpoint. */ dcd -> ux_slave_dcd_function(dcd, UX_DCD_STALL_ENDPOINT, endpoint); return(UX_ERROR); } /* Return the status to the caller. */ return(status); }