/*************************************************************************** * 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 */ /** */ /** HID Class */ /** */ /**************************************************************************/ /**************************************************************************/ /* Include necessary system files. */ #define UX_SOURCE_CODE #include "ux_api.h" #include "ux_host_class_hid.h" #include "ux_host_stack.h" /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _ux_host_class_hid_transfer_request_completed PORTABLE C */ /* 6.2.1 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function is called by the completion thread when a transfer */ /* request has been completed either because the transfer is */ /* successful or there was an error. */ /* */ /* INPUT */ /* */ /* transfer_request Pointer to transfer request */ /* */ /* OUTPUT */ /* */ /* None */ /* */ /* CALLS */ /* */ /* (ux_host_class_hid_report_callback_function) */ /* Callback function for report */ /* _ux_host_class_hid_report_decompress Decompress HID report */ /* _ux_host_stack_transfer_request Process transfer request */ /* _ux_utility_memory_allocate Allocate memory block */ /* _ux_utility_memory_free Release memory block */ /* */ /* CALLED BY */ /* */ /* HID Class */ /* */ /**************************************************************************/ VOID _ux_host_class_hid_transfer_request_completed(UX_TRANSFER *transfer_request) { UX_HOST_CLASS_HID *hid; UX_HOST_CLASS_HID_CLIENT *hid_client; UX_HOST_CLASS_HID_REPORT *hid_report; UINT status; VOID *report_buffer; UX_HOST_CLASS_HID_REPORT_CALLBACK callback; UX_HOST_CLASS_HID_CLIENT_REPORT client_report; ULONG *client_buffer; UX_HOST_CLASS_HID_FIELD *hid_field; ULONG field_report_count; /* Set Status to success. Optimistic view. */ status = UX_SUCCESS; /* Get the class instance for this transfer request. */ hid = (UX_HOST_CLASS_HID *) transfer_request -> ux_transfer_request_class_instance; /* Check the state of the transfer. If there is an error, we do not proceed with this report. */ if (transfer_request -> ux_transfer_request_completion_code != UX_SUCCESS) { /* We have an error. We do not rehook another transfer if the device instance is shutting down or if the transfer was aborted by the class. */ if ((hid -> ux_host_class_hid_state == UX_HOST_CLASS_INSTANCE_SHUTDOWN) || (transfer_request -> ux_transfer_request_completion_code == UX_TRANSFER_STATUS_ABORT)) /* We do not proceed. */ return; else { /* Reactivate the HID interrupt pipe. */ _ux_host_stack_transfer_request(transfer_request); /* We do not proceed. */ return; } } /* Get the client instance attached to the HID. */ hid_client = hid -> ux_host_class_hid_client; /* Get the pointer to the report buffer in the transfer request. */ report_buffer = transfer_request -> ux_transfer_request_data_pointer; /* We know this incoming report is for the Input report. */ hid_report = hid -> ux_host_class_hid_parser.ux_host_class_hid_parser_input_report; /* If there are multiple HID reports, report ID must be checked. */ if (hid_report -> ux_host_class_hid_report_next_report != UX_NULL) { /* Scan the reports to find the report expected. */ while(1) { /* Check report ID at buffer start. */ if (*(UCHAR*)report_buffer == hid_report -> ux_host_class_hid_report_id) break; /* If there is no more report, it's done. */ if (hid_report -> ux_host_class_hid_report_next_report == UX_NULL) break; /* There is more reports, next. */ hid_report = hid_report -> ux_host_class_hid_report_next_report; } /* Check if the report is what we expected. */ if (*(UCHAR*)report_buffer != hid_report -> ux_host_class_hid_report_id) /* Report not found. */ hid_report = UX_NULL; } /* For this report to be used, the HID client must have registered the report. We check the call back function. */ if ((hid_report != UX_NULL) && (hid_report -> ux_host_class_hid_report_callback_function != UX_NULL)) { /* initialize some of the callback structure which are generic to any reporting method. */ callback.ux_host_class_hid_report_callback_client = hid_client; callback.ux_host_class_hid_report_callback_id = hid_report -> ux_host_class_hid_report_id; /* The report is now in memory in a raw format the application may desire to handle it that way! */ if (hid_report -> ux_host_class_hid_report_callback_flags & UX_HOST_CLASS_HID_REPORT_RAW) { /* Put the length of the report in raw form in the callers callback structure. */ callback.ux_host_class_hid_report_callback_actual_length = transfer_request -> ux_transfer_request_actual_length; callback.ux_host_class_hid_report_callback_buffer = report_buffer; /* Build the callback structure status. */ callback.ux_host_class_hid_report_callback_status = status; /* Set the flags to indicate the type of report. */ callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags; /* Call the report owner. */ hid_report -> ux_host_class_hid_report_callback_function(&callback); } else { /* The report may be decompressed, buffer length is based on number of items in report. Each item is a pair of words (usage and the value itself), so the required decompress memory for each item is 4 (word size) * 2 (number of word) = 8 bytes. To accelerate we shift number of item by 3 to get the result. */ if (UX_OVERFLOW_CHECK_MULC_ULONG(hid_report->ux_host_class_hid_report_number_item, 8)) client_buffer = UX_NULL; else { client_report.ux_host_class_hid_client_report_length = hid_report->ux_host_class_hid_report_number_item << 3; /* We need to allocate some memory to build the decompressed report. */ client_buffer = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, client_report.ux_host_class_hid_client_report_length); } /* Check completion status. */ if (client_buffer == UX_NULL) { /* We have an error of memory, do not proceed */ status = UX_MEMORY_INSUFFICIENT; } else { /* We need to build a client structure to be used by the decompression engine. */ client_report.ux_host_class_hid_client_report_buffer = client_buffer; client_report.ux_host_class_hid_client_report_actual_length = 0; client_report.ux_host_class_hid_client_report = hid_report; /* The report buffer must be parsed and decompressed into the local buffer. */ _ux_host_class_hid_report_decompress(hid, &client_report, report_buffer, transfer_request -> ux_transfer_request_actual_length); /* The report may be decompressed and returned as individual Usages. */ if (hid_report -> ux_host_class_hid_report_callback_flags & UX_HOST_CLASS_HID_REPORT_INDIVIDUAL_USAGE) { /* Now, we need to call the HID client call back function with a usage/value couple. */ hid_field = hid_report -> ux_host_class_hid_report_field; /* Set the flags to indicate the type of report (usage/value couple). */ callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags; /* The length of the buffer is irrelevant here so we reset it. */ callback.ux_host_class_hid_report_callback_actual_length = 0; /* Scan all the fields and send each usage/value. */ while(hid_field != UX_NULL) { /* Build each report item. */ for (field_report_count = 0; field_report_count < hid_field -> ux_host_class_hid_field_report_count; field_report_count++) { /* Insert the usage and the report value into the callback structure. */ callback.ux_host_class_hid_report_callback_usage = *client_report.ux_host_class_hid_client_report_buffer++; callback.ux_host_class_hid_report_callback_value = *client_report.ux_host_class_hid_client_report_buffer++; /* Build the callback structure status. */ callback.ux_host_class_hid_report_callback_status = status; /* Call the report owner */ hid_report -> ux_host_class_hid_report_callback_function(&callback); } /* Get the next field. */ hid_field = hid_field -> ux_host_class_hid_field_next_field; } } else { /* Add the length actually valid in the caller's buffer. */ callback.ux_host_class_hid_report_callback_actual_length = client_report.ux_host_class_hid_client_report_actual_length; /* Add the caller's buffer address. */ callback.ux_host_class_hid_report_callback_buffer = client_report.ux_host_class_hid_client_report_buffer; /* Build the callback structure status. */ callback.ux_host_class_hid_report_callback_status = status; /* Set the flags to indicate the type of report. */ callback.ux_host_class_hid_report_callback_flags = hid_report -> ux_host_class_hid_report_callback_flags; /* Call the report owner. */ hid_report -> ux_host_class_hid_report_callback_function(&callback); } /* Free the memory resource we used. */ _ux_utility_memory_free(client_buffer); } } } /* Check latest status. */ if (status != UX_SUCCESS) /* Error trap. */ _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); /* Reactivate the HID interrupt pipe. */ status = _ux_host_stack_transfer_request(transfer_request); /* Check latest status. */ if (status != UX_SUCCESS) /* Error trap. */ _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status); /* Return to caller. */ return; }