//
//	SMBus (I2C) communication test
//		(c) 2019	1YEN Toru
//
//
//	2019/10/26	ver.1.00
//


#define		PROG_NAME	"test_smbus"
#define		PROG_VERS	"1.00"
#define		PROG_COPY	"(c) 2019 1YEN Toru"


typedef		unsigned	char	uint8;
typedef		unsigned	short	uint16;
typedef		unsigned	int		uint32;


#include	<ctype.h>
#include	<errno.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<unistd.h>
#include	<wiringPi.h>
#include	<wiringPiI2C.h>


const	int		ErrNo=0;				// no error
const	int		ErrBadParam=-1;			// bad parameter
const	int		ErrInit=-2;				// initialization error
const	int		ErrOpen=-3;				// fail to open file
const	int		ErrMarch=-4;			// marching: read error
const	int		ErrMarchW=-5;			// marching: write error
const	int		i2c_slv_adr=0x08;		// i2c: Arduino slave address
const	int		i2c_max_reg=256;		// i2c: Arduino i/o register count
const	int		OptCmdNop=0x00;			// nop; command line option
const	int		OptCmdDump=0x01;		// dump
const	int		OptCmdMarch=0x02;		// marching test
const	int		OptCmdRead=0x04;		// read byte
const	int		OptCmdWrite=0x08;		// write byte



int		ovr_wiringPiI2CReadReg8 (
int		fi2c,
int		cmd)
{
//#define		DEBUG_SMBR
#ifdef		DEBUG_SMBR
	int		cnt;
	uint8	buf;

	buf=cmd;
	write (fi2c,&buf,1);
	cnt=read (fi2c,&buf,1);
	if (cnt<=0)
		return (-1);

	return (buf);
#else	//	DEBUG_SMBR
	return (wiringPiI2CReadReg8 (fi2c,cmd));
#endif	//	DEBUG_SMBR
}

int		marching (
int		fi2c)
{
	// marching test for i2c memory module fi2c
	// return: ErrNo / ErrMarch / ErrMarchW
	int		idx;

	// write all "0"
	for (idx=0; idx<i2c_max_reg; idx++)
		if (wiringPiI2CWriteReg8 (fi2c,idx,0)==-1)
		{
			// ERROR:
			return (ErrMarchW);
		}
	// marching, read "0", write "1"
	for (idx=0; idx<i2c_max_reg; idx++)
	{
		if (ovr_wiringPiI2CReadReg8 (fi2c,idx)!=0)
		{
			// ERROR:
			return (ErrMarch);
		}
		if (wiringPiI2CWriteReg8 (fi2c,idx,0xff)==-1)
		{
			// ERROR:
			return (ErrMarchW);
		}
	}
	// marching, read "1", write "0"
	for (idx=0; idx<i2c_max_reg; idx++)
	{
		if (ovr_wiringPiI2CReadReg8 (fi2c,idx)!=0xff)
		{
			// ERROR:
			return (ErrMarch);
		}
		if (wiringPiI2CWriteReg8 (fi2c,idx,0)==-1)
		{
			// ERROR:
			return (ErrMarchW);
		}
	}
	// read all "0"
	for (idx=0; idx<i2c_max_reg; idx++)
		if (ovr_wiringPiI2CReadReg8 (fi2c,idx)!=0)
		{
			// ERROR:
			return (ErrMarch);
		}

	return (ErrNo);
}


int		xtoi (
const	char	*shex)
{
	// convert hexadecimal string to integer
	const	char	*str_c;
	const	char	*hex_c;
	int		ihex;
	const	char	*xdigit="0123456789abcdef";

	ihex=0;
	for (str_c=shex; (*str_c)!='\0'; str_c++)
	{
		hex_c=strchr (xdigit,tolower (*str_c));
		if (hex_c!=NULL)
			ihex=ihex*16 + (hex_c - xdigit);
		else
			break;
	}

	return (ihex);
}


