/*
 * Copyright(C) Paul und Scherer (mct.de/mct.net)
 *
 * This example demonstrates how to...
 *
 *  ... read and set the external RTC.
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "../iic.h"

#define RTC	0x9e				// RTC IIC bus address

/*
 * Buffer for access clock cmd, addr 0
 * and clock data.
 */
static unsigned char clk[2+7] = {0xc0, 0};

/*
 * Return binary of bcd
 */
static int
bcd2bin(int bcd)
{
	return (bcd>>4)*10+(bcd&0xf);
}

/*
 * Return binary coded decimal of bin
 */
static int
bin2bcd(int bin)
{
	return (bin/10<<4)|(bin%10);
}

/*
 * Read RTC to tp, compare with cp and return 0 if equal.
 */
static int
getrtc(struct tm *tp, struct tm *cp)
{
	iic_write(RTC  , clk  ,		    2);	// read
	iic_read (RTC|1, clk+2, sizeof(clk)-2);	//  counters

	tp->tm_sec  = bcd2bin(clk[2]);
	tp->tm_min  = bcd2bin(clk[3]);
	tp->tm_hour = bcd2bin(clk[4]);
	tp->tm_mday = bcd2bin(clk[6]);
	tp->tm_mon  = bcd2bin(clk[7]);
	tp->tm_year = bcd2bin(clk[8]);

	return tp->tm_sec  != cp->tm_sec
	    || tp->tm_min  != cp->tm_min
	    || tp->tm_hour != cp->tm_hour
	    || tp->tm_mday != cp->tm_mday
	    || tp->tm_mon  != cp->tm_mon
	    || tp->tm_year != cp->tm_year;
}

/*
 * Initialize IIC bus interface, and probe for RTC.
 */
static void
probe(void)
{
	static int done;			// skip if already done

	if (!done) {
		iic_init();
		if (iic_write(RTC, 0, 0)) puts("RTC not found!"), abort();
		done = 1;
	}
}

/*
 * Return date (the alarm flag is ignored!)
 * in seconds since 00:00:00, Jan. 1, 1970.
 */
time_t
_gettime(int alarm)
{
	struct tm tm1, tm2;

	probe();				// RTC functional?

	/*
	 * Read until two successive reads are equal.
	 */
	while (getrtc(&tm1, &tm2) && getrtc(&tm2, &tm1)) ;

	/*
	 *  tm_year  : 1970.. 2069  (year-1900)
	 *  tm_mon   :    0.. 11
	 *  tm_isdst : No daylight saving time
	 */
	if (tm1.tm_year < 70) tm1.tm_year += 100;
	tm1.tm_mon--;
	tm1.tm_isdst = 0;

	return mktime(&tm1);
}

/*
 * Set date (the alarm flag is ignored!) to t
 * (t = seconds since 00:00:00, Jan. 1, 1970).
 */
void
_settime(time_t t, int alarm)
{
	struct tm *tp = localtime(&t);

	probe();				// RTC functional?

	clk[2] = bin2bcd(tp->tm_sec);		// set counters
	clk[3] = bin2bcd(tp->tm_min);
	clk[4] = bin2bcd(tp->tm_hour);
	clk[5] = bin2bcd(tp->tm_wday);
	clk[6] = bin2bcd(tp->tm_mday);
	clk[7] = bin2bcd(tp->tm_mon+1);
	clk[8] = bin2bcd(tp->tm_year < 100? tp->tm_year: tp->tm_year-100);

	iic_write(RTC, clk, sizeof(clk));	// write counters
}

/*
 * The current date is displayed. When a new date is entered,
 * the current date is modified and written back to the RTC.
 *
 * Note: Reading from and writing to the RTC is done without
 * checking return values. Both functions probe once for the
 * RTC and if found, assume proper operation.
 *
 * If you use these functions in your own program, you could
 * extract the RTC probing to your global init section.
 */
int
main(void)
{
	while (1) {
		char line[30];			// input buffer
		time_t t = time(0);		// current date

		printf("Current date: %s\n"
		       "New date (hh mm ss DD MM YYYY, no check!): ",
			ctime(&t)
		);
		if (*fgets(line, sizeof(line), stdin) != '\n') {
			struct tm *tp = localtime(&t);

			tp->tm_mon++;			// correct month
			tp->tm_year += 1900;		//         year
			sscanf(line, "%d%d%d%d%d%d",	// scan line
				&tp->tm_hour,
				&tp->tm_min,
				&tp->tm_sec,
				&tp->tm_mday,
				&tp->tm_mon,
				&tp->tm_year
			);
			tp->tm_mon--;			// tm_mon  : 0.. 11
			tp->tm_year -= 1900;		// tm_year : years since 1900
			_settime(mktime(tp), 0);	// set new date
		}
	}
}

