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

#include <stdio.h>
#include <conio.h>
#include <ctype.h>
#include <target.h>
#include "../delay.h"

#define CC	(&INTERN_C1)			// select CAN1/2
#define CC_PINS	0x40000				// CAN1 =0x40000
						// CAN2 =0x14000
/*
 * Standard frame
 */
struct frame {
	int sid;				// standard ID
	int dlc;				// data length code
	unsigned char data[8];			// data
};

static char line[100];				// line buffer

/*
 * 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).
 *
 * Note: Loopback is done via a "self reception
 * request" in self test mode. So, The physical
 * bus is required, even for loopback, i.e.
 *
 *  TDx and RDx
 *
 * must be connected to a CAN transceiver.
 *
 * Bit timing:
 *
 *  One bit is divided into 10 time quanta. For
 *  a baudrate of 1Mbps (equals 1us/b) one time
 *  quantum must be 100ns (=10MHz CAN clock).
 *
 * Return non-zero on error.
 */
static int
can_init(int n)
{
	CC->mod = 1;				// reset mode
	CC->btr = _PCLK/10000000;		// 10MHz CAN clock
	if (n) CC->btr *= n;			//  divided by n
	else   CC->mod |= 4;			// self test mode
	CC->btr--;				// prescaler -1 required
	CC->btr |= 0x430000;			// bit timing (10tq)
	CC->mod &= ~1;				// clear RM (normal mode)
	Intern_afmr = 2;			// acceptance filter bypass
	Intern_pinsel1 |= CC_PINS;		// enable CANx pins
	return 0;
}

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

	if (!(CC->sr&1)) return 0;		// Rx buf empty!

	f.sid =  CC->rid&0x7ff;			// build
	f.dlc = (CC->rfs>>16&0xf)%9;		//  Rx frame

	/*
	 * data
	 */
	f.data[0] = CC->rda    ;
	f.data[1] = CC->rda>> 8;
	f.data[2] = CC->rda>>16;
	f.data[3] = CC->rda>>24;
	f.data[4] = CC->rdb    ;
	f.data[5] = CC->rdb>> 8;
	f.data[6] = CC->rdb>>16;
	f.data[7] = CC->rdb>>24;

	CC->cmr = 4;				// release rec buf
	return &f;
}

/*
 * Transmit frame (if possible).
 *
 * Return non-zero for buf full.
 */
static int
can_tx(struct frame *fp)
{
	if (!(CC->sr&4)) return 1;		// Tx buf full!

	CC->tid1 = fp->sid;			// build
	CC->tfi1 = fp->dlc<<16;			//  Tx frame

	/*
	 * data
	 */
	CC->tda1 = fp->data[0]|fp->data[1]<<8|fp->data[2]<<16|fp->data[3]<<24;
	CC->tdb1 = fp->data[4]|fp->data[5]<<8|fp->data[6]<<16|fp->data[7]<<24;

	CC->cmr = CC->mod&4? 0x30		// send with self reception request
			   : 0x21;		// send normal
	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 LC2194\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;

	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(10);
		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)");
			}
		}
	}
}
