/**
 * @file 		arm_ADS5560.cpp
 * @author		James Thesing <james.thesing@criticallink.com>
 * @version		1.0
 *
 * @section LICENSE
 *
 *
 *    o  0
 *    | /       Copyright (c) 2005-2011
 *  (CL)---o   Critical Link, LLC
 *    \
 *     O
 *
 *
 * @section DESCRIPTION
 *
 * This class handles exercising the ADS5560 on the ARM. The ARM
 * sets up the ADS556- through a SPIDev and then communicates to
 * a DSPAPP to collect data over upp. Data collected is written to
 * a file "results.dat"
 */

#include "dspapp.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <stdint.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>


#include "ipc_inbound.h"
#include "ipc_outbound.h"

using namespace MityDSP;


#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))



//spidev interface requirements
static const char *device = "/dev/spidev1.4";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t numbytes = 2;
static uint32_t speed = 10000000;
static uint16_t delay = 0;
static uint8_t loopback = 0;
static uint32_t loops = 1;
static uint8_t tx[2];
static uint8_t rx[2];

//point for file IO
FILE *fp;


// Forward declarations
int handleInboundMessage(void *Buffer, uint16_t Length, void *UserArg);
void SPISetup(int fd);
static int transfer( int fd );

volatile bool gbDone = false;

/**
 * used to report errors
 * @param s char array representing error
 */
static void pabort(const char *s)
{
	perror(s);
	abort();
}

/**
 * Initializes spidev
 * @param fd int value of spidev
 */
void SPISetup(int fd)
{
	int ret;
	mode = 0;

	/*
		* spi mode
		*/
		ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
		if (ret == -1)
			pabort("can't set spi mode");

		ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
		if (ret == -1)
			pabort("can't get spi mode");

		/*
		 * bits per word
		 */
		ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
		if (ret == -1)
			pabort("can't set bits per word");

		ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
		if (ret == -1)
			pabort("can't get bits per word");

		/*
		 * max speed hz
		 */
		ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
		if (ret == -1)
			pabort("can't set max speed hz");

		ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
		if (ret == -1)
			pabort("can't get max speed hz");

		printf("spi mode: %d\n", mode);
		printf("bits per word: %d\n", bits);
		printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
}

/**
 * Sends a packet of data through a spi dev
 * @param fd int value of spidev
 * @return -1 on failure/ 0 on success
 *
 */
static int transfer(int fd)
{
	int ret, rv = 0;
	uint8_t rx[ARRAY_SIZE(tx)];

	struct spi_ioc_transfer tr;
	tr.tx_buf = (unsigned long)tx;
	tr.rx_buf = (unsigned long)rx;
	tr.len = numbytes;
	tr.delay_usecs = delay;
	tr.speed_hz = speed;
	tr.bits_per_word = bits;

	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

	if (ret == 1)
		pabort("can't send spi message");

	for (ret = 0; ret < numbytes; ret++) {
		if (loopback && (tx[ret] != rx[ret]))
		{
			rv = -1;
			break;
		}
	}

	if (rv)
		printf("ERROR DETECTED IN RECEIVE STREAM\n");

	return rv;
}
/**
 * main routine for ARM. Initializes SPI, creates DSP link,
 * starts communitcation to DSP, and writes return data to file
 */
