#define X5K_MCU_CMD_SHUTDOWM	"#s\n"
#define X5K_MCU_CMD_GET_TEMP	"#t\n"
#define X5K_MCU_CMD_GET_VOLT	"#v\n"
#define X5K_MCU_CMD_GET_FAN	"#f\n"

#define X5K_MCU_TEMPSIZE 12
#define X5K_MCU_VOLTSIZE 51
#define X5K_MCU_FANSIZE 9

#include <proto/exec.h>
#include <proto/dos.h>
#include <devices/serial.h>
#include <stdio.h>

struct TempData
{ /* alle Werte in Grad Celius */
	int32 PCB;
	int32 CPU;
	int32 PCI;
};

struct VoltData
{ /* alle Werte in mV */
	int32 CPLD;
	int32 XenaXorro_A;
	int32 XenaXorro_B;
	int32 PCI;
	int32 Xena;
	int32 Main33;
	int32 Main25;
	int32 Ethernet;
	int32 Platform;
	int32 CoreA;
	int32 CoreB;
	int32 DDR;
};

struct FanData
{
	int32 PWM; // 0 = LГјfter aus, 255 = LГјfter auf 100%
	int32 RPM;
};

int32 ConvertTemp(struct TempData *td, char* t);
int32 ConvertVolt(struct VoltData *vd, char* v);
int32 ConvertFan(struct FanData *fd, char* f);
int32 HexToInt(char **c, int len);

int32 HexToInt(char **c, int len)
{
	int32 r = 0;
	int32 t = 0;
	while(len)
	{
		if( (**c >='0') & (**c <= '9') )
			t = **c - 48;

		if( (**c >='A') & (**c <= 'F') )
			t = **c - 55;

		if( (**c >='a') & (**c <= 'f') )
			t = **c - 87;

		r = (r << 4) + t;
		(*c)++;
		len--;
	}
	return r;
	}

int32 ConvertTemp(struct TempData *td, char* t)
{
	int32 ret = 0;
	
	if(	(t[0] != '$') | (t[1] != 't') )
	{
		return 1;
	}
	t = t + 3;
	td->PCB = HexToInt(&t, 2);
	t++;
	td->CPU = HexToInt(&t, 2);
	t++;
	td->PCI = HexToInt(&t, 2);

	return ret;
	}

