/* VSRV VSOS3 ethernet audio input driver */

#include <vo_stdio.h>
#include <volink.h>
#include <apploader.h>
#include <string.h>
#include <mutex.h>
#include <kernel.h>
#include <exec.h>
#include <taskandstack.h>
#include <timers.h>
#include <imem.h>
#include <consolestate.h>
#include <sysmemory.h>
#include "audiofile.h"

/* CONFIGURATION */

/* Enables monitoring and status printing features. */
#define ENABLE_MONITOR_MAIN
/* Copy audio inside interrupt */
#define COPY_AUDIO_INTERRUPT
/* Rest of program */
#define SIG_F_ETHERNET (1 << 14)

FILE *stdaudioin_orig = NULL;
u_int16 audioMutex;
struct TaskAndStack *taskAndStack = NULL;
void (*oldIsr)(void) = NULL;
u_int32 oldIntVector;
u_int16 ethRun = 1;


u_int32 ReadMasterClockCounter();
u_int32 ReadMasterClockCounterInterrupt();
struct metaHeader {
	u_int16 dummy0;	/* -2 */
	u_int16 dummy1;	/* -1 */
        u_int16 bytes;	/* 0 */
        u_int16 flags;	/* 1 */
        u_int32 ts;	/* 2 */
        u_int16 ipSrc[2];	/* 4 */
        u_int16 ipDst[2];	/* 6 */
        u_int16 udpSrc;	/* 8 */
        u_int16 udpDst;/* 9 */
        u_int16 rtpOffset; /* 10 */
        u_int16 rtpSequence; /* 11 */
        u_int32 rtpTS; /* 12 */
        u_int32 rtpSynSrc;
        u_int16 rtpPad;
        u_int16 udpSize; /* 18 */
        u_int16 rx_filter;
        u_int16 rx_modify;
        u_int16 rx_vlan;
        u_int16 rx_format; /* 24 */
        u_int16 rx_mcast[4];
} h;
union headerRead {
	struct metaHeader h;
	u_int16 size[32 + 2];
}header;
char headerFmt[] =
	"\nbytes %u\tflags %x\tts %lu\n"
	"rtpOffset %x\trtpSequence %u\trtpTS %lu\n"
        "udpSize %u\n"
        "rx_filter %x\trx_modify %x\trx_format %x\n"; /* 24 */
struct ethStats {
	u_int32 nok;
	u_int32 rof;  /* Receiver overflow */
	u_int32 it;   /* Interrupt time */
	u_int32 i2hd; /* Interrupt to handler delay */
	u_int32 ct;   /* Copy time */
	u_int16 lf;   /* last flags */
	u_int16 md[3]; /* Mac dest */
} stats, s;
u_int32 intcnt;

#ifdef COPY_AUDIO_INTERRUPT
void IntEthC(void) {
	u_int16 words;
	u_int16 cf = PERIP(VSRV1_CF);
	stats.it = ReadMasterClockCounterInterrupt();
	intcnt++;
	while (cf & VSRV1_CF_ETHRDST) {
		__mem_y u_int16 *eth = (__mem_y u_int16*)(ETHRAM_START);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		PERIP(0);
		memcpyYX(&header, eth, sizeof(header));
		eth += sizeof(header);
		words = ((header.h.bytes + (1 - 4)) >> 1);
		if (words > 780) { /* 1520 bytes */
			words = 780;
		}
		if (header.h.flags != 0x001f) {
			stats.lf = header.h.flags;
			stats.nok++;
		} else {
			u_int16 steps = (ETHRAM_START + 2 /* Dummies */) + header.h.rtpOffset - ((u_int16)eth);
			/* Bit depth */
			if (header.h.rx_format & (1 << 1)) {
				audioIn.flags |= AIN_32BIT;
			} else {
				audioIn.flags &= ~AIN_32BIT;
			}
			words -= steps;
			stats.ct = ReadMasterClockCounter();
			AudioInsert(eth, steps, words);
			stats.ct = ReadMasterClockCounter() - stats.ct;
		}
		PERIP(VSRV1_CF) = cf;
		if (cf & VSRV1_CF_ETHOVRRUN) {
			stats.rof++;
		}
		cf = PERIP(VSRV1_CF);
	}
	stats.i2hd = ReadMasterClockCounter() - stats.it;
}
#else
u_int16 packetBuf[800];
volatile u_int16 packetDump = 0;
u_int16 stupidDump = 0;

