/*
 * Radiocode Clocks HOPF Funkuhr 6021 mit serieller Schnittstelle
 * base code version from 24th Nov 1995 - history at end
 *
 * Created by F.Schnekenbuehl <frank@comsys.dofn.de> from clk_rcc8000.c
 * Nortel DASA Network Systems GmbH, Department: ND250
 * A Joint venture of Daimler-Benz Aerospace and Nortel
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

#include "config.h"
#include "ntp_fp.h"
#include "ntp_calendar.h"
#include "ascii.h"

#include "parse.h"

#include "ntp_stdlib.h"
#include <stdio.h>

/*
 * hopf Funkuhr 6021
 *      used with 9600,8N1,
 *      UTC ueber serielle Schnittstelle
 *      Sekundenvorlauf ON
 *      ETX zum Sekundenvorlauf ON
 *      Datenstring 6021
 *      Ausgabe Uhrzeit und Datum
 *      Senden mit Steuerzeichen
 *      Senden sekuendlich
 */

/*
 *  Type 6021 Serial Output format
 *
 *      000000000011111111 / char
 *      012345678901234567 \ position
 *      sABHHMMSSDDMMYYnre  Actual
 *       C4110046231195     Parse
 *      s              enr  Check
 *
 *  s = STX (0x02), e = ETX (0x03)
 *  n = NL  (0x0A), r = CR  (0x0D)
 *
 *  A B - Status and weekday
 *
 *  A - Status
 *
 *      8 4 2 1
 *      x x x 0  - no announcement
 *      x x x 1  - Summertime - wintertime - summertime announcement
 *      x x 0 x  - Wintertime
 *      x x 1 x  - Summertime
 *      0 0 x x  - Time/Date invalid
 *      0 1 x x  - Internal clock used
 *      1 0 x x  - Radio clock
 *      1 1 x x  - Radio clock highprecision
 *
 *  B - 8 4 2 1
 *      0 x x x  - MESZ/MEZ
 *      1 x x x  - UTC
 *      x 0 0 1  - Monday
 *      x 0 1 0  - Tuesday
 *      x 0 1 1  - Wednesday
 *      x 1 0 0  - Thursday
 *      x 1 0 1  - Friday
 *      x 1 1 0  - Saturday
 *      x 1 1 1  - Sunday
 */

#define HOPF_DSTWARN	0x01	/* DST switch warning */
#define HOPF_DST	0x02	/* DST in effect */

#define HOPF_MODE	0x0C	/* operation mode mask */
#define  HOPF_INVALID	0x00	/* no time code available */
#define  HOPF_INTERNAL	0x04	/* internal clock */
#define  HOPF_RADIO	0x08	/* radio clock */
#define  HOPF_RADIOHP	0x0C	/* high precision radio clock */

#define HOPF_UTC	0x08	/* time code in UTC */
/* #define HOPF_WMASK	0x07	* mask for weekday code UNUSED */

static struct format hopf6021_fmt =
{
	{
		{  9, 2 }, {11, 2}, { 13, 2}, /* Day, Month, Year */
		{  3, 2 }, { 5, 2}, {  7, 2}, /* Hour, Minute, Second */
		{  2, 1 }, { 1, 1}, {  0, 0}, /* Weekday, Flags, Zone */
		/* ... */
	},
	(const unsigned char *)"\002              \n\r\003",
	0
};

#define OFFS(x) format->field_offsets[(x)].offset
#define STOI(x, y) Stoi(&buffer[OFFS(x)], y, format->field_offsets[(x)].length)
#define hexval(x) (('0' <= (x) && (x) <= '9') ? (x) - '0' : \
		   ('a' <= (x) && (x) <= 'f') ? (x) - 'a' + 10 : \
		   ('A' <= (x) && (x) <= 'F') ? (x) - 'A' + 10 : \
		   -1)

static parse_cvt_fnc_t cvt_hopf6021;
static parse_inp_fnc_t inp_hopf6021;

clockformat_t clock_hopf6021 =
{
  inp_hopf6021,			/* HOPF 6021 input handling */
  cvt_hopf6021,                 /* Radiocode clock conversion */
  0,				/* no direct PPS monitoring */
  (void *)&hopf6021_fmt,        /* conversion configuration */
  "hopf Funkuhr 6021",          /* clock format name */
  19,                           /* string buffer */
  0                            /* private data length, no private data */
};

