/*
 * Copyright(C) Paul und Scherer (mct.de/mct.net)
 *
 * This example demonstrates how to...
 *
 *  ... communicate via CAN.
 */

#include <ctype.h>
#include <sys/time.h>
#include <target.h>
#include "../../tty.h"

/*
 * CAN controller register offsets
 */
#define CAN_CR		 0			/* control */
#define CAN_CMR		 1			/* command */
#define CAN_SR		 2			/* status */
#define CAN_AM		 5			/* acceptance mask */
#define CAN_BTR0	 6			/* bus timing 0 */
#define CAN_BTR1	 7			/*            1 */
#define CAN_OC		 8			/* output control */
#define CAN_TXB		10			/* transmit buffer */
#define CAN_RXB		20			/* receive  buffer */
#define CAN_CDR		31			/* clock divider */

/*
 * Standard frame
 */
struct frame {
	int sid;				/* standard ID */
	int dlc;				/* data length code */
	unsigned char data[8];			/* data */
};

static char line[100];				/* line buffer */

/*
 * Initialize hardware.
 */
void
init(void)
{
	kbhit();				/* dummy call, to init stdin */
}

/*
 * Read from CAN controller register r.
 *
 * Return read data.
 */
static int
canr(int r)
{
	CAN_ALE = r;
	return CAN_DATA;
}

/*
 * Write data d to CAN controller register r.
 */
static void
canw(int r, int d)
{
	CAN_ALE  = r;
	CAN_DATA = d;
}

/*
 * Convert string to frame.
 *
 * Return fp.
 */
static struct frame *
s2f(struct frame *fp, char *s)
{
	int i, k;

	fp->sid = fp->dlc = 0, sscanf(s, "%3x%1d", &fp->sid, &fp->dlc);
	fp->sid &= 0x7ff;
	fp->dlc %= 9;
	for (i = 0; i < fp->dlc; fp->data[i++] = k) k = 0, sscanf(s+4+i*2, "%2x", &k);
	return fp;
}

/*
 * Convert frame to string.
 *
 * Return s.
 */
static char *
f2s(char *s, struct frame *fp)
{
	int i;

	sprintf(s, "%03x%d", fp->sid, fp->dlc);
	for (i = 0; i < fp->dlc; i++) sprintf(s+4+i*2, "%02x", fp->data[i]);
	return s;
}

/*
 * Initialize CAN (baud =1Mbps/n, 0 =loopback).
 *
 * Loopback is not supported.
 *
 * Note: The physical bus is required, i.e.
 *
 *  CANTxD0 and
 *  CANRxD0
 *
 * must be connected to a CAN transceiver.
 *
 * Bit timing:
 *
 *  One bit is divided into 12 time quanta. One
 *  time quantum equals n times 83.333... ns.
 *
 * Return non-zero on error.
 */
static int
can_init(int n)
{
	if (!n) return 1;
	canw(CAN_CR  , 0x01);			/* reset request */
	canw(CAN_AM  , 0xff);			/* acceptance mask =don't care */
	canw(CAN_BTR0, (n-1)&0x3f);		/* baudrate prescaler */
	canw(CAN_BTR1, 0x36);			/* bus timing */
	canw(CAN_OC  , 0x1a);			/*  and mode */
	canw(CAN_CDR , 0x40);			/* input comparator bypass */
	canw(CAN_CR  , 0x00);			/* clear RR (normal operation) */
	return 0;
}

/*
 * Get received frame (if available).
 *
 * Return ptr to received frame or 0.
 */
static struct frame *
can_rx(void)
{
	int i;

	static struct frame f;			/* Rx frame */

	if (!(canr(CAN_SR)&1)) return 0;	/* Rx buf empty! */

	f.sid =  canr(CAN_RXB  )<<3		/* build */
		|canr(CAN_RXB+1)>>5;
	f.dlc = (canr(CAN_RXB+1)&0xf)%9;	/*  Rx frame */

	/*
	 * data
	 */
	for (i = 0; i < 8; i++) f.data[i] = canr(CAN_RXB+2+i);

	canw(CAN_CMR, 4);			/* release rec buf */
	return &f;
}

/*
 * Transmit frame (if possible).
 *
 * Return non-zero for buf full.
 */
static int
can_tx(struct frame *fp)
{
	int i;

	if (!(canr(CAN_SR)&4)) return 1;	/* Tx buf full! */

	canw(CAN_TXB  , fp->sid>>3);		/* build */
	canw(CAN_TXB+1, fp->sid<<5|fp->dlc);	/*  Tx frame */

	/*
	 * data
	 */
	for (i = 0; i < 8; i++) canw(CAN_TXB+2+i, fp->data[i]);

	canw(CAN_CMR, 1);			/* send it */
	return 0;
}

static void
showframe(struct frame *fp, char *type, char *mode)
{
	printf("%s: %s %s\n", type, f2s(line, fp), mode);
}

static void
usage(void)
{
	puts("\n"
	     "Simple CAN Monitor for MEGA332\n"
	     "==============================\n"
	     " 0.. 9 : 1Mbps/n (0 =loopback)\n"
	     " t[x]  : transmit [iiildd... ]\n"
	     " Enter : repeat last Tx frame\n"
	     " Space : same as Enter\n"
	     " r[n]  : repeat [every n*10ms]\n"
	     " E/e   : echo on/off\n"
	);
}

/*
 * Simple CAN Monitor
 *
 * "Simple" means
 *
 *  - only 11Bit standard frames
 *  - only one Tx buffer is used
 *  - no acceptance filtering
 *  - no error handling
 *  - no interrupts.
 *
 * Frames are read and displayed as strings of the form
 *
 *  "iiildd... "
 *
 *  where iii is the SID (Standard ID)      range 0... 7ff
 *          l is the DLC (Data Length Code) range 0... 8
 *         dd is a data byte                range 0... ff
 */
int
main(void)
{
	long repeat = 0;
	long rate   = 0;
	long echo   = 0;
	long loop   = 0;

	init();

	usage();
	ungetc('1', stdin);			/* init with 1Mbps */
	while (1) {
		static struct frame f;		/* Tx frame */

		struct frame *fp;		/* Rx frame ptr */
		int c;				/* command */

		_delay(10000);
		if (kbhit()) switch (c = getch()) {
		default:
			if (isdigit(c)) {
				if (can_init(c-'0')) puts("Not supported!");
				else loop = 0, printf("Baud: %dkb/s\n", 1000/(c-'0'));
			} else usage();
			break;

		case '0':
			if (can_init(0)) puts("Not supported!");
			else loop = 1,	 puts("Loopback mode!");
			break;

		case 't':
		case 'r':
			putchar(c);
			fgets(line, sizeof(line), stdin);
			if (c == 't') s2f(&f, line);
			else rate = 0, sscanf(line, "%ld", &rate);
		case '\r':
		case ' ':
			repeat = 1;
			break;

		case 'E':
		case 'e':
			printf("Echo is %s.\n", (echo = isupper(c))? "on": "off");
			break;
		}

		/*
		 * Check repeat!
		 */
		if (repeat && !--repeat) {
			repeat = rate;
			if (can_tx(&f)) puts("Transmit failed (transmitter busy).");
			else showframe(&f, "Tx", "");
		}

		/*
		 * Check receiver!
		 */
		if ((fp = can_rx())) {
			showframe(fp, "Rx", loop? "(loopback)": "");
			if (echo) {
				if (can_tx(fp)) puts("Echo failed (transmitter busy).");
				else showframe(fp, "Tx", "(echo)");
			}
		}
	}
}