void printPkt() {
	u_int16 *p =packetBuf, i, pl, pos = 0;
	printf(headerFmt,
	       h.bytes, h.flags, h.ts,
	       h.rtpOffset, h.rtpSequence, h.rtpTS,
	       h.udpSize,
	       h.rx_filter, h.rx_modify, h.rx_format);
	h.rtpOffset -= 32;
	printf("\n%03x %c: ", pos, 'E');
	for (i = 0; i < 3; i++) {
		printf("%04x ", *p++);
		pos++;
	}
	printf (" <- ");
	for (i = 0; i < 3; i++) {
		printf("%04x ", *p++);
		pos++;
	}
	printf("\n%03x %c: ", pos, 'E');
	printf("%04x ", *p++);
		pos++;
	printf("\n%03x %c: ", pos, 'I');
	for (i = 0; i < 6; i++) {
		printf("%04x ", *p++);
		pos++;
	}
	printf("|%04x ", *p++);
	printf("%04x| -> ", *p++);
	printf("|%04x ", *p++);
	printf("%04x|", *p++);
	pos += 4;
	printf("\n%03x %c: ", pos, 'U');
	pl = 0;
	for (i = 0; i < 4; i++) {
		if (i == 2) {
			pl += *p;
		}
		if (i == 1) {
			printf("-> ");
		}
		printf("%04x ", *p++);
		pos++;
		pl -= 2;
	}
	if (pl > 1500) {
		printf("\nBAD %u\n", pl);
		pl = 1500;
	}
	printf("\n%03x %c: ", pos, 'R');
	for (i = 0; i < 6; i++) {
		printf("%04x ", *p++);
		pos++;
		pl -= 2;
	}
	printf("\n%03x %u\n%c: ", pos, pl, 'P');
	pl = ((pl + 1) >> 1);
	for (i = 0; i < pl; i++) {
		if ((i & 0x7) == 0) {
			printf("\n%03x %c: ", pos, ' ');
		}
		if (h.rtpOffset == pos) {
			printf("(%04x) ", *p++);
		} else {
			printf("%04x ", *p++);
		}
		pos++;
	}
	printf("\n*-- IC: %10lu NOK: %10lu RO: %10lu hd: %10lu Ct: %lu --*",
	       intcnt, s.nok, s.rof, s.i2hd, s.ct);
}
void IntEthC(void) {
	intcnt++;
	stats.it = ReadMasterClockCounterInterrupt();
	Signal(&(taskAndStack->task), SIG_F_ETHERNET);
}
void EthISR(void) {
	stats.it = ReadMasterClockCounterInterrupt();
	Signal(&(taskAndStack->task), SIG_F_ETHERNET);
}
void EthCopyAudioLoop(void) {
	extern __mem_y u_int16 ethMemArea;
	printf("%p %p\n", &ethMemArea, ethMemArea);

	if (AllocMemAbsY(0x6400, 0x400)) {
		printf("Not allocated!\n");
	}

	while (ethRun) {
#if 0
		printf(".");
		Delay(2000);
#else
		u_int16 cf;
		//Wait(SIG_F_ETHERNET);

		Delay(1);
		cf = PERIP(VSRV1_CF);
		while (cf & VSRV1_CF_ETHRDST) {
			__mem_y u_int16 *eth = (__mem_y u_int16*)(ETHRAM_START);//ethMemArea;/* ETHRAM_START */
			u_int16 words, *dest = (u_int16 *)&header;
			if (stupidDump) {
				u_int16 i, src = ETHRAM_START;
#if 1
				memcpyYX(packetBuf, eth, 780);
#else
				dest = packetBuf;
				for (i = 0; i < 780; i++) {
					*dest++ = PERIP(src++);
				}
#endif
				dest = packetBuf;
				for (i = 0; i < 780; i++) {
					if ((i & 0xf) == 0) {
						printf("\n%04x: ", i);
					}
					printf("%04x ", *dest++);
				}
				printf("\n\n");
				goto finally;
			}
			stats.i2hd = ReadMasterClockCounter() - stats.it;
			memcpyYX(&header, eth, sizeof(header));
			eth += sizeof(header);
			words = ((header.h.bytes + (1 - 4)) >> 1);
			if (words > 780) { /* 1520 bytes */
				words = 780;
			}
			if (packetDump) {
				u_int32 start, end;
				h = header.h;
				dest = packetBuf;
				printf("eth: %p\n", eth);
				start = ReadMasterClockCounter();
				memcpyYX(packetBuf, eth, words);
				end = ReadMasterClockCounter();
				stats.ct = end - start;
				memcpy(&s, &stats, sizeof(s));
				packetDump = 0;
			}
			if (header.h.flags != 0x001f) {
				stats.lf = header.h.flags;
				stats.nok++;
			} else {
				u_int16 steps = (ETHRAM_START + 2 /* Dummies */) + header.h.rtpOffset - ((u_int16)eth);
				/* Bit depth */
				if (header.h.rx_format & (1 << 1)) {
					audioIn.flags |= AIN_32BIT;
				} else {
					audioIn.flags &= ~AIN_32BIT;
				}
				words -= steps;
				stats.ct = ReadMasterClockCounter();
				AudioInsert(eth, steps, words);
				stats.ct = ReadMasterClockCounter() - stats.ct;
			}
		finally:
			PERIP(VSRV1_CF) = cf;
			if (cf & VSRV1_CF_ETHOVRRUN) {
				stats.rof++;
			}
			cf = PERIP(VSRV1_CF);
		}
#endif
	}
}
#endif

