/*
 * uart_server.c
 *
 *  Created on: May 1, 2024
 *      Author: cciof
 */

//we assume altera_avalon_uart as serial device.
//we must change from avaon uart to uartlite.

//#include <string.h>
//#include <stdio.h>
#include <xil_types.h>
//#include <unistd.h>
#include "xparameters.h"
#include "uart_server.h"


volatile u32 * ser_dev_base=(u32 *)XPAR_XUARTLITE_0_BASEADDR;

//#define TRDY_MASK ((unsigned int)0x0040)  // to be changed
//#define RRDY_MASK ((unsigned int)0x0080)  // to be changed
#define RRDY_MASK ((u32) 0x0001)     // if 1, input ueue not empty!  
#define TXFULL_MASK ((u32) 0x0008)     // if 1, TX que is full!  

void execute_cmd(void f());
void ex_nop(void);
void ex_wrp(void);
void ex_wlr(void);
void ex_rpb(void);
void ex_wpc(void);


struct command{
	char mnem[3];
	int num_args;
	void * fn_ptr;
};

struct command cmdlist[]={
		{"NOP",0,ex_nop}, //no operation
		{"WRP",1, ex_wrp}, //write a number of dots (".....") specified by the arg
		{"WLR",2, ex_wlr}, //sets leds (Arg1:Arg2)&0x3FF;
        {"RPB",0,ex_rpb},
        {"WPC",1,ex_wpc}
};

struct command active_cmd;

#define MAX_CMD_LEN 32
#define MAX_RSP_LEN 32
#define MAX_ARG_NUM 5 //one sub command arg + 32 bit

int cmd_state; //state variable for command receiving process
				//0:initial state; waiting for '['
				//1: receiving command
int cmd_pointer;
int rsp_pointer;
int responding;
int cmd_is_in;
int cmd_scan; //index used to scan received command
char cmd_string[MAX_CMD_LEN];//command received form PC
char rsp_string[MAX_CMD_LEN];//response string to be send to the PC
int in_arg_val[MAX_ARG_NUM];

void uart_server_send_answer(void);
int uart_server_cmd_parser (char c);
int get_cmd_string(void);
int check_cmd_string(void);
int get_arg_val(void);
int do_command(void);
void execute_cmd(void f());
void queue_response(void);

//debug

void uart_server_manager(void)
{
	char rec_char;
	int uart_status;
	//we first check for characters in the receiver queue
	//we assume that no character can be send by the host if current task is not complete

	uart_status=*(ser_dev_base+2)&RRDY_MASK;

	if (uart_status!=0)
	{
		//data in data field is valid
		rec_char=(char)(*ser_dev_base&0xFF);
		uart_server_cmd_parser(rec_char);
		if (cmd_is_in==1)//a command has been received
		{
			if(do_command()<0)
			{
				sprintf(rsp_string,"[ERR]");
			}
			else
			{
				execute_cmd(active_cmd.fn_ptr);
			}
			queue_response();
			cmd_is_in=0;
		}
	}
	//now we send out characters, if it is the case
	uart_server_send_answer();
}

void uart_server_send_answer(void)
{
	int go_on=1;
	//int ok_tosend;
    int tx_que_full;
	while (go_on==1)
	{
		//ok_tosend=*(ser_dev_base+2)&TRDY_MASK;
		tx_que_full=*(ser_dev_base+2)&TXFULL_MASK;

		//if ((responding==1)&&(ok_tosend!=0)) //this was for ALTERA
		if ((responding==1)&&(tx_que_full==0)) //this is for xilinx 

		{
			if (rsp_string[rsp_pointer]==0)
			{
				responding=0; //transmission completed
				go_on=0;
				return;
			}
			else
			{
				*(ser_dev_base+1)=rsp_string[rsp_pointer];
				rsp_pointer++;
			}
		}
		else
		{
			go_on=0;
		}
	}
	return;
}