int		main (
int		argc,
char	**argv)
{
	int		idx;
	int		rtncod;
	int		opt;
	int		cmd;
	int		dat;
	int		fi2c;
	int		areg[i2c_max_reg];

	// arguments
	opt=OptCmdDump;
	cmd=0;
	dat=0;
	for (idx=0; idx<argc; idx++)
	{
		if (idx==0)
			continue;
		else if (strcmp (argv[idx],"-c")==0)
		{
			// show /boot/config.txt
			rtncod=system ("grep 'i2c_' /boot/config.txt");
			if (rtncod!=0)
				perror (NULL);
			return (rtncod);
		}
		else if (strcmp (argv[idx],"-rd")==0 && idx + 1<argc)
		{
			// read byte
			idx++;
			cmd=xtoi (argv[idx]);
			opt=OptCmdRead;
		}
		else if (strcmp (argv[idx],"-wr")==0 && idx + 2<argc)
		{
			// write byte
			idx++;
			cmd=xtoi (argv[idx]);
			idx++;
			dat=xtoi (argv[idx]);
			opt=OptCmdWrite;
		}
		else if (strcmp (argv[idx],"-m")==0)
		{
			// marching test
			opt=OptCmdMarch;
		}
		else if (strcmp (argv[idx],"-d")==0)
		{
			// dump
			opt |= OptCmdDump;
		}
		else
		{
			// show help
			printf ("%s ver.%s %s\n",PROG_NAME,PROG_VERS,PROG_COPY);
#ifdef		DEBUG_SMBR
			printf ("attention: not a SMBus version, DEBUG_SMBR enabled.\n");
#endif	//	DEBUG_SMBR
			printf ("usage: %s [<options>]\n",PROG_NAME);
			printf ("options:\n");
			printf ("	-h		Show help, this message.\n");
			printf ("	-d		Dump memory (default)\n");
			printf ("	-m		Marching test\n");
			printf ("	-rd <adr>	Read byte from <adr>.hex\n");
			printf ("	-wr <adr> <dat>	Write byte <dat>.hex to <adr>.hex\n");
			printf ("	-c		Show I2C configurations in /boot/config.txt\n");
			printf ("wirings:\n");
			printf ("	3.3V(phys#1) --> L/S.LV\n");
			printf ("	SDA(phys#3) <=> L/S.LV1\n");
			printf ("	SCL(phys#5) <=> L/S.LV2\n");
			printf ("	GND(phys#9) <=> L/S.GND <=> Arduino.GND\n");
			printf ("	Arduino.5V --> L/S.HV\n");
			printf ("	Arduino.SDA <=> L/S.HV1\n");
			printf ("	Arduino.SCL <=> L/S.HV2\n");
			printf ("	Arduino slave address=0x%02x\n",i2c_slv_adr);
			printf ("\n");

			return ((strcmp (argv[idx],"-h")==0)? ErrNo: ErrBadParam);
		}
	}

	// initialize
	rtncod=ErrNo;
	if (wiringPiSetupPhys ()==-1)
	{
		// ERROR:
		rtncod=ErrInit;
		printf ("ERROR: initialize wiringPi library\n");
		perror (NULL);
		return (rtncod);
	}

	// I2C
	fi2c=wiringPiI2CSetup (i2c_slv_adr);
	if (fi2c<0)
	{
		// ERROR:
		rtncod=ErrOpen;
		printf ("ERROR: I2C open %d for slave=0x%02x\n",fi2c,i2c_slv_adr);
		perror (NULL);
		return (rtncod);
	}

	if (opt&OptCmdMarch)
	{
		uint32	usec;

		printf ("Start marching test, wait a moment...\n");
		usec=micros ();
		rtncod=marching (fi2c);
		usec=micros () - usec;
		if (rtncod==ErrNo)
		{
			// success:
			printf ("OK: marching test passed (%d[us])\n",usec);
		}
		else
		{
			// ERROR:
			printf ("ERROR: marching test failed %d\n",rtncod);
		}
	}

	if (opt&OptCmdRead)
	{
		dat=ovr_wiringPiI2CReadReg8 (fi2c,cmd);
		if (dat<0)
		{
			// ERROR:
			printf ("ERROR: I2C read byte %d\n",dat);
			perror (NULL);
		}
		else
		{
			// success:
			printf ("OK: I2C read byte [0x%04x]=0x%02x\n",cmd,dat);
		}
	}

	if (opt&OptCmdWrite)
	{
		rtncod=wiringPiI2CWriteReg8 (fi2c,cmd,dat);
		if (rtncod==-1)
		{
			// ERROR:
			printf ("ERROR: I2C write byte %d\n",rtncod);
			perror (NULL);
		}
		else
		{
			// success:
			printf ("OK: I2C write byte [0x%04x]=0x%02x\n",cmd,dat);
		}
	}

	if (opt&OptCmdDump)
	{
		// dump all i/o registers
		int		errcnt;
		uint32	usec;

		// read all
		errno=0;
		usec=micros ();
		for (idx=0; idx<i2c_max_reg; idx++)
		{
			areg[idx]=ovr_wiringPiI2CReadReg8 (fi2c,idx);
		}
		usec=micros () - usec;
		rtncod=errno;
		// dump
		printf ("addr:");
		for (idx=0; idx<16; idx++)
			printf (" +%x",idx);
		printf (" : 0123456789abcdef\n");
		errcnt=0;
		for (idx=0; idx<i2c_max_reg; idx++)
		{
			if (idx%16==0)
			{
				if (idx!=0)
					printf ("\n");
				printf ("%04x:",idx);
			}
			if (areg[idx]<0)
			{
				// ERROR:
				errcnt++;
				printf (" --");
			}
			else
			{
				// success:
				printf (" %02x",areg[idx]);
			}
			if ((idx + 1)%16==0)
			{
				int		idx2;
				int		chr;

				printf (" :");
				for (idx2=0; idx2<16; idx2++)
				{
					if (idx2%16==0)
						printf (" ");
					chr=areg[idx - 16 + 1 + idx2];
					if (chr==0x00)
						chr=' ';
					else if (chr<0x20 || 0x7e<chr)
						chr='.';
					printf ("%c",chr);
				}
			}
		}
		printf ("\n");
		printf ("DEBUG: access time=%d[us]/%d[byte] freq=%.3f[kHz]\n",
			usec,i2c_max_reg, 8000.*i2c_max_reg/usec);
		if (errcnt>0)
		{
			printf ("ERROR: read error count=%d\n",errcnt);
			errno=rtncod;
			perror (NULL);
		}
	}

	// close
	close (fi2c);

	return (rtncod);
}

