/***************************************************************************
 * 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 Printer Class                                                */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define UX_SOURCE_CODE


/* Include necessary system files.  */

#include "ux_api.h"
#include "ux_device_class_printer.h"
#include "ux_device_stack.h"


/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    _ux_device_class_printer_control_request            PORTABLE C      */
/*                                                           6.1.12       */
/*  AUTHOR                                                                */
/*                                                                        */
/*    Chaoqiong Xiao, Microsoft Corporation                               */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function manages the requests sent by the host on the control  */
/*    endpoint with a CLASS or VENDOR SPECIFIC type.                      */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    command                               Pointer to class command      */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    None                                                                */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    (ux_device_class_printer_soft_reset)  Notify a software reset       */
/*    _ux_device_class_printer_soft_reset   Performs software reset       */
/*    _ux_device_stack_transfer_request     Transfer request              */
/*    _ux_utility_short_get                 Get short value from buffer   */
/*    _ux_utility_short_get_big_endian      Get big endian short value    */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Device Printer Class                                                */
/*                                                                        */
/**************************************************************************/
UINT  _ux_device_class_printer_control_request(UX_SLAVE_CLASS_COMMAND *command)
{
UX_DEVICE_CLASS_PRINTER                 *printer;
UX_SLAVE_CLASS                          *printer_class;
UX_SLAVE_INTERFACE                      *printer_interface;
UX_SLAVE_TRANSFER                       *transfer_request;
UX_SLAVE_DEVICE                         *device;
ULONG                                   request;
ULONG                                   value;
ULONG                                   request_length;
ULONG                                   length;
UCHAR                                   *buffer;
UCHAR                                   index_low;
UCHAR                                   *descriptor;
ULONG                                   descriptor_length;
ULONG                                   index;
UCHAR                                   found;

    /* Get the class container.  */
    printer_class =  command -> ux_slave_class_command_class_ptr;

    /* Get the class instance in the container.  */
    printer = (UX_DEVICE_CLASS_PRINTER *) printer_class -> ux_slave_class_instance;

    /* Get the interface.  */
    printer_interface = printer_class -> ux_slave_class_interface;

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

    /* Get the pointer to the transfer request associated with the control endpoint.  */
    transfer_request =  &device -> ux_slave_device_control_endpoint.ux_slave_endpoint_transfer_request;

    /* Extract all necessary fields of the request.  */
    request =  *(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_REQUEST);

    /* Extract all necessary fields of the value.  */
    value =  _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_VALUE);
    index_low = transfer_request -> ux_slave_transfer_request_setup[UX_SETUP_INDEX];

    /* Pickup the request length.  */
    request_length =   _ux_utility_short_get(transfer_request -> ux_slave_transfer_request_setup + UX_SETUP_LENGTH);

    /* Get buffer to fill.  */
    buffer = transfer_request -> ux_slave_transfer_request_data_pointer;

    /* Here we proceed only the standard request we know of at the device level.  */
    switch (request)
    {
    case UX_DEVICE_CLASS_PRINTER_GET_DEVICE_ID:

        /* Sanity check
           - wValue == config index
           - wIndex_high == interface (already checked before entry, index of
                                       interface should equal to interface number)
           - wIndex_low == alternate  */
        if (index_low != (UCHAR)printer_interface -> ux_slave_interface_descriptor.bAlternateSetting)
            return(UX_ERROR);

        /* Check config index.  */
        descriptor = _ux_system_slave -> ux_system_slave_device_framework;
        length = _ux_system_slave->ux_system_slave_device_framework_length;
        index = 0;
        found = 0;
        while(length)
        {

            /* By default use bLength @ 0.  */
            descriptor_length = descriptor[0];

            /* Check bDescriptorType @ 1.  */
            switch(descriptor[1])
            {
            case UX_CONFIGURATION_DESCRIPTOR_ITEM:

                /* It's configuration requested?  */
                if (index == value)
                {

                    /* Check if bConfigurationValue @ 5 is as expected.  */
                    if ((UCHAR)printer_class -> ux_slave_class_configuration_number != descriptor[5])
                        return(UX_ERROR);

                    /* Find the configuration.  */
                    found = 1;
                    break;
                }

                /* Target to next configuration index.  */
                index ++;

                /* Use wTotalLength @ 2.  */
                descriptor_length = _ux_utility_short_get(descriptor + 2);
                break;

            default:
                break;
            }

            /* Break if found.  */
            if (found)
                break;

            /* Descriptor broken check.  */
            if (descriptor_length < 2 ||
                descriptor_length > length)
            {

                /* Error trap!  */
                _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_DESCRIPTOR_CORRUPTED);
                return(UX_DESCRIPTOR_CORRUPTED);
            }

            /* Next descriptor.  */
            descriptor += descriptor_length;
            length -= descriptor_length;
        }

        /* Request parameter not expected if not found.  */
        if (!found)
            return(UX_ERROR);

        /* Length of data (first two bytes in big endian).  */
        length = _ux_utility_short_get_big_endian(printer ->
                        ux_device_class_printer_parameter.ux_device_class_printer_device_id);

        /* Prepare device ID to send.  */
        if (length)
        {

            /* Sanity check.  */
            if (length > UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH)
            {

                /* Error trap!  */
                _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_BUFFER_OVERFLOW);
                return(UX_BUFFER_OVERFLOW);
            }

            /* Copy data.  */
            _ux_utility_memory_copy(buffer, printer ->
                        ux_device_class_printer_parameter.ux_device_class_printer_device_id,
                        length); /* Use case of memcpy is verified. */
        }

        /* Set the phase of the transfer to data out.  */
        transfer_request -> ux_slave_transfer_request_phase =  UX_TRANSFER_PHASE_DATA_OUT;

        /* Transfer.  */
        _ux_device_stack_transfer_request(transfer_request, length, request_length);
        break ;

    case UX_DEVICE_CLASS_PRINTER_GET_PORT_STATUS:

        /* Sanity check.  */
        if (request_length < 1)
            return(UX_ERROR);

        /* Fill status byte.  */
        *buffer = (UCHAR)printer -> ux_device_class_printer_port_status;

        /* Set the phase of the transfer to data out.  */
        transfer_request -> ux_slave_transfer_request_phase = UX_TRANSFER_PHASE_DATA_OUT;

        /* Perform the data transfer.  */
        _ux_device_stack_transfer_request(transfer_request, 1, request_length);
        break;

    case UX_DEVICE_CLASS_PRINTER_SOFT_RESET:
        _ux_device_class_printer_soft_reset(printer);

        /* If there is a soft reset function call it.  */
        if (printer -> ux_device_class_printer_parameter.
                                ux_device_class_printer_soft_reset != UX_NULL)
        {

            /* Invoke the application callback.  */
            printer -> ux_device_class_printer_parameter.
                                ux_device_class_printer_soft_reset(printer);
        }
        break;

    default:

        /* Unknown function. It's not handled.  */
        return(UX_ERROR);
    }

    /* It's handled.  */
    return(UX_SUCCESS);
}
