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


/**************************************************************************/
/**************************************************************************/
/**                                                                       */
/** POSIX wrapper for THREADX                                             */
/**                                                                       */
/**                                                                       */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

/* Include necessary system files.  */

#include "tx_api.h"    /* Threadx API */
#include "pthread.h"  /* Posix API */
#include "px_int.h"    /* Posix helper functions */

/**************************************************************************/
/*                                                                        */
/*  FUNCTION                                               RELEASE        */
/*                                                                        */
/*    mq_send                                             PORTABLE C      */
/*                                                           6.1.7        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    William E. Lamie, Microsoft Corporation                             */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    The mq_send() function puts a message of size msg_len and pointed to*/
/*    by msg_ptr into the queue indicated by mqdes. The new message has a */
/*    priority of msg_prio.                                               */
/*    The queue maintained is in priority order (priorities may range from*/
/*    0 to MQ_PRIO_MAX), and in FIFO order within the same priority.      */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    mqdes                             Queue descriptor                  */
/*    msg_ptr                           Message pointer                   */
/*    msg_len                           length of message                 */
/*    msg_prio                          Priority of the message           */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    OK                                no of bytes received              */
/*    ERROR                             If error occurs                   */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    tx_thread_identify                returns currently running thread  */
/*    tx_byte_allocate                  allocate memory                   */
/*    tx_queue_send                     ThreadX queue send                */
/*    posix_priority_search             search message for same priority  */
/*                                                                        */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application Code                                                    */
/*                                                                        */
/**************************************************************************/
INT  mq_send( mqd_t mqdes, const CHAR * msg_ptr, size_t msg_len,
                ULONG msg_prio )
{

TX_QUEUE           *Queue;
UINT                temp1;
POSIX_MSG_QUEUE    *q_ptr;
VOID               *bp;
UCHAR              *source;
UCHAR              *destination;
UCHAR              *save_ptr;
ULONG               mycount;
ULONG               msg[TX_POSIX_MESSAGE_SIZE];

    /* Assign a temporary variable for clarity.  */
    Queue = &(mqdes->f_data->queue);
    q_ptr = (POSIX_MSG_QUEUE * )mqdes->f_data;

    /* First, check for an invalid queue pointer.  */
    if ( (!q_ptr) || ( (q_ptr -> px_queue_id) != PX_QUEUE_ID))
    {
        /* Queue pointer is invalid, return appropriate error code.  */
        posix_errno = EBADF;
        posix_set_pthread_errno(EBADF);

        /* Return ERROR.  */
        return(ERROR);
    }
    /* Make sure if we're calling this routine from a ISR timeout
        is set to zero.  */
    if (!(tx_thread_identify()))
    {
        /* POSIX doesn't have error for this, hence give default.  */
        posix_errno = EINTR ;
        posix_set_pthread_errno(EINTR);

        /* Return ERROR.  */
        return(ERROR);
    }

    /* First, check for an invalid queue pointer.  */
    if ( (!q_ptr) || ( (q_ptr->queue.tx_queue_id) != TX_QUEUE_ID))
    {
        /* Queue descriptor is invalid, set appropriate error code.  */
        posix_errno = EBADF ;
        posix_set_pthread_errno(EBADF);

        /* Return ERROR.  */
        return(ERROR);
    }
    if(((mqdes->f_flag & O_WRONLY) != O_WRONLY) && ((mqdes->f_flag & O_RDWR) != O_RDWR))
    {
        /* Queue pointer is invalid, return appropriate error code.  */
        posix_errno = EBADF;
        posix_set_pthread_errno(EBADF);

        /* Return ERROR.  */
        return(ERROR);
    }
    if( msg_prio > MQ_PRIO_MAX)
    {
        /* Return appropriate error.  */
        posix_errno = EINVAL;
        posix_set_pthread_errno(EINVAL);

        /* Return error.  */
        return(ERROR);
    }
    /* Check for an invalid source for message.  */
    if (! msg_ptr)
    {
        /* POSIX doesn't have error for this, hence give default.  */
        posix_errno = EINTR ;
        posix_set_pthread_errno(EINTR);

        /* Return ERROR.  */
        return(ERROR);
    }

    /* Now check the length of message.  */
    if ( msg_len > (q_ptr->q_attr.mq_msgsize ) )
    {
        /*  Return message length exceeds max length.  */
        posix_errno = EMSGSIZE ;
        posix_set_pthread_errno(EMSGSIZE);

        /* Return ERROR.  */
        return(ERROR);
    }

    /* Now try to allocate memory to save the message from the
      queue's byte pool.  */
    temp1 = tx_byte_allocate((TX_BYTE_POOL * )&(q_ptr->vq_message_area), &bp,
                             msg_len, TX_NO_WAIT);

    if (temp1 != TX_SUCCESS)
    {
    posix_internal_error(9999);
    }
    /* Got the memory , Setup source and destination pointers
       Cast them in UCHAR as message length is in bytes.  */
    source      =  (UCHAR * ) msg_ptr;
    destination =  (UCHAR * ) bp;

    /* Save start of message storage.  */
    save_ptr   = destination;

    /* Copy the message into private buffer.  */
    for ( mycount = 0; mycount < msg_len; mycount++)
    {

        * destination++ =  * source++;
    }
    /* Restore the pointer of save message.  */
    source =  save_ptr ;
    /* Create message that holds saved message pointer and message length.  */
#ifdef TX_64_BIT
    msg[0] = (ULONG)((ALIGN_TYPE)source >> 32);
    msg[1] = (ULONG)((ALIGN_TYPE)source);
    msg[2] =  msg_len;
    msg[3] =  msg_prio;
    msg[4] =  posix_priority_search(mqdes, msg_prio);
#else
    msg[0] = (ULONG)source;
    msg[1] =  msg_len;
    msg[2] =  msg_prio;
    msg[3] =  posix_priority_search(mqdes, msg_prio);
#endif
    /* Attempt to post the message to the queue.  */
    temp1 = tx_queue_send(Queue, msg, TX_WAIT_FOREVER);
    if ( temp1 != TX_SUCCESS)
    {
        /* POSIX doesn't have error for this, hence give default.  */
        posix_errno = EINTR ;
	    posix_set_pthread_errno(EINTR);

        /* Return ERROR.  */
        return(ERROR);
    }

    /* All done.  */
    return(OK);
}
