// CrossWorks Tasking Library.
//
// Copyright (c) 2004 Rowley Associates Limited.
//
// This file may be distributed under the terms of the License Agreement
// provided with this software.
//
// THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "ctl_api.h"
#include "ctl_impl.h"

void
ctl_semaphore_init(CTL_SEMAPHORE_t *s,
                   unsigned value)
{
  int enabled = ctl_global_interrupts_disable();
  *s = value;
  ctl_global_interrupts_set(enabled);
}

unsigned 
ctl_semaphore_wait(CTL_SEMAPHORE_t *s,
                   CTL_TIMEOUT_t t,
                   CTL_TIME_t timeout)
{  
  ctl_global_interrupts_disable();
  if (ctl_interrupt_count)
    ctl_handle_error(CTL_UNSUPPORTED_CALL_FROM_ISR);    
  if (*s)
    {
      (*s)--;          
       ctl_task_executing->v.timeout_occured = 0;
    }
  else
    {
      unsigned type = CTL_STATE_SEMAPHORE_WAIT;
      switch (t)
        {
          case CTL_TIMEOUT_NONE:
            ctl_task_executing->timeout = 0;
            break;
          case CTL_TIMEOUT_ABSOLUTE:
            ctl_task_executing->timeout = timeout;
            type |= CTL_STATE_TIMER_WAIT;  
            break;
          case CTL_TIMEOUT_DELAY:
            ctl_task_executing->timeout = ctl_current_time + timeout;
            type |= CTL_STATE_TIMER_WAIT;  
            break;
        }         
      ctl_task_executing->v.wait_object = s;
      ctl_task_executing->u.wait_events = 0;
      ctl_task_executing->state = type;          
    }
  ctl_private_reschedule();
  return ctl_task_executing->v.timeout_occured==0;
}

void 
ctl_semaphore_signal(CTL_SEMAPHORE_t *s)
{
  int enabled = ctl_global_interrupts_disable();
  if (*s)    
    (*s)++;    
  else
    {
      CTL_TASK_t *t;
      for (t=ctl_task_list; t; t=t->next)
        if ((t->state & CTL_STATE_SEMAPHORE_WAIT) && (t->v.wait_object == s))
          {
            t->state = CTL_STATE_RUNNABLE;    
            t->v.timeout_occured = 0;
            break;
          }
      if (!t)
        (*s)++;
    }
  if (ctl_interrupt_count==0)
    ctl_private_reschedule();
  else
    ctl_global_interrupts_set(enabled);
}
