/***************************************************************************
 * 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        */
/*                                                                        */
/*    sem_open                                            PORTABLE C      */
/*                                                           6.2.0        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    William E. Lamie, Microsoft Corporation                             */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */
/*    This function creates/initialize a semaphore.                       */
/*                                                                        */
/*  INPUT                                                                 */
/*                                                                        */
/*    name                                  Semaphore name                */
/*    oflags                                Flags                         */
/*    mode                                  Optional parameter            */
/*    value                                 Optional parameter            */
/*                                                                        */
/*  OUTPUT                                                                */
/*                                                                        */
/*    sem                                   Semaphore descriptor          */
/*                                                                        */
/*  CALLS                                                                 */
/*                                                                        */
/*    posix_in_thread_context               Make sure caller is thread    */
/*    posix_find_sem                        Finds the required semaphore  */
/*    posix_get_new_sem                     Get new semaphore block       */
/*    posix_internal_error                  Internal posix error          */
/*    tx_semaphore_create                   ThreadX semaphore create      */
/*    posix_set_sem_name                    Sets name for semaphore       */
/*                                                                        */
/*  CALLED BY                                                             */
/*                                                                        */
/*    Application Code                                                    */
/*                                                                        */
/**************************************************************************/
sem_t * sem_open(const CHAR * name, ULONG oflag, ...)
{

TX_INTERRUPT_SAVE_AREA

TX_SEMAPHORE     *TheSem;
sem_t            *semid;
ULONG             retval;
ULONG             len;
sem_t            *posix_sem;
va_list           vl;
UINT              value;
mode_t            mode;

    /* Make sure we're calling this routine from a thread context.  */
    if (!posix_in_thread_context())
    {
        /* return POSIX error.  */
        posix_internal_error(444);

        /* return error.  */
        return (( sem_t * )SEM_FAILED);
    }

    /* Find length of the name. The actual length is not known yet.  */
    len = strlen(name);

    if(len > SEM_NAME_MAX)
    {
        /* Set appropriate errno.  */
        posix_errno = ENAMETOOLONG;
        posix_set_pthread_errno(ENAMETOOLONG);

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

    /* Check if semaphore exists.  */
    if((semid = posix_find_sem(name)) != NULL)
    {
        if(semid->unlink_flag ==TX_TRUE )
        {
            /* Return error.  */
            posix_errno = EINVAL;
            posix_set_pthread_errno(EINVAL);
            return(( sem_t * )SEM_FAILED);
       }
    }

    /* Semaphore not found.  */
    if(!(semid))
    {
        /* Check if flag set is O_EXCL.  */
        if( oflag == O_EXCL )
        {

            /* Set error for sem does not exist and O_CREAT not set.  */
            posix_errno = ENOENT;
            posix_set_pthread_errno(ENOENT);

            /* Return the SEM_FAILED error.  */
            return(( sem_t * )SEM_FAILED);
        }
        if( (oflag == O_CREAT) || ( (oflag & (O_CREAT|O_EXCL )) == (O_CREAT|O_EXCL) ) )
        {

            /* Get the variable arguments pointers.  */

            va_start( vl, oflag );

            mode = va_arg( vl, mode_t);
            mode = mode;  /* just to keep compiler happy */

            value = va_arg( vl, ULONG);

            if(value > SEM_VALUE_MAX)
            {
                /* Semaphore value large.  */
               posix_errno = EINVAL;
               posix_set_pthread_errno(EINVAL);

               /* Return ERROR  */
               return (( sem_t * )SEM_FAILED);

            }

            /* Disable interrupts.  */
            TX_DISABLE

            /* Get a new semaphore from the POSIX semaphore pool.  */
            TheSem = posix_get_new_sem();

            /* Make sure we actually got a semaphore.  */
            if (!TheSem)
            {
                /* Semaphore cannot be initialized due to resource constraint.  */
                posix_errno = ENOSPC;
                posix_set_pthread_errno(ENOSPC);

                /* Restore interrupts.  */
                TX_RESTORE

                /* return appropriate error code.  */
                return(( sem_t * )SEM_FAILED);
            }

            /* Set the semaphore name.  */
            posix_set_sem_name((sem_t *)TheSem, ( CHAR * ) name);

            /* Now actually create the ThreadX semaphore.  */
            posix_sem = (struct POSIX_SEMAPHORE_STRUCT *)TheSem;
            retval = tx_semaphore_create(TheSem, ( CHAR * ) posix_sem->sem_name , value);

            /* Make sure it worked.  */
            if (retval)
            {
                /* Return generic error.  */
                posix_internal_error(100);

                /* Restore interrupts.  */
                TX_RESTORE

                /* Return appropriate error code.  */
                return(( sem_t * )SEM_FAILED);
            }

            /* Add the calling thread to the thread list.  */
            posix_sem -> count += 1;

            /* Set initial count  */
            posix_sem->refCnt = value;

            /* Give the caller the semaphore ID.  */
            semid = (sem_t * )TheSem;

            /* Restore interrupts.  */
            TX_RESTORE

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

    }
    /* Semaphore found.  */
    if(semid)
    {

        /* Check if flags are O_CREAT|O_EXCL.  */
        if( (oflag == O_EXCL) || (oflag & (O_CREAT|O_EXCL )) == (O_CREAT|O_EXCL) )
        {
            /* Set appropriate errno.  */
            posix_errno = EEXIST;
            posix_set_pthread_errno(EEXIST);

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

        /* Check if flag is only O_CREAT.  */
        if( (oflag == O_CREAT) || (oflag==0))
        {
            /* Disable interrupts.  */
            TX_DISABLE

            /* Add the calling thread to the thread list.  */
            semid -> count++;

            /* Restore interrupts.  */
            TX_RESTORE

            /* Return semaphore.  */
            return (( sem_t * ) semid);
        }
    }

    /* Semaphore value large.  */
    posix_errno = EINVAL;
    posix_set_pthread_errno(EINVAL);

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