#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/mman.h>

uint16_t *mmcr;

static void
W(int a, unsigned d)
{
	mmcr[a / 2] = d;
}

static unsigned
R(int a)
{
	return (mmcr[a/2]);
}

static void
waitbusy()
{
	unsigned u, u2;
	int i;

	u = R(0xc2a);		/* Make data lines input */
	u &= ~0x01e0;
	W(0xc2a, u);

	W(0xc38, 0x1000);	/* Clear RS */
	W(0xc36, 0x0040);	/* Raise DIR */
	W(0xc34, 0x0800);	/* Set E */
	for (i = 0; i < 2000; i++) {
		u = R(0xc30) & 0x0100;
		if (u == 0)
			break;
		if (i > 30) 
			getuid();
	}
	W(0xc38, 0x0800);	/* Clear E */
	W(0xc34, 0x0800);	/* Set E */
	W(0xc38, 0x0800);	/* Clear E */
	W(0xc3a, 0x0040);	/* Lower DIR */
	u = R(0xc2a);		/* Make data lines output */
	u |= 0x001e0;
	W(0xc2a, u);
}

static int
WBD(unsigned d)
{
	unsigned u, u2;

	u = R(0xc30);			/* Get current value */
	u &= ~0x19e0;			/* Mask our bits out */
	u |= 0x1000;			/* Set DATA and ENABLE */
	u2 = u;
	u |= ((d >> 4) & 0xf) << 5;	/* Set data bits */
	u2 |= (d & 0xf) << 5;		/* Set data bits */
	waitbusy();
	W(0xc30, u); 			/* plunk! */
	W(0xc34, 0x0800);		/* Set ENABLE */
	W(0xc38, 0x0800);		/* Clear ENABLE */
	W(0xc30, u2); 			/* plunk! */
	W(0xc34, 0x0800);		/* Set ENABLE */
	W(0xc38, 0x0800);		/* Clear ENABLE */
}

static int
WBC(unsigned d)
{
	unsigned u, u2;

	u = R(0xc30);			/* Get current value */
	u &= ~0x19e0;			/* Mask our bits out */
	u2 = u;
	u |= ((d >> 4) & 0xf) << 5;	/* Set data bits */
	u2 |= (d & 0xf) << 5;		/* Set data bits */
	waitbusy();
	W(0xc30, u); 			/* plunk! */
	W(0xc34, 0x0800);		/* Set ENABLE */
	W(0xc30, u2); 			/* plunk! */
	W(0xc34, 0x0800);		/* Set ENABLE */
	W(0xc38, 0x0800);		/* Clear ENABLE */
}


static void
WSD(char *p)
{
	while (*p)
		WBD(*p++);
}

static void
WC(unsigned d)
{
	unsigned u;

	printf("WC %x\n", d & 0xf);
	u = R(0xc30);
	u &= ~0x19e0;
	u |= (d & 0xf) << 5;
	u |= 0x0800;
	W(0xc30, u); 
	u &= ~0x0800;
	W(0xc30, u); 
	u |= 0x0800;
	W(0xc30, u); 
}