int uart_server_cmd_parser(char c )
{
	int error=0;
	switch (cmd_state)
	{
	case 0: //initial state, waiting for '['
		if (c=='[')
		{
			cmd_pointer=0;
			cmd_state=1;
			error=0;
		}
		break;
	case 1:  //receiving command
		if (c==']')
		{
			//end of command found
			cmd_state=0; //ready to receive the next command;
			cmd_is_in=1; //a command is in
			cmd_string[cmd_pointer]=0; //terminating the command string with 0
			error=0;
		}
		else
		{
			if (c==' ' || c==',')
			{
				//ignore space and comma
			}
			else
			{
				cmd_string[cmd_pointer]=c;
				cmd_pointer++;
				if (cmd_pointer==MAX_CMD_LEN)
				{
					error=-1; //command too long
					cmd_state=0; //go back to state 0 and wait for a new command
				}
			}
		}
		break;
	}
	return error;

}

int do_command(void)
{
	int arg_num;
	int i;

	cmd_scan=0;
	if (get_cmd_string()<0)//get thre charcaters
	{
		return -1;
	}
	if ((arg_num=check_cmd_string())<0)
	{
		return -2;
	}//return if string does not correspond to any command; arg_num is the number of arguments of the command (found in the proper array)

	for (i=0; i<arg_num; i++)
	{
		if ((in_arg_val[i]=get_arg_val())<0)
		{
			return -3; //not enough arguments or error
		}
	}

	return 0;
}

int get_cmd_string(void)
{
	int i;
	if (strlen(&cmd_string[cmd_scan])<3)
	{
		return -1;//stringa troppo corta per contenere un comando
	}
	else
	{
		for (i=0; i<3; i++)
		{
			active_cmd.mnem[i]=cmd_string[cmd_scan];
			cmd_scan++;
		}

		return 0;
	}
}


int check_cmd_string(void)
{
	int numcmds=sizeof(cmdlist)/sizeof(cmdlist[0]);
	int i;
	int found=0;
	for (i=0; i< numcmds; i++)
	{
		if (memcmp(active_cmd.mnem,cmdlist[i].mnem,3)==0)
		{
			found =1;
			active_cmd.num_args=cmdlist[i].num_args;
			active_cmd.fn_ptr=cmdlist[i].fn_ptr;
		}
	}
	if (found==1)
	{
		return active_cmd.num_args;
	}
	else
	{
		return -1;
	}
}

int get_arg_val(void)
{
	int i;
	char c;
	int val=0;
	if (strlen(&cmd_string[cmd_scan])<2)
	{
		return -1;//stringa troppo corta per contenere un argomento (argoomento 2 cifre esadecimali conlettere maiuscole)
	}
	else
	{
		for (i=0; i<2; i++)
		{
			c=cmd_string[cmd_scan];
			if (((c>='A') && (c<='F'))||((c>='0') && (c<='9')))
			{
				if ((c>='A') && (c<='F'))
				{
					val=val*16+(c-'A')+10;
				}
				else
				{
					val=val*16+c-'0';
				}
			}
			else
			{
				return -1;//error in argument
			}

			cmd_scan++;
		}


		return val;
	}
}



void uart_server_init(void)
{
	cmd_state=0;
	cmd_pointer=0;
	cmd_is_in=0;
	rsp_pointer=0;
	responding=0;
	return;
}


void execute_cmd(void f())
{
	f();
}

void queue_response(void)
{
	rsp_pointer=0;
	responding=1;

	return;
}

void ex_nop(void)
{
	sprintf(rsp_string,"[nop received]");

	return;
}

void ex_wrp(void)
{
	int i;
	rsp_string[0]='[';
	for (i=1; i< in_arg_val[0]+1;i++)
	{
		rsp_string[i]='.';
	}
	rsp_string[in_arg_val[0]+1]=']';
	rsp_string[in_arg_val[0]+2]=0;

	return;
}


void ex_wlr (void) // write ler red (two args)
{
	volatile int * pledr=(int *)XPAR_XGPIO_0_BASEADDR;

	*pledr=(in_arg_val[0]*256+in_arg_val[1]);

	sprintf(rsp_string,"[OK!]");
    return;
}

void ex_wpc (void) // write on connector C 
{
	volatile u32 * pbptr=(u32 *)XPAR_PERTEST_0_BASEADDR;
    
	*(pbptr+1)=(in_arg_val[0]);

	sprintf(rsp_string,"[OK!]");
    return;
}

void ex_rpb (void) // write on connector C 
{
	volatile u32 * pbptr=(u32 *)XPAR_PERTEST_0_BASEADDR;
    
    int dummy;

	dummy=*(pbptr);

	sprintf(rsp_string,"[%02X]",dummy);

    return;
}