DLLENTRY(init)
ioresult init(char *parameters) {
	extern u_int32 intOS;
	extern u_int16 IntEthVector;
	int nParam = RunProgram("ParamSpl", parameters);
	char *p = parameters;
	int i;
	u_int32 intvect = 0x2a00000e + (((u_int32)&intOS) << 6);
	InitMutex(&audioMutex);
	stdaudioin_orig = stdaudioin;

	for (i=0; i<nParam; i++) {
		if (!strcmp(p, "S") || !strcmp(p, "s")) {
			if (stdaudioin != ((FILE *)&audioFile)) {
				/* Getting mutex always succeeds because no-one else has had a chance
				   to grab hold of it yet. So we don't check the result. */
				AttemptMutex(&audioMutex);
				stdaudioin = (FILE*)&audioFile;
			}
		}
		p += strlen(p)+1;
	}
	/*
	for (i = 0; i < 32; i++) {
		printf("%x %lx\n", i+0x20, ReadIMem(0x20 +i));
	}
	*/
	printf("%lx %lx\n", intvect, ReadIMem(0x20 + INTV_TIMER0));
	oldIntVector = ReadIMem(0x20 + INTV_ETH);
	audioFile.op->Ioctl((FILE *)&audioFile, IOCTL_RESTART, 0);
#ifndef COPY_AUDIO_INTERRUPT
	oldIsr = AddIntServer(INTV_ETH, EthISR);
	taskAndStack = CreateTaskAndStack(EthCopyAudioLoop, "EthCopy", 256, 2);
#endif

#if 1
	WriteIMem((0x20+INTV_ETH), ReadIMem((u_int16)(&IntEthVector)));
#else
	WriteIMem(0x20 + INTV_ETH, intvect);
#endif
	PERIP(VSRV1_CF) |= VSRV1_CF_ETHRDENA;
        PERIP(CF_SYSTEM1) |= CF_SYSTEM1_YETH_SEL;
	PERIP(INT_ENABLE1_LP) |= INTF1_ETH;

	printf("InitEnd\n");
}