int
main(int argc, char **argv)
{
	int fd, error;
	int i, j;
	unsigned u;
	char buf[20];

	fd = open("/dev/elan-mmcr", O_RDWR);
	if (fd < 0)
		err(1, "/dev/elan-mmcr");
	mmcr = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	printf("mmcr = %p\n", mmcr);

	printf("R 0xc20 = %x (%x)\n", R(0xc20), R(0xc20) & 0x19e0);
	printf("R 0xc22 = %x (%x)\n", R(0xc22), R(0xc22) & 0x0040);
	
	printf("R 0xc2a = %x (%x)\n", R(0xc2a), R(0xc2a) & 0x19e0);
	u = R(0xc2a);
	u |= 0x019e0;
	W(0xc2a, u);
	printf("R 0xc2a = %x (%x)\n", R(0xc2a), R(0xc2a) & 0x19e0);

	printf("R 0xc2c = %x (%x)\n", R(0xc2c), R(0xc2c) & 0x0040);
	u = R(0xc2c);
	u |= 0x0040;
	W(0xc2c, u);
	printf("R 0xc2c = %x (%x)\n", R(0xc2c), R(0xc2c) & 0x0040);

	WC(3);	usleep(10000);
	WC(3);	usleep(10000);
	WC(3);  usleep(10000);

	WC(2); usleep(10000);

	WBC(0x2c);
	WBC(0x28);
	WBC(0x0c);
	WBC(0x01);
	WBC(0x01);
	WBC(0x80);

#if 0
	WBC(0x80);
	while (1) {
		WBD(random());
	}
#endif

#if 0
	WBC(0x40);
	for (j = 0; j < 8; j++) WBD(0x0f);
	for (j = 0; j < 8; j++) WBD(0x07);
	for (j = 0; j < 8; j++) WBD(0x03);
	for (j = 0; j < 8; j++) WBD(0x01);
	for (j = 0; j < 8; j++) WBD(0x10);
	for (j = 0; j < 8; j++) WBD(0x18);
	for (j = 0; j < 8; j++) WBD(0x1c);
	for (j = 0; j < 8; j++) WBD(0x1e);

	while (1) {
		for (i = 0; i < 19; i++) {
			WBC(0x80 + i); WBD(0xff); usleep(30000);
			WBC(0x80 + i); WBD(0x00); usleep(30000);
			WBC(0x80 + i); WBD(0x01); WBD(0x04); usleep(30000);
			WBC(0x80 + i); WBD(0x02); WBD(0x05); usleep(30000);
			WBC(0x80 + i); WBD(0x03); WBD(0x06); usleep(30000);
			WBC(0x80 + i); WBD(random()); WBD(0x07); usleep(30000);
		}
		WBC(0x80 + 19);
		WBD(random());
	}
#endif
	
#if 0
	WBC(0x40);
	WBD(0x01); WBD(0x03); WBD(0x07); WBD(0x0f);	/* top left */
	WBD(0x0f); WBD(0x1f); WBD(0x1f); WBD(0x1f);

	WBD(0x1f); WBD(0x1f); WBD(0x1f); WBD(0x0f);	/* bottom left */
	WBD(0x0f); WBD(0x07); WBD(0x03); WBD(0x01);

	WBD(0x10); WBD(0x18); WBD(0x1c); WBD(0x1e);	/* top right */
	WBD(0x1e); WBD(0x1f); WBD(0x1f); WBD(0x1f);

	WBD(0x1f); WBD(0x1f); WBD(0x1f); WBD(0x1e);	/* bottom right */
	WBD(0x1e); WBD(0x1c); WBD(0x18); WBD(0x10);

	WBD(0x00); WBD(0x00); WBD(0x00); WBD(0x00);	/* mid top */
	WBD(0xff); WBD(0xff); WBD(0xff); WBD(0xff);

	WBD(0xff); WBD(0xff); WBD(0xff); WBD(0xff);	/* mid bot */
	WBD(0x00); WBD(0x00); WBD(0x00); WBD(0x00);

	WBC(0x80);
	WBD(0x00); WBD(0x05); WBD(0x02); WBD(0x20);
	WBD(0x00); WBD(0x05); WBD(0x02); WBD(0x20);
	WBD(0x20); WBD(0xff); WBD(0x20); WBD(0x20);
	WBD(0x00); WBD(0x05); WBD(0x02); WBD(0x20);
	WBD(0x05); WBD(0x05); WBD(0xff); WBD(0x20);

	WBC(0x80 + 64);
	WBD(0xff); WBD(0x20); WBD(0xff); WBD(0x20);
	WBD(0x01); WBD(0x04); WBD(0x03); WBD(0x20);
	WBD(0xff); WBD(0x20); WBD(0xff); WBD(0x20);
	WBD(0x20); WBD(0x04); WBD(0xff); WBD(0x20);
	WBD(0x20); WBD(0x00); WBD(0x03); WBD(0x20);

	WBC(0x80 + 20);
	WBD(0xff); WBD(0x20); WBD(0xff); WBD(0x20);
	WBD(0x00); WBD(0x05); WBD(0x02); WBD(0x20);
	WBD(0xff); WBD(0x20); WBD(0xff); WBD(0x20);
	WBD(0x20); WBD(0x05); WBD(0xff); WBD(0x20);
	WBD(0x20); WBD(0xff); WBD(0x20); WBD(0x20);

	WBC(0x80 + 84);
	WBD(0x01); WBD(0x04); WBD(0x03); WBD(0x20);
	WBD(0x01); WBD(0x04); WBD(0x03); WBD(0x20);
	WBD(0x20); WBD(0xff); WBD(0x20); WBD(0x20);
	WBD(0x01); WBD(0x04); WBD(0x03); WBD(0x20);
	WBD(0x20); WBD(0xff); WBD(0x20); WBD(0x20);
#endif

#if 0
	WBC(0x80 + 40);
	for (j = 0; j < 80; j++)
		WBD(j);
#endif
	i = 0;
	while(1) {
#if 0
		sprintf(buf, "%8dabcdefghij", i++);
		WBC(0x80 + 20);
		WSD(buf);
#endif
#if 0
	
		WBC(0x40);
		for (j = 0; j < 64; j++)
			WBD(j);
		usleep(100000);
#endif
#if 0
		WBC(0x80 + 20);
		WSD("...................|");
#endif
	}
	
	return (0);

}
