// ARTK.h - defines the public interface for user programs
// A pre-emptive multitasking kernel for Arduino
//
// History:
// Release 0.1  June 2012  Paul H. Schimpf
// Release 0.2  June 2012  Paul H. Schimpf
//              Some changes to squeeze out bits of memory
// Release 0.4  May 2013   Paul H. Schimpf
//              removed mention of Main() in comments in this file
//              init newtask in resched() to avoid warning
//              added some volatiles while debugging releases 1.0.4, 1.0.5
//              removed dependency on TimerOne library
//              no longer allowed to modify sleep timer interval
// Release 0.7  Aug 2015   Paul H. Schimpf
//              added Printf support for the F macro
//              allowed to modify sleep timer interval down to 1 msec
//              fixed DQNode (sleeper) allocation with fixed pool so
//              can be used with latest arduino release (1.6.5 at present)
//
// Acknowledgement:
// Thank you to Raymond J. A. Buhr and Donald L. Bailey for inspiration
// and ideas from "An Introduction to Real-Time Systems."  While there are 
// significant differences, there are also similarities in the structure 
// and implementation of ARTK and Tempo.

// *******************************
// IMPORTANT USAGE NOTES
// *******************************
// You must NOT implement a setup() function
// Implement a Setup() function instead - ARTK will call it for you
// Setup is the best place to create Tasks and semaphores
// You must NOT implement a loop() function
// Implement a Main() function instead, which will be the lowest priority task
// See the file ARTKtest.ino for example usage 
// This library is NOT compatible with the Timer1 library or the servo library
// At present the maximum number of sleeping tasks is 8

/******* License ***********************************************************
  This file is part of ARTK - Arduino Real-Time Kernel

  ARTK is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  ARTK is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with ARTK.  If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/ 

#ifndef ARTK_H
#define ARTK_H
#include <stdint.h>
#include <kernel.h>

// This library won't work at all with task stacks less than about 210,
// so the library won't let you set a stack size less than 256
// This is the default size - only a bit larger than the min
#define DEFAULT_STACK 256 

class Semaphore ;
class Task ;
typedef Semaphore* SEMAPHORE ;
typedef Task* TASK ;

// utilities to printf to serial port 
// the first is for use with the F macro and is preferred for conserving SRAM
void Printf(const __FlashStringHelper *fmt, ... ) ;
void Printf(char *fmt, ... )  ;

// #define Printf printf

// IMPORTANT: Call this from Setup() only, and only if you don't like a default
// For each option, -1 says to leave that setting unchanged
// iLargeModel:  1 if you have more than 64k memory (e.g., Mega)
//               0 otherwise (e.g., UNO)
//               Defaults to small memory model (e.g., UNO)
// iTimerUsec:   specify the sleep timer interval in usec
//               min allowed is 1000 (1 msec)
//               max allowed is 32767 (because this is an int argument)
//               defaults to 1000 (1 msec)
void ARTK_SetOptions(int iLargeModel, int iTimerUsec=-1) ;

// a Task can call this to determine how much local stack space it has left
// if it gets below about 203, things will probably break as the preemptive
// kernel needs about that much space on any task to get business done
// inline 
int ARTK_StackLeft() ;

// this is a conservative estimate - it does not traverse the heap freelist, so 
// it includes 16 bytes times the max number of simultaneous sleeps that have 
// ever occurred at the time it is called. If the number of tasks sleeping is 
// less than that max then there will be some additional space left on the 
// freelist, but it is best to assume you will build to that number of sleepers
// again at some point
//inline 
int ARTK_EstAvailRam() ;

// All code enclosed by the following Critical Section macro are protected by 
// a single a global mutex.  Useful, for example, to prevent a waking Task of 
// higher priority from interspersing output on a shared resource (like the
// Serial link) with a lower-priority task that it preempts.
extern SEMAPHORE ARTK_mutex ;
#define CS(x) {ARTK_WaitSema(ARTK_mutex); x ARTK_SignalSema(ARTK_mutex);}

// Task functions
// Valid user task priority is 1 to 16 (1 being lowest)
// In general tasks are created from Setup, but it is safe to create a 
// from any task.  The new task can't swap in until the creating task does 
// something that allows a context switch, such as signaling a semaphore 
// (or allowing an ISR to signal a semaphore), or waiting on a semaphore, 
// or sleeping.  Of course, once a higher priority task starts up, it can 
// take the processor anytime it is ready to do so.
TASK ARTK_CreateTask(void (*root_fn_ptr)(), uint8_t priority = 1, 
     unsigned stacksize = DEFAULT_STACK) ;

// Sleep for so many ticks.  See ARTK_SetOptions above for the tick interval.
// inlined 
void ARTK_Sleep(unsigned ticks) ;

// ARTK is preemptive but does not timeshare automatically between tasks of 
// equal priority. When a task at a given priority is preempted, it
// goes to the back of a round-robin queue for that priority level,
// which allows other tasks at the same priority level to run.
// Normally you won't create tasks of equal priority if you care about
// their relative scheduling. If you do, you may want to consider having 
// them yield to each other.  They can yield by sleeping, by waiting
// on a semaphore, by signaling a semaphore (directly or via an ISR), by
// exiting, or explicitly yielding to other tasks at the same priority level:
// inlined 
void ARTK_Yield() ;

// Multiple tasks can use the same root function, in which case this function
// might be handy for distinguishing between them at run-time.  It is probably
// easier to use a static population id counter though.
// inlined 
TASK ARTK_MyId() ;

// Semaphore functions - for the last the timeout is in ticks
// The version with timeout returns -1 if it timed out, 0 if the semaphore
// was acquired
// When a semaphore is signalled, ARTK makes ready to run the first process
// to wait on the semaphore, which will immediately run only if it is at
// a higher priority than the signaller. Thus semaphore waiters are processed
// in a FIFO order. Some kernels will process semaphore waiters in a priority
// order, which can starve a task. Some provide an option to the programmer.
SEMAPHORE ARTK_CreateSema(int initial_count = 0) ;
// inlined 
void ARTK_WaitSema(SEMAPHORE semaphore) ;
// inlined 
void ARTK_SignalSema(SEMAPHORE semaphore) ;
// inlined 
int  ARTK_WaitSema(SEMAPHORE semaphore, unsigned timeout) ;

// ARTK will terminate when all tasks return (including Main), or you can 
// terminate early by calling this
void ARTK_TerminateMultitasking() ;

#endif