DLLENTRY(fini)
void fini(void) {
	// Add code here to force release of resources such as
	// memory allocated with malloc, entry points,
	// hardware locks or interrupt handler vectors.

	stdaudioin = stdaudioin_orig;
	/* Stop interrupt */
	PERIP(INT_ENABLE1_HP) &= ~(INTF1_ETH);
	PERIP(INT_ENABLE1_LP) &= ~(INTF1_ETH);

	WriteIMem(0x20 + INTV_ETH, oldIntVector);
#ifndef COPY_AUDIO_INTERRUPT
	AddIntServer(INTV_ETH, oldIsr);

	ethRun = 0;
	if (taskAndStack) {
		while (taskAndStack->task.tc_State &&
		       taskAndStack->task.tc_State != TS_REMOVED) {
			EthISR(); /* Wakes up the task */
			Delay(TICKS_PER_SEC/100);
		}
		FreeTaskAndStack(taskAndStack);
	}
#endif
	PERIP(VSRV1_CF) &= ~VSRV1_CF_ETHRDENA;
	PERIP(CF_SYSTEM1) &= ~CF_SYSTEM1_YETH_SEL;

	printf("FiniEnd\n");
}
DLLENTRY(OpenAudioFile)
FILE *OpenAudioFile(const char *mode) {
	if (AttemptMutex(&audioMutex)) {
		return NULL;
	}
	return (FILE *)&audioFile;
}
DLLENTRY(CloseAudioFile)
ioresult CloseAudioFile(FILE *audioFP) {
	ReleaseMutex(&audioMutex);
}
#ifdef  ENABLE_MONITOR_MAIN
ioresult main(char *parameters) {
	int nParam = RunProgram("ParamSpl", parameters);
	char *p = parameters;
	int i, loop = 0, packets = 0, dumpFifo = 0;
	FILE *lf = NULL, *so;
	for (i=0; i<nParam; i++) {
		if (!strcmp(p, "-h")) {
			printf("auieth [-h][-d][-i][-l][-m][-p][-v][-w]\n"
			       "-h\tThis help\n"
			       "-d\tDump last packet header\n"
#ifndef COPY_AUDIO_INTERRUPT
			       "-i\tMost idiotic dump\n"
#endif
			       "-l\tDump audio data from buffer as it is fed to there\n"
			       "-m\tMonitor auieth processing\n"
#ifndef COPY_AUDIO_INTERRUPT
			       "-p\tDump packets\n"
#endif
			       "-r\tRaw header dump\n"
			       "-v\tPrint stats once\n"
			       "-w\tWrite output to pckts.txt\n");
			return S_OK;
		} else if (!strcmp(p, "-d")) {
			Disable();
			h = header.h;
			Enable();
			printf(headerFmt,
			       h.bytes, h.ts, h.rtpOffset, h.rtpSequence, h.rtpTS,
			       h.udpSize, h.rx_filter, h.rx_modify, h.rx_format);
#ifndef COPY_AUDIO_INTERRUPT
		} else if (!strcmp(p, "-i")) {
			stupidDump = 1;
#endif
		} else if (!strcmp(p, "-l")) {
			dumpFifo = 1;
		} else if (!strcmp(p, "-m")) {
			loop = -1;
#ifndef COPY_AUDIO_INTERRUPT
		} else if (!strcmp(p, "-p")) {
			packets = 1;
#endif
		} else if (!strcmp(p, "-r")) {
			u_int32 lastIc = -1;
			while ((appFlags & APP_FLAG_QUIT) == 0) {
				if (intcnt != lastIc) {
					u_int16 k, *hh = (u_int16*)&h;
					Disable();
					h = header.h;
					lastIc = intcnt;
					Enable();
					for (k = 0; k < sizeof(h);k++) {
						printf("%c%04x", (k&0xf)?' ':'\n', *hh++);
					}
					printf("\n");
				}
			}
			appFlags &= ~APP_FLAG_QUIT;

			printf(headerFmt,
			       h.bytes, h.ts, h.rtpOffset, h.rtpSequence, h.rtpTS,
			       h.udpSize, h.rx_filter, h.rx_modify, h.rx_format);
		} else if (!strcmp(p, "-v")) {
			loop = 1;
		} else if (!strcmp(p, "-w")) {
			if (lf == NULL) {
				lf = fopen ("s:pckts.txt", "wb");
				if (lf) {
					so = stdout;
					stdout = lf;
				}
			}
		} else if (!strcmp(p, "S") || !strcmp(p, "s")) {
			// Init took care of this.
		} else {
			printf("Unknown option\n");
		}
		p += strlen(p)+1;
	}
#ifndef COPY_AUDIO_INTERRUPT
	if (stupidDump) {
		while ((appFlags & APP_FLAG_QUIT) == 0) {
			;
		}
	}
	stupidDump = 0;
	if (packets) {
		while ((appFlags & APP_FLAG_QUIT) == 0) {
			packetDump = 1;
			while (packetDump && ((appFlags & APP_FLAG_QUIT) == 0)) {
				Delay(1);
			}
			if (!packetDump) {
				printPkt();
			}
		}
		appFlags &= ~APP_FLAG_QUIT;
	}
#endif
	if (dumpFifo) {
		static u_int16 ab[4];
		u_int16 odd = 0;
		while ((appFlags & APP_FLAG_QUIT) == 0) {
			if (AudioInFill()) {
				fread(ab, 2, 2, &audioFile);
				printf("%04x %04x %04x %04x%c", ab[0], ab[1], ab[2], ab[3], odd?'\n':' ');
				odd ^= 1;
			}
		}
		appFlags &= ~APP_FLAG_QUIT;
	}
	while (((appFlags & APP_FLAG_QUIT) == 0) && loop) {
		u_int32 sc, of;
		Disable();
		memcpy(&s, &stats, sizeof(s));
		Enable();
		audioFile.op->Ioctl((FILE *)&audioFile,
				    IOCTL_AUDIO_GET_SAMPLE_COUNTER,
				    (IOCTL_ARGUMENT)&sc);
		audioFile.op->Ioctl((FILE *)&audioFile,
				    IOCTL_AUDIO_GET_OVERFLOWS,
				    (IOCTL_ARGUMENT)&of);
		printf("\rIC: %lu S: %10lu Bad: %10lu LE: %04x ROF: %10lu OF: %10lu IH: %10lu CT: %10lu F: %4u",
		       intcnt, sc, s.nok, s.lf, s.rof, of, s.i2hd, s.ct, AudioInFill());
		if (loop > 0) {
			loop--;
		}
		Delay(11);/* Let UART flush*/
	}
	printf("\n");
	if (lf) {
		stdout = so;
		fclose(lf);
	}
	return S_OK;
}
#else

ioresult main(char *parameters) {
#if 0
	u_int16 c;
	u_int32 last = ReadTimeCount();
	/* Stop interrupt */
	PERIP(INT_ENABLE1_HP) &= ~(INTF1_ETH);
	PERIP(INT_ENABLE1_LP) &= ~(INTF1_ETH);
	while ((appFlags & APP_FLAG_QUIT) == 0) {
		if (PERIP(INT_ORIGIN1) & INTF1_ETH) {
			c = PERIP(VSRV1_CF);
			PERIP(VSRV1_CF) = c;
			PERIP(INT_ORIGIN1) = INTF1_ETH;
			printf("I: %x %x %lu\n", c, PERIP(VSRV1_CF), ReadTimeCount() - last);
			last = ReadTimeCount();

			c = PERIP(VSRV1_CF);
			if (c & VSRV1_CF_ETHRDST) {
				PERIP(VSRV1_CF) = c;
				printf("P: %x %x %lu\n", c, PERIP(VSRV1_CF), ReadTimeCount() - last);
				last = ReadTimeCount();
			}
		}
	}
	PERIP(INT_ENABLE1_LP) |= INTF1_ETH;
#endif
	printf("Hello world\n");
	return S_OK;
}

#endif
