/*************************************************************************** * 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 */ /** */ /** Host Stack */ /** */ /**************************************************************************/ /**************************************************************************/ /* Include necessary system files. */ #define UX_SOURCE_CODE #include "ux_api.h" #include "ux_host_stack.h" /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _ux_host_stack_device_resources_free PORTABLE C */ /* 6.1.12 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function will free all the device resources allocated. */ /* */ /* INPUT */ /* */ /* device Device pointer */ /* */ /* OUTPUT */ /* */ /* Completion Status */ /* */ /* CALLS */ /* */ /* _ux_host_stack_endpoint_transfer_abort */ /* Abort transfer */ /* _ux_host_stack_endpoint_instance_delete */ /* Delete endpoint instance */ /* _ux_utility_memory_free Free memory block */ /* _ux_utility_memory_set Set memory with a value */ /* _ux_utility_semaphore_delete Semaphore delete */ /* _ux_utility_thread_schedule_other Sleep thread to let others */ /* run */ /* (ux_hcd_entry_function) HCD entry function */ /* */ /* CALLED BY */ /* */ /* USBX Components */ /* */ /**************************************************************************/ UINT _ux_host_stack_device_resources_free(UX_DEVICE *device) { UX_CONFIGURATION *configuration; UX_INTERFACE *interface_ptr; UX_ENDPOINT *endpoint; VOID *container; ULONG current_alternate_setting; UX_HCD *hcd; #if UX_MAX_DEVICES > 1 UINT device_address_byte_index; UINT device_address_bit_index; UCHAR device_address_byte; #endif #if defined(UX_HOST_STANDALONE) UX_DEVICE *enum_next; #endif /* If trace is enabled, insert this event into the trace buffer. */ UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_STACK_DEVICE_RESOURCE_FREE, device, 0, 0, 0, UX_TRACE_HOST_STACK_EVENTS, 0, 0) #if defined(UX_HOST_STANDALONE) /* Free possible allocated enumeration resources. */ if (device -> ux_device_flags & UX_DEVICE_FLAG_ENUM) { /* If transfer buffer is not freed, free it. */ if (device -> ux_device_enum_trans && device -> ux_device_enum_trans -> ux_transfer_request_data_pointer) { _ux_utility_memory_free(device -> ux_device_enum_trans -> ux_transfer_request_data_pointer); } /* If configuration is not attached, free it. */ if ((device -> ux_device_enum_state == UX_HOST_STACK_ENUM_CONFIG_DESCR_PARSE) || ((device -> ux_device_enum_state == UX_HOST_STACK_ENUM_TRANS_WAIT) && (device -> ux_device_enum_next_state == UX_HOST_STACK_ENUM_CONFIG_DESCR_PARSE))) { if (device -> ux_device_enum_inst.configuration && device -> ux_device_enum_inst.configuration -> ux_configuration_device == UX_NULL) { _ux_utility_memory_free(device -> ux_device_enum_inst.ptr); } } } /* Reset device flags. */ device -> ux_device_flags = 0; #endif /* Set the alternate setting to zero. */ current_alternate_setting = 0; /* Get the first configuration registered to the device. */ configuration = device -> ux_device_first_configuration; /* Parse all the configurations, remove all resources for the possible configuration. */ while (configuration != UX_NULL) { /* We have the correct configuration, search the interface(s). */ interface_ptr = configuration -> ux_configuration_first_interface; /* Parse all the interfaces. */ while (interface_ptr != UX_NULL) { /* The alternate setting 0 has the selected alternate setting value. */ if (interface_ptr -> ux_interface_descriptor.bAlternateSetting == 0) current_alternate_setting = interface_ptr -> ux_interface_current_alternate_setting; /* If this is the selected interface, we need to free all the endpoints attached to the alternate setting for this interface. */ endpoint = interface_ptr -> ux_interface_first_endpoint; /* Parse all the endpoints. */ while (endpoint != UX_NULL) { /* Check if this is the selected interface. */ if (interface_ptr -> ux_interface_descriptor.bAlternateSetting == current_alternate_setting) { /* Delete the endpoint instance first. */ _ux_host_stack_endpoint_instance_delete(endpoint); } /* Memorize the endpoint container address. */ container = (VOID *) endpoint; /* Get the next endpoint. */ endpoint = endpoint -> ux_endpoint_next_endpoint; /* Delete the endpoint container. */ _ux_utility_memory_free(container); } /* Memorize the interface container address. */ container = (VOID *) interface_ptr; /* Get the next interface. */ interface_ptr = interface_ptr -> ux_interface_next_interface; /* Delete the interface container. */ _ux_utility_memory_free(container); } /* Memorize this configuration address before we free it. */ container = (VOID *) configuration; /* Move to the next configuration in the list. */ configuration = configuration -> ux_configuration_next_configuration; /* Free the configuration. */ _ux_utility_memory_free(container); } /* If there was a copy of packed descriptor, free it. */ if (device -> ux_device_packed_configuration) { _ux_utility_memory_free(device -> ux_device_packed_configuration); /* Pointer and keep count is set NULL later while reseting instance memory. */ } /* We need the HCD address for the control endpoint removal and to free the device address. */ hcd = UX_DEVICE_HCD_GET(device); /* Was the control endpoint already created ? */ if (device -> ux_device_control_endpoint.ux_endpoint_state != 0) { /* There may be pending transactions on the control endpoint. They need to be aborted. */ _ux_host_stack_endpoint_transfer_abort(&device -> ux_device_control_endpoint); /* The enumeration thread needs to sleep a while to allow the application or the class that may be using the control endpoint to exit properly. */ _ux_host_thread_schedule_other(UX_THREAD_PRIORITY_ENUM); /* The control endpoint should be destroyed at the HCD level. */ hcd -> ux_hcd_entry_function(hcd, UX_HCD_DESTROY_ENDPOINT, (VOID *) &device -> ux_device_control_endpoint); } /* The semaphore attached to the control endpoint must be destroyed. */ _ux_host_semaphore_delete(&device -> ux_device_control_endpoint.ux_endpoint_transfer_request.ux_transfer_request_semaphore); #if UX_MAX_DEVICES > 1 /* Check if the device had an assigned address. */ if (device -> ux_device_address != 0) { /* The USB address of this device can now be returned to the pool We need the HCD pointer for this operation. */ /* Calculate in which byte index the device address belongs. */ device_address_byte_index = (UINT) (device -> ux_device_address-1)/8; /* Now calculate the amount left in the byte index in bit. */ device_address_bit_index = (UINT) (device -> ux_device_address-1)%8; /* Build the mask for the address. */ device_address_byte = (UCHAR)(1 << device_address_bit_index); /* Free the address. */ hcd -> ux_hcd_address[device_address_byte_index] &= (UCHAR)~device_address_byte; } #endif /* The semaphore for endpoint 0 protection must be destroyed. */ _ux_host_semaphore_delete(&device -> ux_device_protection_semaphore); /* Now this device can be free and its container return to the pool. */ #if defined(UX_HOST_STANDALONE) enum_next = device -> ux_device_enum_next; _ux_utility_memory_set(device, 0, sizeof(UX_DEVICE)); /* Use case of memset is verified. */ device -> ux_device_enum_next = enum_next; #else _ux_utility_memory_set(device, 0, sizeof(UX_DEVICE)); /* Use case of memset is verified. */ #endif /* Mark the device handle as unused. */ device -> ux_device_handle = UX_UNUSED; /* Return successful completion. */ return(UX_SUCCESS); }