text: /****************************************************************************
   * drivers/modem/altmdm/altmdm_sys.c
   *
   *   Copyright 2018 Sony Semiconductor Solutions Corporation
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   * 3. Neither the name of Sony Semiconductor Solutions Corporation nor
   *    the names of its contributors may be used to endorse or promote
   *    products derived from this software without specific prior written
   *    permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <errno.h>
  #include <debug.h>
  #include <nuttx/irq.h>
  #include <signal.h>
  
  #include "altmdm_dev.h"
  #include "altmdm_sys.h"
  
  #if defined(CONFIG_MODEM_ALTMDM)
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  #define MY_TIMER_SIGNAL SIGUSR1
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: altmdm_sys_initlock
   *
   * Description:
   *   Initialize lock resource.
   *
   ****************************************************************************/
  
  int altmdm_sys_initlock(FAR struct altmdm_sys_lock_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_init(&handle->sem, 0, 1);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_init() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_deletelock
   *
   * Description:
   *   Delete lock resource
   *
   ****************************************************************************/
  
  int altmdm_sys_deletelock(FAR struct altmdm_sys_lock_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_destroy(&handle->sem);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_destroy() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_lock
   *
   * Description:
   *   Acquire lock.
   *
   ****************************************************************************/
  
  int altmdm_sys_lock(FAR struct altmdm_sys_lock_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_wait_uninterruptible(&handle->sem);
    if (ret < 0)
      {
        m_err("nxsem_wait_uninterruptible() failed:%d\n", ret);
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_unlock
   *
   * Description:
   *   Release lock.
   *
   ****************************************************************************/
  
  int altmdm_sys_unlock(FAR struct altmdm_sys_lock_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_post(&handle->sem);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_post() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_initcsem
   *
   * Description:
   *   Initialize counting semaphore.
   *
   ****************************************************************************/
  
  int altmdm_sys_initcsem(FAR struct altmdm_sys_csem_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_init(&handle->sem, 0, 0);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_init() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_deletecsem
   *
   * Description:
   *   Delete counting semaphore.
   *
   ****************************************************************************/
  
  int altmdm_sys_deletecsem(FAR struct altmdm_sys_csem_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_destroy(&handle->sem);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_destroy() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_waitcsem
   *
   * Description:
   *   Wait counting semaphore.
   *
   ****************************************************************************/
  
  int altmdm_sys_waitcsem(FAR struct altmdm_sys_csem_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_wait_uninterruptible(&handle->sem);
    if (ret < 0)
      {
        m_err("nxsem_wait_uninterruptible() failed:%d\n", ret);
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_postcsem
   *
   * Description:
   *   Post counting semaphore.
   *
   ****************************************************************************/
  
  int altmdm_sys_postcsem(FAR struct altmdm_sys_csem_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_post(&handle->sem);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_post() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_getcsemvalue
   *
   * Description:
   *   Get value of counting semaphore.
   *
   ****************************************************************************/
  
  int altmdm_sys_getcsemvalue(FAR struct altmdm_sys_csem_s *handle,
                              FAR int *value)
  {
    int ret;
  
    /* Check argument. */
  
    if ((handle == NULL) || (value == NULL))
      {
        return ERROR;
      }
  
    ret = nxsem_getvalue(&handle->sem, value);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_getvalue() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_initflag
   *
   * Description:
   *   Initialize event flag resource.
   *
   ****************************************************************************/
  
  int altmdm_sys_initflag(FAR struct altmdm_sys_flag_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    handle->flag = 0;
    ret = nxsem_init(&handle->sem, 0, 0);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_init() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_deleteflag
   *
   * Description:
   *   Delete event flag resource.
   *
   ****************************************************************************/
  
  int altmdm_sys_deleteflag(FAR struct altmdm_sys_flag_s *handle)
  {
    int ret;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    ret = nxsem_destroy(&handle->sem);
  
  #ifdef CONFIG_MODEM_ALTMDM_DEBUG
    if (ret < 0)
      {
        m_err("nxsem_destroy() failed:%d\n", ret);
      }
  #endif
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_waitflag
   *
   * Description:
   *   Wait event flag.
   *
   ****************************************************************************/
  
  int altmdm_sys_waitflag(FAR struct altmdm_sys_flag_s *handle,
                          uint32_t wait_pattern, uint32_t wait_mode,
                          FAR uint32_t * pattern, uint32_t timeout_ms)
  {
    int ret = OK;
    struct timespec abs_time;
    struct timespec curr_time;
    irqstate_t flags;
    uint32_t ptn;
  
    /* Check argument. */
  
    if ((handle == NULL) || (pattern == NULL))
      {
        m_err("invalid parameter\n");
  
        return ERROR;
      }
  
    switch (wait_mode)
      {
      case ALTMDM_SYS_FLAG_WMODEOR:
      case ALTMDM_SYS_FLAG_WMODEAND:
        break;
  
      default:
        m_err("invalid wait mode:%d\n", wait_mode);
        return ERROR;
      }
  
    if (timeout_ms != ALTMDM_SYS_FLAG_TMOFEVR)
      {
        /* Get current time. */
  
        ret = clock_gettime(CLOCK_REALTIME, &curr_time);
        if (ret != OK)
          {
            return ret;
          }
  
        abs_time.tv_sec = timeout_ms / 1000;
        abs_time.tv_nsec = (timeout_ms - (abs_time.tv_sec * 1000)) * 1000 * 1000;
  
        abs_time.tv_sec += curr_time.tv_sec;
        abs_time.tv_nsec += curr_time.tv_nsec;
  
        /* Check more than 1 sec. */
  
        if (abs_time.tv_nsec >= (1000 * 1000 * 1000))
          {
            abs_time.tv_sec += 1;
            abs_time.tv_nsec -= (1000 * 1000 * 1000);
          }
      }
  
    *pattern = 0;
  
    while (1)
      {
        if (wait_mode == ALTMDM_SYS_FLAG_WMODEOR)
          {
            flags = enter_critical_section();
  
            ptn = (handle->flag & wait_pattern);
            if (ptn != 0)
              {
                /* Wait pattern matched. */
  
                *pattern = ptn;
                handle->flag = (handle->flag & ~ptn);
  
                /* Clear the semaphore posted by altmdm_sys_setflag. */
  
                while (1)
                  {
                    if (nxsem_trywait(&handle->sem) < 0)
                      {
                        break;
                      }
                  }
  
                leave_critical_section(flags);
  
                ret = OK;
                break;
              }
  
            leave_critical_section(flags);
          }
        else
          {
            flags = enter_critical_section();
  
            ptn = (handle->flag & wait_pattern);
            if (ptn == wait_pattern)
              {
                /* Wait pattern matched. */
  
                *pattern = ptn;
                handle->flag = (handle->flag & ~ptn);
  
                /* Clear the semaphore posted by altmdm_sys_setflag. */
  
                while (1)
                  {
                    if (nxsem_trywait(&handle->sem) < 0)
                      {
                        break;
                      }
                  }
  
                leave_critical_section(flags);
  
                ret = OK;
                break;
              }
  
            leave_critical_section(flags);
          }
  
        if (timeout_ms != ALTMDM_SYS_FLAG_TMOFEVR)
          {
            /* Wait for the semaphore to be posted until timeout occurs. */
  
            ret = nxsem_timedwait_uninterruptible(&handle->sem, &abs_time);
            if (ret < 0)
              {
                m_err("nxsem_timedwait_uninterruptible() failed:%d\n", ret);
                break;
              }
          }
        else
          {
            /* Wait for the semaphore to be posted forever. */
  
            ret = nxsem_wait_uninterruptible(&handle->sem);
            if (ret < 0)
              {
                m_err("nxsem_wait_uninterruptible() failed:%d\n", ret);
                break;
              }
          }
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_setflag
   *
   * Description:
   *   Set event flag.
   *
   ****************************************************************************/
  
  int altmdm_sys_setflag(FAR struct altmdm_sys_flag_s *handle, uint32_t pattern)
  {
    int ret;
    irqstate_t flags;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    flags = enter_critical_section();
  
    handle->flag = (handle->flag | pattern);
  
    leave_critical_section(flags);
  
    ret = nxsem_post(&handle->sem);
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_clearflag
   *
   * Description:
   *   Clear event flag.
   *
   ****************************************************************************/
  
  int altmdm_sys_clearflag(FAR struct altmdm_sys_flag_s *handle,
                           uint32_t pattern)
  {
    irqstate_t flags;
  
    /* Check argument. */
  
    if (handle == NULL)
      {
        return ERROR;
      }
  
    flags = enter_critical_section();
  
    handle->flag = (handle->flag & ~pattern);
  
    leave_critical_section(flags);
  
    return OK;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_referflag
   *
   * Description:
   *   Refer event flag.
   *
   ****************************************************************************/
  
  int altmdm_sys_referflag(FAR struct altmdm_sys_flag_s *handle,
                           FAR struct altmdm_sys_flagstate_s *status)
  {
    irqstate_t flags;
  
    /* Check argument. */
  
    if ((handle == NULL) || (status == NULL))
      {
        return ERROR;
      }
  
    flags = enter_critical_section();
  
    status->flag_pattern = handle->flag;
  
    leave_critical_section(flags);
  
    return OK;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_starttimer
   *
   * Description:
   *   Start timer.
   *
   ****************************************************************************/
  
  timer_t altmdm_sys_starttimer(int first_ms, int interval_ms,
                                FAR void *handler, int int_param,
                                FAR void *ptr_param)
  {
    int ret;
    sigset_t mask;
    struct sigaction sa;
    struct sigevent sev;
    struct itimerspec timer;
    timer_t timerid;
  
    /* Check argument. */
  
    if (handler == NULL)
      {
        return NULL;
      }
  
    sigemptyset(&mask);
    sigaddset(&mask, MY_TIMER_SIGNAL);
  
    ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
    if (ret != OK)
      {
        m_err("sigprocmask() failed:%d\n", ret);
        return NULL;
      }
  
    sa.sa_sigaction = handler;
    sa.sa_flags = SA_SIGINFO;
    sigfillset(&sa.sa_mask);
    sigdelset(&sa.sa_mask, MY_TIMER_SIGNAL);
  
    ret = sigaction(MY_TIMER_SIGNAL, &sa, NULL);
    if (ret != OK)
      {
        m_err("sigaction() failed:%d\n", ret);
        return NULL;
      }
  
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = MY_TIMER_SIGNAL;
    sev.sigev_value.sival_int = int_param;
    sev.sigev_value.sival_ptr = ptr_param;
  
    ret = timer_create(CLOCK_REALTIME, &sev, &timerid);
    if (ret != OK)
      {
        m_err("timer_create() failed:%d\n", ret);
        return NULL;
      }
  
    timer.it_value.tv_sec = first_ms / 1000;
    timer.it_value.tv_nsec = (first_ms % 1000) * 1000 * 1000;
    timer.it_interval.tv_sec = interval_ms / 1000;
    timer.it_interval.tv_nsec = (interval_ms % 1000) * 1000 * 1000;
  
    ret = timer_settime(timerid, 0, &timer, NULL);
    if (ret != OK)
      {
        m_err("timer_settime() failed:%d\n", ret);
        return NULL;
      }
  
    return timerid;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_restarttimer
   *
   * Description:
   *   Restart timer.
   *
   ****************************************************************************/
  
  int altmdm_sys_restarttimer(timer_t timerid, int first_ms, int interval_ms)
  {
    int ret;
    struct itimerspec timer;
  
    timer.it_value.tv_sec = first_ms / 1000;
    timer.it_value.tv_nsec = (first_ms % 1000) * 1000 * 1000;
    timer.it_interval.tv_sec = interval_ms / 1000;
    timer.it_interval.tv_nsec = (interval_ms % 1000) * 1000 * 1000;
  
    ret = timer_settime(timerid, 0, &timer, NULL);
    if (ret != OK)
      {
        m_err("timer_settime() failed:%d\n", ret);
        return ret;
      }
  
    return ret;
  }
  
  /****************************************************************************
   * Name: altmdm_sys_stoptimer
   *
   * Description:
   *   Stop timer.
   *
   ****************************************************************************/
  
  void altmdm_sys_stoptimer(timer_t timerid)
  {
    sigset_t mask;
  
    timer_delete(timerid);
  
    sigfillset(&mask);
    sigprocmask(SIG_SETMASK, &mask, NULL);
  }
  
  #endif
  