/* parse_cvt_fnc_t cvt_hopf6021 */
static unsigned long
cvt_hopf6021(
	     unsigned char *buffer,
	     int            size,
	     struct format *format,
	     clocktime_t   *clock_time,
	     void          *local
	     )
{
	unsigned char status,weekday;

	UNUSED_ARG(size);
	UNUSED_ARG(local);

	if (!Strok(buffer, format->fixed_string))
	{
		return CVT_NONE;
	}

	if (  STOI(O_DAY,   &clock_time->day)    ||
	      STOI(O_MONTH, &clock_time->month)  ||
	      STOI(O_YEAR,  &clock_time->year)   ||
	      STOI(O_HOUR,  &clock_time->hour)   ||
	      STOI(O_MIN,   &clock_time->minute) ||
	      STOI(O_SEC,   &clock_time->second)
	      )
	{
		return CVT_FAIL|CVT_BADFMT;
	}

	clock_time->usecond   = 0;
	clock_time->utcoffset = 0;

	status = (uint8_t) hexval(buffer[OFFS(O_FLAGS)]);
	weekday= (uint8_t) hexval(buffer[OFFS(O_WDAY)]);

	if ((status == 0xFF) || (weekday == 0xFF))
	{
		return CVT_FAIL|CVT_BADFMT;
	}

	clock_time->flags  = 0;

	if (weekday & HOPF_UTC)
	{
		clock_time->flags |= PARSEB_UTC;
	}
	else
	{
		if (status & HOPF_DST)
		{
			clock_time->flags     |= PARSEB_DST;
			clock_time->utcoffset  = -2*60*60; /* MET DST */
		}
		else
		{
			clock_time->utcoffset  = -1*60*60; /* MET */
		}
	}

	clock_time->flags |= (status & HOPF_DSTWARN)  ? PARSEB_ANNOUNCE : 0;

	switch (status & HOPF_MODE)
	{
	    case HOPF_INVALID:  /* Time/Date invalid */
		clock_time->flags |= PARSEB_POWERUP;
		break;

	    case HOPF_INTERNAL: /* internal clock */
		clock_time->flags |= PARSEB_NOSYNC;
		break;

	    case HOPF_RADIO:    /* Radio clock */
	    case HOPF_RADIOHP:  /* Radio clock high precision */
		break;

	    default:
		return CVT_FAIL|CVT_BADFMT;
	}

	return CVT_OK;
}

/*
 * parse_inp_fnc_t inp_hopf6021
 *
 * grab data from input stream
 */
static unsigned long
inp_hopf6021(
	     parse_t      *parseio,
	     char         ch,
	     timestamp_t  *tstamp
	  )
{
	unsigned int rtc;

	parseprintf(DD_PARSE, ("inp_hopf6021(0x%lx, 0x%x, ...)\n",
                    (unsigned long)parseio, (unsigned)ch));

	switch (ch)
	{
	case ETX:
		parseprintf(DD_PARSE, ("inp_hopf6021: EOL seen\n"));
		parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */
		if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP) {
			return parse_end(parseio);
		} else {
			return rtc;
		}
	default:
		return parse_addchar(parseio, ch);
	}
}

/*
 * History:
 *
 * clk_hopf6021.c,v
 * Revision 4.10  2004/11/14 15:29:41  kardel
 * support PPSAPI, upgrade Copyright to Berkeley style
 *
 * Revision 4.7  1999/11/28 09:13:49  kardel
 * RECON_4_0_98F
 *
 * Revision 4.6  1998/11/15 20:27:57  kardel
 * Release 4.0.73e13 reconciliation
 *
 * Revision 4.5  1998/06/14 21:09:35  kardel
 * Sun acc cleanup
 *
 * Revision 4.4  1998/06/13 12:02:38  kardel
 * fix SYSV clock name clash
 *
 * Revision 4.3  1998/06/12 15:22:27  kardel
 * fix prototypes
 *
 * Revision 4.2  1998/06/12 09:13:25  kardel
 * conditional compile macros fixed
 * printf prototype
 *
 * Revision 4.1  1998/05/24 09:39:52  kardel
 * implementation of the new IO handling model
 *
 * Revision 4.0  1998/04/10 19:45:29  kardel
 * Start 4.0 release version numbering
 *
 * from V3 3.6 log info deleted 1998/04/11 kardel
 */
