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

    #ifndef CrossWorks_Tasking_Library
    #define CrossWorks_Tasking_Library

    #ifdef __cplusplus
    extern "C" {
    #endif

    //
    // Task Support
    //

    // Enumeration of set of task states
    enum
    {
      CTL_STATE_RUNNABLE                   = 0x00, // Task can run
      CTL_STATE_TIMER_WAIT                 = 0x01, // Task is waiting for a time value
      CTL_STATE_EVENT_WAIT                 = 0x02, // Task is waiting for events to be set
      CTL_STATE_EVENT_ANY_NOT_ALL          = 0x04, // Task is waiting for any rather than all events to be set
      CTL_STATE_EVENT_AUTO_CLEAR           = 0x08, // Auto clear events on wait completion
      CTL_STATE_SEMAPHORE_WAIT             = 0x10, // Task is waiting for a semaphore
      CTL_STATE_MESSAGE_QUEUE_POST_WAIT    = 0x20, // Task is waiting to post to a message queue
      CTL_STATE_MESSAGE_QUEUE_RECEIVE_WAIT = 0x30  // Task is waiting to receive from a message queue
    };

    // 32bit timer
    typedef unsigned long CTL_TIME_t;
    // Event Set's are word sized - 16 or 32 depending on the machine
    typedef unsigned CTL_EVENT_SET_t;

    // The Task structure should only be modified via the API functions
    typedef struct CTL_TASK_s CTL_TASK_t;
    struct CTL_TASK_s
    {
      unsigned *stack_pointer;              // to saved registers - don't move this - assembly code knows about it
      unsigned char priority;               // higher numbers denote higher priority
      unsigned char state;                  // state of task
      CTL_TASK_t *next;                     // next pointer for wait queue

      CTL_TIME_t timeout;                   // timeout value - defined when state & CTL_STATE_TIMER_WAIT

      void *wait_object;                    // the event set to wait on - when state & CTL_STATE_EVENT_WAIT
                                            // the semaphore to wait on - when state & CTL_STATE_SEMAPHORE_WAIT
                                            // the message queue to wait on - when (state & CTL_STATE_MESSAGE_QUEUE_POST_WAIT) || (state & CTL_STATE_MESSAGE_QUEUE_RECEIVE_WAIT)
      union
        {
          CTL_EVENT_SET_t wait_events;      // the events to wait for - defined when state & CTL_STATE_EVENT_WAIT
          void *message;                    // the message to post/receive - a temporary
        }
      u;

      int errno;                            // thread specific errno

      char *name;                           // a name for the debugger to display
    };

    // Turn main into a task - this must be called first
    void ctl_task_init(CTL_TASK_t *,
                       unsigned char priority,
                       char *name);

    // Add a new task to task list and do scheduling
    void ctl_task_run(CTL_TASK_t *,
                      unsigned char priority,
                      void (*entrypoint)(void *),     // entrypoint the task starts executing - parameter is passed to it
                      void *parameter,                // parameter to pass to entrypoint
                      char *name,
                      unsigned stack_size_in_words,   // note that the stack should be allocated in words not bytes
                      unsigned *stack,
                      unsigned call_size_in_words);   // optional parameter if call stack is seperate from data stack

    // Remove an existing task from the task list and do scheduling
    void ctl_task_remove(CTL_TASK_t *);

    // Remove the executing task from the list.
    void ctl_task_die(void);

    // Change the priority of the task and do scheduling
    void ctl_task_set_priority(CTL_TASK_t *,
                               unsigned char priority);

    // Cause a reschedule
    void ctl_task_reschedule(void);

    //
    // Timer support
    //

    // Atomically get the current value of the timer
    CTL_TIME_t ctl_get_current_time(void);

    // Wait for the timer to be timeout
    void ctl_timeout_wait(CTL_TIME_t timeout);

    // increment the timer tick and do scheduling
    // this must be called from an interrupt handler
    // interrupts must be disabled when this is called
    void ctl_increment_tick_from_isr(void);

    //
    // Event support
    //

    // Initialise an event set
    void ctl_events_init(CTL_EVENT_SET_t *e,
                         CTL_EVENT_SET_t set);

    // Set and clear the specified events in the eventSet and do scheduling.
    // The operation is *e = (*e | set) & ~clear
    void ctl_events_set_clear(CTL_EVENT_SET_t *e,
                              CTL_EVENT_SET_t set,
                              CTL_EVENT_SET_t clear);

    typedef enum
    {
      CTL_EVENT_WAIT_ANY_EVENTS = CTL_STATE_EVENT_WAIT | CTL_STATE_EVENT_ANY_NOT_ALL,
      CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR = CTL_STATE_EVENT_WAIT | CTL_STATE_EVENT_ANY_NOT_ALL | CTL_STATE_EVENT_AUTO_CLEAR,
      CTL_EVENT_WAIT_ALL_EVENTS = CTL_STATE_EVENT_WAIT,
      CTL_EVENT_WAIT_ALL_EVENTS_WITH_AUTO_CLEAR = CTL_STATE_EVENT_WAIT | CTL_STATE_EVENT_AUTO_CLEAR
    }
    CTL_EVENT_WAIT_TYPE_t;

    // Wait on the specified events in the event set and do scheduling.
    // Returns the eventSet prior to auto clearing, 0 on timeout
    unsigned ctl_events_wait(CTL_EVENT_WAIT_TYPE_t type,
                             CTL_EVENT_SET_t *eventSet,
                             CTL_EVENT_SET_t events,
                             unsigned use_timeout,  // If non-zero use timeout
                             CTL_TIME_t timeout);

    //
    // Semaphore support
    //

    // Semaphore's are word sized - 16 or 32 depending on the machine
    typedef unsigned CTL_SEMAPHORE_t;

    // Initialise a semaphore
    void ctl_semaphore_init(CTL_SEMAPHORE_t *s,
                            unsigned value);

    // Signal a semaphore and do scheduling.
    void ctl_semaphore_signal(CTL_SEMAPHORE_t *s);

    // Wait on a semaphore and do scheduling.
    // Return 1 on success, 0 on timeout
    unsigned ctl_semaphore_wait(CTL_SEMAPHORE_t *s,
                                unsigned use_timeout, // if non-zero use timeout
                                CTL_TIME_t timeout);

    //
    // Message queue support
    //

    typedef struct {
      void **q;  // pointer to array of objects
      unsigned s; // size of array q;
      unsigned front; // the next element to leave the q
      unsigned n; // the number of elements in the q
    } CTL_MESSAGE_QUEUE_t;

    // Initialise a message queue structure
    void ctl_message_queue_init(CTL_MESSAGE_QUEUE_t *m, // message queue pointer
                                void **queue,           // pointer to the queue
                                unsigned queue_size);   // the number of elements in the queue

    // Post a message to a message queue and do scheduling
    // Return 1 on success, 0 on timeout
    unsigned ctl_message_queue_post(CTL_MESSAGE_QUEUE_t *m, // message queue pointer
                                    void *message,          // the message to post
                                    unsigned use_timeout,   // if non-zero use the timeout
                                    CTL_TIME_t timeout);    // timeout

    // Post a message to a message queue and do scheduling
    // Return 1 on success, 0 if message queue is full
    unsigned ctl_message_queue_post_nb(CTL_MESSAGE_QUEUE_t *m, // message queue pointer
                                       void *message);         // the message to post

    // Receive a message from a message queue and do scheduling.
    // If with_timeout is true (non-zero) use timeout to stop blocking.
    // Returns the message or 0 if the timeout occured.
    void *ctl_message_queue_receive(CTL_MESSAGE_QUEUE_t *m, // message queue pointer
                                    unsigned with_timeout,  // if non-zero use the timeout
                                    CTL_TIME_t timeout);    // timeout

    //
    // Global interrupts support
    //

    // Turn off global interrupt enables.
    // Returns the previous global interrupt enables state (1 enabled, 0 disabled).
    int ctl_global_interrupts_disable(void);

    // Turn on global interrupt enables.
    // Returns the previous global interrupt enables state (1 enabled, 0 disabled).
    int ctl_global_interrupts_enable(void);

    // Set global interrupt enables (1 enabled, 0 disabled).
    void ctl_global_interrupts_set(int enabled);

    // Re-enable interrupts from within an ISR in order to allow higher priority
    // interrupts to execute. A call to this function must be accompanied with a
    // call to ctl_global_interrupts_un_re_enable_from_isr before the ISR exits.
    void ctl_global_interrupts_re_enable_from_isr(void);

    // Disable interrupts from within an ISR.
    void ctl_global_interrupts_un_re_enable_from_isr(void);

    // Low-level ISR support - only need to use this if
    // no programmable interrupt support.
    // Jump to this on exit from an IRQ - it will not return.
    // savedRegisters must point to the saved user registers.
    // interrupts must be disabled when this is called.
    void ctl_exit_isr(void *savedRegisters);

    //
    // Programmable interrupt controller support
    //

    // Interrupt ISR function type.
    typedef void (*CTL_ISR_FN_t)(void);

    // Enumeration of interrupt trigger types
    typedef enum
    {
      CTL_ISR_TRIGGER_FIXED,
      CTL_ISR_TRIGGER_LOW_LEVEL,
      CTL_ISR_TRIGGER_HIGH_LEVEL,
      CTL_ISR_TRIGGER_NEGATIVE_EDGE,
      CTL_ISR_TRIGGER_POSITIVE_EDGE,
      CTL_ISR_TRIGGER_DUAL_EDGE
    }
    CTL_ISR_TRIGGER_t;

    // Install a new interrupt handler.
    int ctl_set_isr(unsigned int vector,
                    unsigned int priority,
                    CTL_ISR_TRIGGER_t trigger,
                    CTL_ISR_FN_t isr,
                    CTL_ISR_FN_t *oldisr);

    // Unmask an interrupt source.
    int ctl_unmask_isr(unsigned int vector);

    // Mask an interrupt source.
    int ctl_mask_isr(unsigned int vector);

    //
    // Error handling
    //

    typedef enum
    {
      CTL_ERROR_NO_TASKS_TO_RUN,
      CTL_WAIT_CALLED_FROM_ISR,
      CTL_SUICIDE_IN_ISR
    }
    CTL_ERROR_CODE_t;

    // This function is called if something goes wrong with your application.
    // You must supply this function
    void ctl_handle_error(CTL_ERROR_CODE_t);

    //
    // Memory area support
    //

    typedef unsigned *CTL_MEMORY_AREA_t;

    // Initialise a memory area
    void ctl_memory_area_init(CTL_MEMORY_AREA_t *memory_area, // pointer to the memory area
                              unsigned *memory, // should be block_size_in_words * num_blocks in length
                              unsigned block_size_in_words, // size of the memory block in words
                              unsigned num_blocks); // the number of blocks

    // Allocate a free memory block
    // Returns the block or 0
    unsigned *ctl_memory_area_allocate(CTL_MEMORY_AREA_t *memory_area);

    // Return a memory block
    void ctl_memory_area_free(CTL_MEMORY_AREA_t *memory_area,
                              unsigned *block);

    //
    // System state
    //
    extern CTL_TASK_t *ctl_task_list;             // list of waiting tasks sorted by priority
    extern CTL_TASK_t *ctl_task_executing;        // the task that is currently executing
    extern unsigned char ctl_interrupt_count;     // the nested interrupt count
    extern CTL_TIME_t ctl_current_time;           // the current time
    extern CTL_EVENT_SET_t ctl_libc_mutex;        // event set used to serialise access to the C library

    // Enumeration of the event bits contained in the ctl_libc_mutex
    typedef enum
    {
      CTL_LIBC_MUTEX_HEAP = (1<<0),
      CTL_LIBC_MUTEX_PRINTF = (1<<1),
      CTL_LIBC_MUTEX_SCANF = (1<<2),
      CTL_LIBC_MUTEX_DEBUG_IO = (1<<3)
    }
    CTL_LIBC_MUTEX_BITS;

    #ifdef __cplusplus
    }
    #endif

    #endif