// System includes
//
// Note: Compile with cc -T 1 -g file.c -o lab

#include <sys/irqinfo.h>
#include <sys/proxy.h>
#include <sys/kernel.h>
#include <stdio.h>
#include <math.h>
#include <process.h>
#include <unistd.h>
#include <libc.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>

#include "semquh.h"
#include "semquc.c"

// DT2821 Port Addresses              
#define ADCSR  		0x0240    // A/D control & status register.     
#define CHANCSR     0x0242    // CHANNEL control & status register. 
#define ADDAT  		0x0244    // A/D DATA port.                     
#define DACSR  		0x0246    // D/A control & status register.     
#define DADAT  		0x0248    // D/A data port.                     
#define DIODAT 		0x024a    // Digital IO data port.              
#define SUPCSR 		0x024c    // Board control & status register.   
#define TMRCTR 		0x024e    // Pacer clock register.              


// Global Variables
Queue *mqueuea;
Queue *mqueueb;
pid_t proxy;

int debug = 0;

volatile unsigned adcCounter;

#pragma off (check_stack);
pid_t far timer_int_handler()
/*  The timer interrupt handler is triggered by a hardware timer
 *  interrupt every 50 milliseconds. Every N*50 milliseconds, a 
 *  proxy is kicked to initiate an A/D scan.
 */
{
     int N;
     N = 10;
     if( (++adcCounter % N) == 0 ) return(proxy);
     return(0);
}
#pragma on (check_stack);


void set_pacer_freq(double pacer_freq)
{
     unsigned short int M, N, count;

/*  Each A/D conversion is triggered on the first pulse of the pacer clock
 *  output following a (software) trigger). The function set_pacer_freq()
 *  sets the pacer clock frequency (in units of Hz.) to a value between 
 *  0.4768 Hz to 4 MHz.
 */
     if (pacer_freq > 4000000) pacer_freq = 4000000;
     if (pacer_freq < (4000000/pow(2,23))) pacer_freq = 4000000/pow(2,23);
     N = ceil( log(4000000/(256*pacer_freq))/log(2) );
     N = N & 0x000F;
     if (N == 1) N = 0;
     M = 256 - ( 4000000/(pacer_freq * pow(2,N)) );
     M = M & 0x00FF;
     count = (N * 256) + M;
     outpw(TMRCTR, count);
     printf("Pacer clock frequency = %f \n", 4000000/((pow(2,N)*(256-M))));
}

void reset_adc(double freq)
{
     outpw(SUPCSR, 0x0001);   /* DT2821 Board Reset.                 */
     set_pacer_freq(freq);   /* Set pacer clock frequency.          */
     outpw(SUPCSR, 0x2240);
     outpw(CHANCSR, 0x8000); /* Activate "Load List Enable".        */
                             /* # of entries in channel list = 16.  */ 
     outpw(ADCSR, 0x0200);   /* First entry = ch. 0, with gain = 1. */
     outpw(CHANCSR, 0x0000); /* Deactivate "Load List Enable".      */
     outpw(SUPCSR, 0x0010);  /* Preload multiplexer                 */
     printf("Channel list has %d, entries.\n", 0x000F & inpw(CHANCSR));
     delay(2000);            /* Delay for message display.          */ 
}


void get_word_adc(int *ptr_addata)
{
/*  Function get_word_adc() performs a single A/D conversions on 
 *  analog input channel 0 of the DT2821 board and stores the ADC
 *  data (in 12-bit offset binary, right justified) in the integer
 *  variable pointed to by ptr_data.
 */

     while ( (inpw(ADCSR) & 0x0100) != 0 ) {};  /* Wait for MUXBUSY = 0.           */
     outpw(SUPCSR, 0x0008);  /* Software trigger pacer clock    */ 
                             /* which starts A/D conversions.   */
     while ( (inpw(ADCSR) & 0x0080) == 0) {
     };                            /* Wait for A/D DONE               */
     *ptr_addata = inpw(ADDAT);
}

void stop_adc()
{
     outpw(SUPCSR, 0x0001);			/* Reset DT2821 board.                 */
}

void reset_dac()
{
     outpw(DACSR, 0x0002);			/* Selects dual mode DAC output and    */
									/* DIO port 0 as input and DIO port 1  */
									/* as output.                          */
     while((inpw(DACSR) & 0x0080) == 0) 
	 {
     };								/* Wait for DAC READY = 1              */

     outpw(DADAT, 0x0800);
     outpw(DADAT, 0x0800);
     while((inpw(DACSR) & 0x0080) != 0)
	 {
     };						
									/* Wait for DAC READY = 0              */
     outpw(SUPCSR, 0x0080);			/* Software trigger for D/A            */     
     printf("DAC outputs are initialized to 0.0V. \n");
     delay(2000);
}

