/* ---------------------------------------------------------------------------
| Filename: sema_coop.c
|
| Authors:      Thomas Braunl   
|               Thomas Lampart  
|
| Description: 
| This is a RoBiOS demo program demonstrating thread and semaphore usage
| for COOPERATIVE Multitasking
| ------------------------------------------------------------------------- */

#include "eyebot.h"

#define SLAVES 3
#define SSIZE  4096

/** pointer to thread control blocks */
struct tcb *slave_p[SLAVES], *master_p;

/** semaphores */
struct sem sema[SLAVES];

/* ----------------------------------------------------------------------- */
/**Code for task "slave"
   task is started with id-number (0..SLAVES-1)
   and does mutual exclusion with the semaphor matching its id-no.
   semaphor P and V is also controlled by the master task
   execution is deferred to other processes after each loop iteration
*/
void slave()
{ int id, i, count = 0;
  
  /** read slave id no. as supplied from main program during spawn op. */
  id = OSGetUID(0);
  LCDPrintf("slave %d start\n", id);

  while(1) {
    OSSemP(&sema[id]);                      /* occupy semaphore */
      for (i=0; i<2*id; i++) LCDPrintf(" ");   /* space for easy reading */
      LCDPrintf("slave %d:%d\n", id, count);
      count = (count+1) % 100;              /* max count 99 */
    OSSemV(&sema[id]);                      /* free semaphore */ 
    OSSleep(70);                            /* sleep 0.7s --> other threads */
  }
} /* end slave */


/* ----------------------------------------------------------------------- */
/**Code for task "master"
   master handles user input via keys to occupy and free semaphores,
   which in turn enable slave tasks to work or block them
*/
void master()
{ int i;
 
  /** start with both semaphores and slave tasks blocked */
  int block[SLAVES] = {1,1,1}; 
  LCDPrintf("master start\n");

  /** display appropr. menu for soft-keys, depend. on slave task status */
  LCDMenu("V.0", "V.1", "V.2", "END");

  while(1) { 
    switch (KEYRead())
    {
      case KEY1: if (block[0]) { OSSemV(&sema[0]); LCDMenu("P.0", "","",""); }
			  else { OSSemP(&sema[0]); LCDMenu("V.0", "","",""); }
		 block[0] = !block[0];
		 break;

      case KEY2: if (block[1]) { OSSemV(&sema[1]); LCDMenu("", "P.1", "",""); }
			  else { OSSemP(&sema[1]); LCDMenu("", "V.1", "",""); }
		 block[1] = !block[1];
		 break;

      case KEY3: if (block[2]) { OSSemV(&sema[2]); LCDMenu("","", "P.2", ""); }
			  else { OSSemP(&sema[2]); LCDMenu("","", "V.2", ""); }
		 block[2] = !block[2];
		 break;

      case KEY4: /** kill both slaves and then exit master itself */
		 for (i=0; i<SLAVES; i++) OSKill(slave_p[i]);
		 OSKill(0);
		 break;
    }
    OSReschedule();  /* transfer control to other threads */
  } /* end while */
} /* end master */


/* ----------------------------------------------------------------------- */
/** main of demo program */
int main()
{ int i;

  /** initialize multithreading */
  LCDPrintf("Kernel.. ");
  OSMTInit(COOP);
  LCDPrintf("done\n");

  /** initialize semaphores */
  LCDPrintf("Semaphore..");
  for (i=0; i<SLAVES; i++) OSSemInit(&sema[i],0);
  LCDPrintf("done\n");

  /** Init threads
      Spawn needs: a pointer to the control block 
      the name of the thread, a pointer to its stack
      a pointer to its code, the size of its stack; its priority
      and returns: a pointer to the initializes control block
  */
  LCDPrintf("Spawn..");
  for (i=0; i<SLAVES; i++) {
    slave_p[i] = OSSpawn("slave-i", slave, SSIZE, MIN_PRI,i);
    if(!slave_p[i]) OSPanic("spawn for slave failed");
  }
  master_p     = OSSpawn("master", master, SSIZE, MIN_PRI,0);
  if(!master_p) OSPanic("spawn for master failed");
  LCDPrintf("done\n");
  
  /** READY all threads and start one */
  for (i=0; i<SLAVES; i++) OSReady(slave_p[i]);
  OSReady(master_p);
  OSReschedule();

/* ----------------------------------------------------------------------- */
/** processing will return HERE, when there is no READY thread left */
	
  LCDPrintf("back to main\n");
  LCDMenu (" ", " ", " ", "END");
  KEYWait(KEY4);
  return 0;
}


