/***************************************************************************
 * 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_task_run               PORTABLE C      */
/*                                                           6.2.1        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function is the background task of the CCID.                   */
/*                                                                        */
/*    It's for standalone mode.                                           */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    ccid                                  Pointer to CCID class         */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    State machine status                                                */
/*    UX_STATE_EXIT                         Device not configured         */
/*    UX_STATE_IDLE                         No streaming transfer running */
/*    UX_STATE_WAIT                         Streaming transfer running    */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    USBX Device Stack                                                   */
/*                                                                        */
/**************************************************************************/
UINT _ux_device_class_ccid_runner_task_run(UX_DEVICE_CLASS_CCID *ccid)
{

UX_SLAVE_DEVICE                                     *device;
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                                               cmd_checks;
UINT                                                status;
INT                                                 immediate_state = UX_TRUE;


    /* Get the pointer to the device.  */
    device =  &_ux_system_slave -> ux_system_slave_device;

    /* Check if the device is configured.  */
    if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
        return(UX_STATE_EXIT);

    /* Get CCID runner (optimized only 1 runner).  */
    runner = ccid -> ux_device_class_ccid_runners;

    /* Slot to handle (optimized for only one slot).  */
    slot = ccid -> ux_device_class_ccid_slots;

    /* Get CCID parameter.  */
    parameter = &ccid -> ux_device_class_ccid_parameter;

    /* Get CCID messages.  */
    messages = &runner -> ux_device_class_ccid_runner_messages;

    /* Get command and response buffers.  */
    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;

    /* Process states.  */
    while(immediate_state)
    {

        /* Check states.  */
        switch(runner -> ux_device_class_ccid_runner_state)
        {
        case UX_DEVICE_CLASS_CCID_RUNNER_IDLE:
            return(UX_STATE_IDLE);

        case UX_DEVICE_CLASS_CCID_RUNNER_START:

            /* 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;

                /* Send response.  */
                _ux_device_class_ccid_response(ccid, (UCHAR *)rsp,
                            messages -> ux_device_class_ccid_messages_rdr_to_pc_length);

                /* Runner is idle (status is updated after response sent).  */
                runner -> ux_device_class_ccid_runner_state = UX_DEVICE_CLASS_CCID_RUNNER_IDLE;
                continue;
            }

            /* Check auto sequencing!  */
            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;

                /* Send response.  */
                _ux_device_class_ccid_response(ccid, (UCHAR *)rsp,
                            messages -> ux_device_class_ccid_messages_rdr_to_pc_length);

                /* Runner is idle (status is updated after response sent).  */
                runner -> ux_device_class_ccid_runner_state = UX_DEVICE_CLASS_CCID_RUNNER_IDLE;
                continue;
            }

            /* Get command to process, application can fill status.  */
            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;
            }

            /* Save handle that is running.  */
            runner -> ux_device_class_ccid_runner_handle = handle;

            /* Fall through.  */
        case UX_DEVICE_CLASS_CCID_RUNNER_HANDLE:

            /* Run handle (state machine).  */
            status = runner -> ux_device_class_ccid_runner_handle(cmd -> bSlot, messages);

            /* Error cases.  */
            if (status < UX_STATE_NEXT ||
                slot -> ux_device_class_ccid_slot_aborting)
            {

                /* There is no response in this case.  */
                runner -> ux_device_class_ccid_runner_state = UX_DEVICE_CLASS_CCID_RUNNER_IDLE;
                return(UX_STATE_IDLE);
            }

            /* Done case.  */
            if (status == UX_STATE_NEXT)
            {

                /* Next: response start.  */
                runner -> ux_device_class_ccid_runner_state = UX_DEVICE_CLASS_CCID_RUNNER_RSP_START;
                continue;
            }

            /* Waiting.  */
            return(UX_STATE_WAIT);

        case UX_DEVICE_CLASS_CCID_RUNNER_RSP_START:

            /* Wait response idle.  */
            if (ccid -> ux_device_class_ccid_rsp_state != UX_DEVICE_CLASS_CCID_RSP_IDLE)
                return(UX_STATE_WAIT);

            /* Send response.  */
            _ux_device_class_ccid_response(ccid, (UCHAR *)rsp,
                        messages -> ux_device_class_ccid_messages_rdr_to_pc_length);

            /* Runner is idle (status is updated after response sent).  */
            runner -> ux_device_class_ccid_runner_state = UX_DEVICE_CLASS_CCID_RUNNER_IDLE;
            continue;

        default:
            break;
        }

        /* Break the loop.  */
        immediate_state = UX_FALSE;
    }

    /* Unhandled state.  */
    return(UX_STATE_EXIT);
}

#endif