void put_word_dac(int data)
{
     while ( (inpw(DACSR) & 0x080) == 0 ) 
	 {
     };									/* Wait for DAC READY = 1.        */
     
	 outpw(DADAT, data);				/* to channel X DAC 0  */
     outpw(DADAT, data);				/* to channel Y DAC 1  */
     
	 while((inpw(DACSR) & 0x0080) != 0) 
	 {
     };
     
	 outpw(SUPCSR, 0x0080);         /* S/W trigger for D/A            */
}


// Obtain A->D data from the shared memory buffer and then do something ot
// the data, and then send the modified sample to the DAC for conversion.
void processA(Queue *mqueuea, int numberCycles)
{
	int data;
	int i;

	for (i=0; i<numberCycles; i++)
	{
		queue_get(mqueuea,&data);

		// High Pulse
		if (data)
			data = 1000;

		put_word_dac(data);
		
		if (debug)
			printf("Process 1 : Out DAC: %i Queue = %i\n", data, mqueuea);
	}
}

// Obtain A->D data from the shared memory buffer and then do something ot
// the data, and then send the modified sample to the DAC for conversion.
void processB(Queue *mqueueb, int numberCycles)
{
	int data;
	int i;

	for (i=0; i<numberCycles; i++)
	{
		queue_get(mqueueb, &data);

		// Low pulse
		if (data)
			data = -1000;
		
		put_word_dac(data);

		if (debug)
			printf("Process 2 : Out DAC: %i Address = %i\n", data, mqueueb);
	}
}


// Main Program 
void main()
{
    int i, id;
	int pid_A;
	int pid_B;
	int numElements;
    int ad_data;
	int cycles = 25;

    float pacerFrequency;
	float runningTime;

	unsigned short int dio_data;
	ssize_t elementSize; 

	clock_t startTime;
	clock_t endTime;

	elementSize = 2;
	numElements = 64;

	printf("Clocks per sec: %f\n", (float)CLOCKS_PER_SEC);

	// Connect to the proxy
    if( (proxy = qnx_proxy_attach(0, 0, 0, 0) ) == -1 ) 
	{
          printf("Proxy connect error");
          return;
    }

    // Initialize ADC and DAC
	reset_dac();
    pacerFrequency =  2000.0;
    reset_adc(pacerFrequency);
 
	// Set up the interrupt handler
	if((id=qnx_hint_attach(-1, &timer_int_handler, FP_SEG(&adcCounter))) == -1) 
	{
          printf("Unable to attach to an interrupt\n");
          return;
    }
     
	// Set up the shared buffers for each process
	mqueuea = new_queue(elementSize, numElements);
	mqueueb = new_queue(elementSize, numElements);

	// Set up two consumer processes
	if ((pid_A = fork()) == -1)
		printf("Forking error\n");
	
	if (pid_A == 0)
	{
		startTime = clock();
		processA(mqueuea, cycles);
		endTime = clock();

		runningTime = ((endTime - startTime - 50*cycles)  / (float)(cycles * 10));
		printf("Reader A throughput: %f ms / read cycle\n", runningTime);
	}
	else
	{	
		// Launch the second process
		if ((pid_B = fork()) == -1)
		{
				printf("Forking error\n");
		}
		else if (pid_B == 0)
		{
				startTime = clock();
				processB(mqueueb, cycles);
				endTime = clock();

				runningTime = ((endTime - startTime - 50*cycles)  / (float)(cycles * 10));
				printf("Reader B throughput: %f ms / read cycle\n", runningTime);
		}
		else
		{
			startTime = clock();

			// It and run the collect process
			for (i=0; i<cycles; i++)
			{
				Receive(proxy, 0, 0);
				get_word_adc(&ad_data);

				if (debug)
					printf("Collected Value = %i\n", &ad_data);

				queue_put(mqueuea, &ad_data);
				queue_put(mqueueb, &ad_data);

				if (debug)
					printf("Collect:  Put %i into the buffers A & B\n", ad_data);
			}
			endTime = clock();

			runningTime = ((endTime - startTime - 50*cycles)  / (float)(cycles*10));
			printf("Collector throughput: %f ms / read cycle\n", runningTime);
		}
	}


	// Shutdown the A-D and nicely exit
	stop_adc();
	qnx_hint_detach(id);
};
