/*
 * l16bitio.cc
 * 
 * Latch16Bit (low lewel) IO routines. 
 * Driver for the Latch16Bit board. 
 * 
 * Copyright (c) 2003 by Wolfgang Wieser (wwieser@gmx.de) 
 * 
 * This file may be distributed and/or modified under the terms of the 
 * GNU General Public License version 2 as published by the Free Software 
 * Foundation. 
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * 
 */

#include "l16bitio.h"

#include <unistd.h>


void Latch16BitIO::_WriteControl(register uchar val,uchar do_save_restore,
	uchar set_data)
{
	// Take control select (and strobe to be sure) down. 
	// Make sure parallel port is in output mode. <-- IMPORTANT!
	pio->control_clrbit(L_CTLSEL|L_STROBE|LPPortIO::C_INPEN);
	
	// Save current data bits: 
	if(do_save_restore & WCDoSave)
	{  pio->push_data();  }
	
	pio->data_outb(val);
	control_bits=val;  // CORRECT. 
	
	// Fire strobe: 
	pio->control_setbit(L_STROBE);
	pio->control_clrbit(L_STROBE);
	
	// Restore data: 
	if(do_save_restore & WCDoRestore)
	{  pio->pop_data();  }
	else if(do_save_restore & WCSetData)
	{  pio->data_outb(set_data);  }
	
	// Take control select up again: 
	pio->control_setbit(L_CTLSEL);
}


void Latch16BitIO::CtlChannelOutput(int channel,uchar enable)
{
	register uchar bits=
		((channel & 1) ? L_OA_HIGHZ : 0) | 
		((channel & 2) ? L_OB_HIGHZ : 0);
	if(enable)
	{  _ClrControlBits(bits);  }
	else
	{  _SetControlBits(bits);  }
}


void Latch16BitIO::CtlOutChannelTransparent(int channel,uchar transparent)
{
	register uchar bits=
		((channel & 1) ? L_OA_TRANSP : 0) | 
		((channel & 2) ? L_OB_TRANSP : 0);
	if(transparent)
	{
		L_CTLSEL&=~LPPortIO::C_INPEN;
		_SetControlBits(bits);
	}
	else
	{  _ClrControlBits(bits);  }
}


void Latch16BitIO::SelectInputChannel(int channel)
{
	switch(channel)
	{
		case 0:
			//L_CTLSEL&=~LPPortIO::C_INPEN;
			_ClrControlBits(L_IA_BUSEN|L_IB_BUSEN);
			break;
		case 1:
			//L_CTLSEL|=LPPortIO::C_INPEN;
			_ChgControlBits(L_IA_BUSEN,L_IB_BUSEN,WCSetData);
			break;
		case 2:
			//L_CTLSEL|=LPPortIO::C_INPEN;
			_ChgControlBits(L_IB_BUSEN,L_IA_BUSEN,WCSetData);
			break;
	}
}


void Latch16BitIO::CtlInChannelTransparent(int channel,uchar transparent)
{
	register uchar bits=
		((channel & 1) ? L_IA_TRANSP : 0) | 
		((channel & 2) ? L_IB_TRANSP : 0);
	if(transparent)
	{
		L_CTLSEL|=LPPortIO::C_INPEN;
		_SetControlBits(bits);
	}
	else
	{  _ClrControlBits(bits);  }
}


uchar Latch16BitIO::ReadSingleByte(int channel,uchar disable_out)
{
	// Save current control bits: 
	uchar ctl_save=control_bits;
	uchar L_CTLSEL_save=L_CTLSEL;
	
	// This internally saves the data bits and 
	// gets us ready for reading: 
	uchar set_bits = disable_out ? 
		(channel==1 ? (L_IA_TRANSP|L_IA_BUSEN|L_OA_HIGHZ) : 
		              (L_IB_TRANSP|L_IB_BUSEN|L_OB_HIGHZ)) : 
		(channel==1 ? (L_IA_TRANSP|L_IA_BUSEN) : 
		              (L_IB_TRANSP|L_IB_BUSEN));
	L_CTLSEL|=LPPortIO::C_INPEN;
	_ChgControlBits(
		set_bits,
		L_OA_TRANSP|L_OB_TRANSP,
		/*do_save_restore=*/WCDoSave/*|WCSetData*/);
	
	// Actually read data: 
	uchar indata=pio->data_inb();
	
	// Restore previous state & data direction (including data bits): 
	L_CTLSEL=L_CTLSEL_save;
	_WriteControl(ctl_save,/*do_save_restore=*/WCDoRestore);
	
	return(indata);
}


void Latch16BitIO::WriteSingleByte(int channel,uchar byte)
{
	// Save current control bits: 
	uchar ctl_save=control_bits;
	uchar L_CTLSEL_save=L_CTLSEL;
	
	// This internally saves the data bits and 
	// actually writes the data byte: 
	uchar transp_mask=
		((channel&1) ? L_OA_TRANSP : 0x00) | 
		((channel&2) ? L_OB_TRANSP : 0x00);
	L_CTLSEL&=~LPPortIO::C_INPEN;
	_ChgControlBits(transp_mask,L_IA_BUSEN|L_IB_BUSEN,
		/*do_save_restore=*/WCDoSave|WCSetData,byte);
	
	// Now latch the data and 
	// restore previous state & data direction (including data bits): 
	L_CTLSEL=L_CTLSEL_save;
	_WriteControl(ctl_save&~transp_mask,
		/*do_save_restore=*/WCDoRestore);
}


int Latch16BitIO::Init(uchar strobe_line,uchar crlsel_line)
{
	L_STROBE=strobe_line;
	L_CTLSEL=crlsel_line;
	control_bits=0;
	
	// Do the initialisation: 
	pio->control_setbit(LPPortIO::C_INPEN);   // input direction (temporarily)
	pio->control_clrbit(LPPortIO::C_INTREN);
	pio->control_clrbit(L_STROBE);
	pio->control_clrbit(L_CTLSEL);
	
	// This will set output direction because L_CTLSEL 
	// does not have LPPortIO::C_INPEN bit set. 
	Deactivate();
	
	return(0);
}


Latch16BitIO::Latch16BitIO(LPPortIO *_pio)
{
	pio=_pio;
	L_STROBE=0x0;
	L_CTLSEL=0x0;
	control_bits=0;
}

Latch16BitIO::~Latch16BitIO()
{
	// Set port inactive:
	if(L_STROBE && L_CTLSEL)
	{  Deactivate();  }
	
	pio=NULL;
}

