/*
 * 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 <string.h>
#include <target.h>
#include "../delay.h"

/*
 * Pins
 */
#define PIN_CSADC	0x00010000		// P0.16
#define PIN_MS3		0x00400000		// P0.22
#define PIN_CSFLASH	0x00800000		// P0.23
#define PIN_PORT	0xff000000		// P0.24.. 31

/*
 * SPImS (SPI made Simple) chip selects
 */
#define CSCAN		0x10			// SPI4

/*
 * CAN controller register offsets
 */
#define CAN_WRITE	0x02			// write       cmd
#define CAN_READ	0x03			// read        cmd
#define CAN_STAT	0xa0			// read status cmd
#define CAN_RESET	0xc0			// reset       cmd
#define CAN_CTRL	0x0f			// control
#define CAN_INTF	0x2c			// interrupt flag
#define CAN_CNF3	0x28			// configuration 3
#define CAN_TXB0CTRL	0x30			// Tx buffer 0 control
#define CAN_TXB0SIDH	0x31			//             standard identifier hi
#define CAN_TXB0DM	0x36			//             data field byte m
#define CAN_RXB0CTRL	0x60			// Rx buffer 0 control
#define CAN_RXB0SIDH	0x61			//             standard identifier hi
#define CAN_RXB0DM	0x66			//             data field byte m

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

static char line[100];				// line buffer
static unsigned char can_ib[16];		// CAN SPI in  buffer
static unsigned char can_ob[16];		// CAN SPI out buffer

static char const can_reset[] = {CAN_RESET			    };
static char const can_stat [] = {CAN_STAT , 0x00		    };
static char	  can_conf1[] = {CAN_WRITE, CAN_CTRL    , 0x00	    };
static char const can_conf2[] = {CAN_WRITE, CAN_RXB0CTRL, 0x60	    };
static char const can_send [] = {CAN_WRITE, CAN_TXB0CTRL, 0x0b	    };
static char const can_clear[] = {CAN_WRITE, CAN_INTF    , 0x00, 0x00};

/*
 * Select SPImS (SPI made Simple) device.
 */
static void
spims(int cs)
{
	Intern_ioclr = PIN_MS3|PIN_PORT;
	Intern_ioset = (unsigned long)~cs<<24;
	Intern_ioset = PIN_MS3;
}

/*
 * Initialize hardware.
 */
void
init(void)
{
	/*
	 * Define I/Os, set all outputs hi.
	 */
	Intern_ioset  = PIN_CSADC|PIN_MS3|PIN_CSFLASH|PIN_PORT;
	Intern_iodir |= PIN_CSADC|PIN_MS3|PIN_CSFLASH|PIN_PORT;

	spims(0);				// de-select SPImS

	Intern_spcr	= 0x20;			// MSTR
	Intern_spccr	= 32;			// SPI SCK =pclk/32
	Intern_pinsel0 |= 0x5500;		// enable SPI pins
	delay(10);
}

/*
 * SPI transfer
 */
static int
spi_xfer(int c)
{
	Intern_spdr = c;			// start transfer

	/*
	 * Wait for transfer complete or error
	 * (mask the reserved bits 0, 1 and 2).
	 */
	while (!(Intern_spsr&0xf8)) ;

	return Intern_spdr;
}

/*
 * Send n bytes from ib to the CAN controller
 * and write the received bytes to can_ob.
 */
static void
can(const char *ib, int n)
{
	char *p = can_ob;

	spims(CSCAN);				// select

	while (n--) *p++ = spi_xfer(*ib++);

	spims(0);				// de-select
}

/*
 * 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).
 *
 * Bit timing:
 *
 *  One bit is divided into 10 time quanta. One
 *  time quantum equals n times 100ns.
 *
 * Return non-zero on error.
 */
static int
can_init(int n)
{
	can(can_reset, sizeof(can_reset));
	delay(10);

	can_ib[0] = CAN_WRITE;			// write
	can_ib[1] = CAN_CNF3;			// addr
	can_ib[2] = 0x02;			// CNF3
	can_ib[3] = 0xa0;			// CNF2
	can_ib[4] = n? (n-1)&0x3f: 0;		// CNF1
	can(can_ib, 5);

	can_conf1[2] = n? 0: 0x40;
	can(can_conf1, sizeof(can_conf1));
	can(can_conf2, sizeof(can_conf2));

	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

	can(can_stat, sizeof(can_stat));	// read status
	if (!(can_ob[1]&1)) return 0;		// Rx buf empty!

	can_ib[0] = CAN_READ;			// read
	can_ib[1] = CAN_RXB0SIDH;		// msg
	can(can_ib, 7+8);			// header+data
	
	f.sid =  can_ob[2]<<3			// build
		|can_ob[3]>>5;
	f.dlc = (can_ob[6]&0xf)%9;
	memcpy(f.data, can_ob+7, 8);		//  Rx frame

	can(can_clear, sizeof(can_clear));	// clear int/err flags
	return &f;
}

/*
 * Transmit frame (if possible).
 *
 * Return ptr to received frame or 0.
 */
static int
can_tx(struct frame *fp)
{
	can(can_stat, sizeof(can_stat));	// read status
	if (can_ob[1]&4) return 1;		// Tx buf full!

	can_ib[0] = CAN_WRITE;			// write
	can_ib[1] = CAN_TXB0SIDH;		// addr
	can_ib[2] = fp->sid>>3;			// std ID hi
	can_ib[3] = fp->sid<<5;			//        lo
	can_ib[4] = 0x00;			// xtd ID hi
	can_ib[5] = 0x00;			//        lo

	can_ib[6] = fp->dlc;			// build
	memcpy(can_ib+7, fp->data, 8);
	can(can_ib, 7+8);			//  Tx frame

	can(can_send, sizeof(can_send));	// 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 BASE2\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(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)");
			}
		}
	}
}
