/*************************************************************************** * 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 CCID Class */ /** */ /**************************************************************************/ /**************************************************************************/ #define UX_SOURCE_CODE /* Include necessary system files. */ #include "ux_api.h" #include "ux_device_class_ccid.h" #include "ux_device_stack.h" #if !defined(UX_DEVICE_STANDALONE) /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _ux_device_class_ccid_runner_thread_entry PORTABLE C */ /* 6.2.1 */ /* AUTHOR */ /* */ /* Chaoqiong Xiao, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function is the thread of the CCID command handling. It */ /* is waiting for the event to start command processing. */ /* */ /* INPUT */ /* */ /* ccid_runner Address of CCID runner (32b) */ /* */ /* OUTPUT */ /* */ /* None */ /* */ /* CALLS */ /* */ /* _ux_device_stack_transfer_request Request transfer */ /* */ /* CALLED BY */ /* */ /* ThreadX */ /* */ /**************************************************************************/ VOID _ux_device_class_ccid_runner_thread_entry(ULONG ccid_runner) { UX_SLAVE_DEVICE *device; UX_DEVICE_CLASS_CCID *ccid; UX_DEVICE_CLASS_CCID_PARAMETER *parameter; UX_DEVICE_CLASS_CCID_HANDLE *handles; UX_DEVICE_CLASS_CCID_HANDLE handle; UX_DEVICE_CLASS_CCID_RUNNER *runner; UX_DEVICE_CLASS_CCID_SLOT *slot; UX_DEVICE_CLASS_CCID_MESSAGE_HEADER *cmd; UX_DEVICE_CLASS_CCID_COMMAND_SETT *cmd_sett; UX_DEVICE_CLASS_CCID_RDR_TO_PC_SLOT_STATUS_HEADER *rsp; UX_DEVICE_CLASS_CCID_MESSAGES messages; ULONG event_mask, flags; ULONG cmd_checks; UINT status; /* Cast properly the ccid runner. */ UX_THREAD_EXTENSION_PTR_GET(runner, UX_DEVICE_CLASS_CCID_RUNNER, ccid_runner) /* Get the pointer to the device. */ device = &_ux_system_slave -> ux_system_slave_device; /* Get CCID instance. */ ccid = runner -> ux_device_class_ccid_runner_ccid; /* Get CCID parameter. */ parameter = &ccid -> ux_device_class_ccid_parameter; /* Get event mask to wait. */ event_mask = 1u << runner -> ux_device_class_ccid_runner_id; /* This thread runs forever but can be suspended or resumed by the user application. */ status = UX_SUCCESS; while(1) { /* Check device state. */ if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED) { _ux_utility_thread_suspend(&runner -> ux_device_class_ccid_runner_thread); continue; } /* Wait signal. */ status = _ux_utility_event_flags_get(&ccid -> ux_device_class_ccid_events, event_mask, UX_OR_CLEAR, &flags, UX_WAIT_FOREVER); if (status != UX_SUCCESS) { _ux_utility_thread_suspend(&runner -> ux_device_class_ccid_runner_thread); continue; } /* We have a command to process. */ cmd = (UX_DEVICE_CLASS_CCID_MESSAGE_HEADER *) runner -> ux_device_class_ccid_runner_command; rsp = (UX_DEVICE_CLASS_CCID_RDR_TO_PC_SLOT_STATUS_HEADER *) runner -> ux_device_class_ccid_runner_response; /* Slot to handle. */ slot = ccid -> ux_device_class_ccid_slots; slot += runner -> ux_device_class_ccid_runner_slot; /* Command settings. */ cmd_sett = (UX_DEVICE_CLASS_CCID_COMMAND_SETT *)_ux_device_class_ccid_command_sett; cmd_sett += runner -> ux_device_class_ccid_runner_command_index; /* Message to pass to application. */ messages.ux_device_class_ccid_messages_pc_to_rdr = (UCHAR *)cmd; messages.ux_device_class_ccid_messages_rdr_to_pc = (UCHAR *)rsp; /* Internal checks. */ cmd_checks = (ULONG)cmd_sett -> ux_device_class_ccid_command_sett_flags; cmd_checks &= (ULONG)slot -> ux_device_class_ccid_slot_flags; /* Check hardware error! */ if (cmd_checks & UX_DEVICE_CLASS_CCID_FLAG_HW_ERROR) { /* Response: (1,1,HW_ERROR). */ rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(1, 1); rsp -> bError = UX_DEVICE_CLASS_CCID_HW_ERROR; } /* Check auto sequencing! */ else if (cmd_checks & UX_DEVICE_CLASS_CCID_FLAG_AUTO_SEQUENCING) { /* Response: (1,1,BUSY_WITH_AUTO_SEQUENCE). */ rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(1, 1); rsp -> bError = UX_DEVICE_CLASS_CCID_BUSY_WITH_AUTO_SEQUENCE; } /* Process command, application can fill status. */ else { handles = (UX_DEVICE_CLASS_CCID_HANDLE *)parameter -> ux_device_class_ccid_handles; handle = handles[(INT)cmd_sett -> ux_device_class_ccid_command_sett_handle_index]; /* Initialize response length based on type. */ switch(rsp -> bMessageType) { case UX_DEVICE_CLASS_CCID_RDR_TO_PC_DATA_RATE_AND_CLOCK_FREQ: /* Length fixed to 10+8. */ messages.ux_device_class_ccid_messages_rdr_to_pc_length = 18; UX_DEVICE_CLASS_CCID_MESSAGE_LENGTH_SET(rsp, 8); break; case UX_DEVICE_CLASS_CCID_RDR_TO_PC_SLOT_STATUS: /* Length fixed to 10. */ messages.ux_device_class_ccid_messages_rdr_to_pc_length = UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH; break; default: /* There is possible data, set length to max transfer length. */ messages.ux_device_class_ccid_messages_rdr_to_pc_length = parameter -> ux_device_class_ccid_max_transfer_length; break; } /* Invoke application callback. */ status = handle(cmd -> bSlot, &messages); /* Save application status updates. */ /* Save bmICCStatus. */ slot -> ux_device_class_ccid_slot_icc_status = rsp->bStatus & UX_DEVICE_CLASS_CCID_SLOT_STATUS_ICC_MASK; /* Save bClockStatus. */ slot -> ux_device_class_ccid_slot_clock_status = rsp->bClockStatus; } /* If failed (aborted), no response. */ if (status != UX_SUCCESS) continue; if (slot -> ux_device_class_ccid_slot_aborting) continue; /* Send response. */ _ux_device_class_ccid_response(ccid, (UCHAR *)rsp, messages.ux_device_class_ccid_messages_rdr_to_pc_length); _ux_device_class_ccid_lock(ccid); /* Free runner. */ runner -> ux_device_class_ccid_runner_slot = -1; ccid -> ux_device_class_ccid_n_busy --; /* Clear slot busy. */ slot -> ux_device_class_ccid_slot_runner = -1; _ux_device_class_ccid_unlock(ccid); } } #endif