int32 ConvertVolt(struct VoltData *vd, char* v)
{
	int32 ret = 0;
	
	if(	(v[0] != '$') | (v[1] != 'v') )
	{
		return 1;
	}
	v = v + 2;
	
	vd->CPLD =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->XenaXorro_A =	(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->XenaXorro_B =	(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->PCI =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->Xena =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->Main33 =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->Main25 =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->Ethernet =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->Platform =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->CoreA =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->CoreB =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	vd->DDR =		(HexToInt(&v, 2) * 1000) + (HexToInt(&v, 2) *10);
	
	return ret;
	}

int32 ConvertFan(struct FanData *fd, char* f)
{
	int32 ret = 0;
	
	if(	(f[0] != '$') | (f[1] != 'f') )
	{
		return 1;
	}
	f = f + 2;
	
	fd->PWM = HexToInt(&f, 2);
	fd->RPM = HexToInt(&f, 4);
	
	return ret;
}


int main()
{

/*	
	char test_temp[X5K_MCU_TEMPSIZE] = "$t+20+38+4B\n";
	char test_volt[X5K_MCU_VOLTSIZE] = "$v031E031E01000100031E023201140100010A010A013201500041\n"; !! BUG ! LESS On 2 BYTES IN REALITY !
	char test_fan[X5K_MCU_FANSIZE] = "$fC010E6\n";
*/	
	struct TempData temp = {0,0,0};
	struct VoltData volt = {0,0,0,0,0,0,0,0,0,0,0,0};
	struct FanData fan ={0,0};
	
	int ret = RETURN_OK;
	struct MsgPort* port = NULL;
	struct IOExtSer* req = NULL;

#define BUFSIZE 60
	char buf[BUFSIZE];
	uint8 err = 0;
	uint8 dev = 1;
	
	port = IExec->AllocSysObject(ASOT_PORT, NULL);
	if(!port)
	{
		printf("Kein Port\n");
		goto error;
		}
	req = IExec->AllocSysObjectTags(ASOT_IOREQUEST, ASOIOR_Size, sizeof(struct IOExtSer),
													ASOIOR_ReplyPort, port,
													TAG_DONE);
	if(!req)
	{
		printf("Kein Request\n");
		goto error;
		}
		
	err = IExec->OpenDevice("serial.device", 1, (struct IORequest*)req, 0);
	if(err)
	{
		dev = 0;
		printf("OpenDevice fehler: %d\n", err);
		goto error;
		}
	
	// Parameter setzten um MCU anzusprechen
	req->IOSer.io_Command = SDCMD_SETPARAMS;
	req->io_Baud = 38400; // 38400 Baud
	req->io_ReadLen = 8; // 8 bit data
	req->io_WriteLen = 8; // 8 bit data
	req->io_StopBits = 1; // 1 Stop bit
	req->io_SerFlags &= ~SERF_PARTY_ON; // No Parity
	req->io_SerFlags |= SERF_XDISABLED; // No Flow Control
	err = IExec->DoIO((struct IORequest*)req);
	if(err != IOERR_SUCCESS)
	{
		printf("Setzen der seriellen Parameter gescheitert. Code: %d\n", err);
			goto error;
		}
		
	// Werte abfragen und umwandeln
	// Temperaturen
	req->IOSer.io_Length = 3;
	req->IOSer.io_Data = (APTR)X5K_MCU_CMD_GET_TEMP;
	req->IOSer.io_Command = CMD_WRITE;
	err = IExec->DoIO((struct IORequest*)req);
	
	req->IOSer.io_Length = X5K_MCU_TEMPSIZE;
	req->IOSer.io_Data = (APTR)buf;
	req->IOSer.io_Command = CMD_READ;
	err = IExec->DoIO((struct IORequest*)req);
	
	ConvertTemp(&temp, buf);
	// Spannungen
	req->IOSer.io_Length = 3;
	req->IOSer.io_Data = (APTR)X5K_MCU_CMD_GET_VOLT;
	req->IOSer.io_Command = CMD_WRITE;
	err = IExec->DoIO((struct IORequest*)req);
	
	req->IOSer.io_Length = X5K_MCU_VOLTSIZE;
	req->IOSer.io_Data = (APTR)buf;
	req->IOSer.io_Command = CMD_READ;
	err = IExec->DoIO((struct IORequest*)req);
	
	ConvertVolt(&volt, buf);
	// LГјfter
	req->IOSer.io_Length = 3;
	req->IOSer.io_Data = (APTR)X5K_MCU_CMD_GET_FAN;
	req->IOSer.io_Command = CMD_WRITE;
	err = IExec->DoIO((struct IORequest*)req);
	
	req->IOSer.io_Length = X5K_MCU_FANSIZE;
	req->IOSer.io_Data = (APTR)buf;
	req->IOSer.io_Command = CMD_READ;
	err = IExec->DoIO((struct IORequest*)req);
	
	ConvertFan(&fan, buf);
	

		
		
	// Ergebnisse Ausgeben
	printf("Temperaturen\n");
	printf("************\n");
	printf("PCB %ldВєC CPU %ldВєC PCIe %ldВєC\n\n", temp.PCB, temp.CPU, temp.PCI);
	printf("Spannungen\n");
	printf("**********\n");
	printf("CPLD         %ldmV\n", volt.CPLD);
	printf("XenaXorroA   %ldmV\n", volt.XenaXorro_A);
	printf("XenaXorroB   %ldmV\n", volt.XenaXorro_B);
	printf("PCIe         %ldmV\n", volt.PCI);
	printf("Xena         %ldmV\n", volt.Xena);
	printf("3,3V Schiene %ldmV\n", volt.Main33);
	printf("2,5V Schiene %ldmV\n", volt.Main25);
	printf("Ethernet     %ldmV\n", volt.Ethernet);
	printf("Platform     %ldmV\n", volt.Platform);
	printf("CoreA        %ldmV\n", volt.CoreA);
	printf("CoreB        %ldmV\n", volt.CoreB);
	printf("DDR3         %ldmV\n", volt.DDR);
	printf("LГјfter\n");
	printf("******\n");
	printf("PWM-Signal %ld\n", fan.PWM);
	printf("Drehzahl/min %ld\n", fan.RPM);
	
	goto cleanup;
	
error:
	ret = RETURN_WARN;	
cleanup:
	if(dev)
	{
		IExec->CloseDevice((struct IORequest*)req);
		}
	if(req)
	{
		IExec->FreeSysObject(ASOT_IOREQUEST, req);
		}
	if(port)
	{
		IExec->FreeSysObject(ASOT_PORT, port);
		}
	return ret;

}