int main(int argc, char* argv[])
{
	fp = fopen("results.dat", "w");
	int fd, i, ret;

	if (fp == NULL)
	{
		printf("I couldn't open results.dat for writing.\n");
	    exit(0);
	}
	fd = open(device, O_RDWR);
	SPISetup(fd);

	// tcDspApp class for booting and loading the DSP
	tcDspApp* 		lpDspApp = NULL;
	// Used to setup handling of inbound messages from DSP
	tcIPCInbound* 	lpMessageInbound  = NULL;
	// Used to send messages to the DSP
	tcIPCOutbound* 	lpMessageOutbound  = NULL;
	// Message to send to the DSP
	char			lpMessage[] = "Convert";
	// Pointer to buffer obtained from dsplink
	char*			lpMessageBuffer = NULL;


	// Check application usage
	if (argc < 2)
	{
		printf("usage: <ADS5560.out>\n");
		return -1;
	}

	// Create the DspApp object
	printf("Creating DSPApp object.\n");
	lpDspApp = new tcDspApp();

	// Load the DSP.out file
	printf("Loading file %s\n", argv[1]);
	lpDspApp->LoadApp(argv[1]);

	printf("Starting application.\n");

	// Create the object to handle incoming messages from the DSP
	lpMessageInbound =  new tcIPCInbound((char*)"GPPMSGQ1");

	if (NULL != lpMessageInbound)
	{
		// Register the callback for handling messages from the DSP
		lpMessageInbound->Register(handleInboundMessage, (void*)NULL);

		// Intiailize the inbound controller to create the thread that handles the callbacks
		lpMessageInbound->Initialize();
	}

	// Create the object used to send messages to the DSP
	lpMessageOutbound = new tcIPCOutbound((char*)"DSPMSGQ0");

	// Wait for the DSP to finish initialization
	while(false == gbDone);


	// Reset bool in prep for next receive message from DSP
	gbDone = false;

	//software reset of ADC
	tx[0] = 0x6e;
	tx[1] = 0x01;
	for (i = 1; i <= loops; i++)
		{
			ret = transfer(fd);
			if (ret) {
				printf("Failed after %d cycles\n",i);
				break;
			}
			if (!(i % 10))
				printf(".");
			if (!(i % 400))
				printf("\n");
		}
	//test pattern
	/*
	//tx[0] = 0x65;
	//tx[1] = 0x20; //all 1's
	//tx[1] = 0x40; //all 0's
	//Stx[1] = 0x60; //toggle 1/0
	//tx[1] = 0x80; //ramp 0x0000 -> 0xFFFF

	for (i = 1; i <= loops; i++)
		{
			ret = transfer(fd);
			if (ret) {
				printf("Failed after %d cycles\n",i);
				break;
			}
			if (!(i % 10))
				printf(".");
			if (!(i % 400))
				printf("\n");
		}
	 */
	// Get a buffer for a message to the DSP
	lpMessageBuffer = (char*)lpMessageOutbound->GetBuffer(strlen(lpMessage)+1);

	// Copy the message to the dsplink buffer
	strcpy(lpMessageBuffer, lpMessage);

	/*unsigned short *ptr = ( unsigned short* )0x66000202;
	*ptr =  1;
	printf ("the value of 0x66000202 is: %d\n", *ptr);*/

	// Send the message to the DSP
	printf("Sending a message to the DSP to start receive.\n");
	lpMessageOutbound->SendMessage(lpMessageBuffer);

	// Wait for a message to be received from the DSP or for user to quit

	printf("Waiting for DSP response (type \'q\' to quit)...\n");
	while(!gbDone){}

	printf("Exiting application.\n");

	// Stop the DSP application from running
	lpDspApp->StopApp();


	// Cleanup
	delete lpDspApp;
	delete lpMessageInbound;
	delete lpMessageOutbound;
	fclose(fp);

	return 0;
}

/**
 *	Handle inbound messages from the DSP.
 *
 *	@param apBuffer		Pointer to the buffer containing the message.
 *	@param anLength 	The length of the message.
 *	@param apUserArg	User defined argument.
 *
 *	\return 0.
 */
int handleInboundMessage(void *apBuffer, uint16_t anLength, void *apUserArg)
{

    printf("size of buffer is: %d\n", anLength);
	if (anLength > 1000){
		for( int i = 0; i < 32000; i++ )
		{
			fprintf( fp, "%d\n", ((int16_t*)apBuffer)[i] );
			//printf( "Value %d is : %d\n", i, ((int16_t*)apBuffer)[i] );

		}
		return 0;
	}
	else
	{

	printf("ARM received a message from the DSP:\n");

	// Print the message we received
	printf("\tDSP Message = \"%s\"\n", (char *)apBuffer);

	// Notify the main function that we have received a message from the DSP and are done
	gbDone = true;

	}
	return 0;
}






