/* libnexys.c
 * Parallel port bit-banging driver for libnexys
 * libnexys, a free driver for Digilent NEXYS2 boards
 *
 * Copyright (c) 2007-2008 Joshua Wise
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * Commercial licenses are available by request.
 */


#ifdef WIN32
#include "libnexys.win32.c"
#else
#define LIBNEXYS_INTERNAL
#ifndef PARPORT
#  define PARPORT
#endif
#include "libnexys.h"
#include <stdio.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include <linux/ppdev.h>
#include <string.h>

#define TDI     0
#define TCK     1
#define TMS     2
#define TDO     4


nexys2_t nexys2_init(void) {
	int pfd = open("/dev/parport0", O_RDWR);
	if (pfd < 0)
	{
		perror("open /dev/parport0");
		return -1;
	}
	
	if ((ioctl(pfd, PPEXCL) < 0) || (ioctl(pfd, PPCLAIM) < 0))
	{
		perror("lock /dev/parport0");
		return -1;
	}
	
	return pfd;
}

int nexys2_jtag_enable(int pfd)
{
	return 0;
}

int nexys2_jtag_disable(int pfd)
{
	return 0;
}

int nexys2_jtag_setbits(int pfd, int tms, int tdi, int tck)
{
	unsigned char data;
	int i;
	
	data = ((!!tms) << TMS) | ((!!tdi) << TDI) | ((!!tck) << TCK);
	
	if (ioctl(pfd, PPWDATA, &data) < 0)
	{
		perror("nexys2_jtag_setbits: ioctl");
		return -1;
	}
	
	return 0;
}

int _gettdo(int pfd)
{
	unsigned char data;
	
	if (ioctl(pfd, PPRSTATUS, &data) < 0)
	{
		perror("_gettdi: PPRSTATUS");
		return -1;
	}
	
	data ^= 0x80;
	data >>= TDO;
	data &= 1;
	
	return data;
}

int nexys2_jtag_puttdi(int pfd, int tms, int nbits, unsigned char *bits, unsigned char *obits)
{
	int i;
	
	if (obits)
		memset(obits, 0, (nbits / 8) + ((nbits % 8) != 0));
	
	for (i = 0; i < nbits; i++)
	{
		int bit;
		
		bit = bits[i / 8] & (1 << (i % 8));
		nexys2_jtag_setbits(pfd, tms, bit, 1);
		nexys2_jtag_setbits(pfd, tms, bit, 0);
		if (obits)
			obits[i/8] |= _gettdo(pfd) << (i % 8);
	}
	return 0;
}

int nexys2_jtag_puttmstdi(int pfd, int nbits, unsigned char *bits, unsigned char *obits)
{
	int i;
	
	if (obits)
		memset(obits, 0, (nbits / 8) + ((nbits % 8) != 0));
	
	for (i = 0; i < nbits; i++)
	{
		int tdi, tms;
		
		tdi = bits[i / 4] & (1 << ((i % 4) * 2));
		tms = bits[i / 4] & (2 << ((i % 4) * 2));
		nexys2_jtag_setbits(pfd, tms, tdi, 0);
		nexys2_jtag_setbits(pfd, tms, tdi, 1);
		nexys2_jtag_setbits(pfd, tms, tdi, 0);
		if (obits)
			obits[i/8] |= _gettdo(pfd) << (i % 8);
	}
	return 0;
}

int nexys2_jtag_gettdo(int pfd, int tdi, int tms, int nbits, unsigned char *obits)
{
	int i;
	
	if (obits)
		memset(obits, 0, (nbits / 8) + ((nbits % 8) != 0));
	
	for (i = 0; i < nbits; i++)
	{
		nexys2_jtag_setbits(pfd, tms, tdi, 0);
		nexys2_jtag_setbits(pfd, tms, tdi, 1);
		nexys2_jtag_setbits(pfd, tms, tdi, 0);
		if (obits)
			obits[i/8] |= _gettdo(pfd) << (i % 8);
	}
	return 0;
}

#endif

#ifdef TEST_DRIVER

void poke_idcode(nexys2_t nexys)
{
	unsigned char seq1[] = { 0xaa, 0x22};
	unsigned char seq2[] = { 0xaa, 0x02};
	unsigned int idcode = 0;
	unsigned int woop = 0x12345678;
	
//	while(1)
	{
		while (nexys2_jtag_enable(nexys) < 0)
			;
		if (nexys2_jtag_puttmstdi(nexys, 8, seq1, NULL) < 0)
			return;
		do
		{
			if (nexys2_jtag_gettdo(nexys, 0, 0, 32, &idcode) < 0)
				return;
			printf("IDCODE: %08x\n", idcode);
		} while(idcode && (idcode != 0xFFFFFFFF));
		if (nexys2_jtag_puttmstdi(nexys, 8, seq2, NULL) < 0)
			return;
		if (nexys2_jtag_disable(nexys) < 0)
			return;
	}
}

void main()
{
	nexys2_t nexys;
	nexys = nexys2_init();
	if (!nexys)
		return;
	poke_idcode(nexys);
}

#endif

