Source file: /~heha/hs/avrpp.zip/src/hwctrl.cpp

/*--- Hardware control functions for AVRPP ---*/
#include "avrpp.h"

enum{
// LPT data port offset and control bit assgnment
 L_DAT	=0,
 B_XT1	=0x01,	// Pin 2
 B_OE	=0x02,	// Pin 3
 B_WR	=0x04,	// Pin 4
 B_BS1	=0x08,	// Pin 5
 B_XA0	=0x10,	// Pin 6
 B_XA1	=0x20,	// Pin 7
 B_SDAT	=0x10,	//	  '299 parallel load
 B_SCLK	=0x40,	// Pin 8, '299 clock
 B_SCMD	=0x80,	// Pin 9, '299 data in
 B_CHK	=0x80,

 B_CLK	=0x01,	// Pin 2, SCI, TPICLK (DIP8:2)
 B_DAT	=0x02,	// Pin 3, SDI (DIP8:5)
 B_CMD	=0x04,	// Pin 4, SII (DIP8:6)
 B_GND	=0x08,	// Pin 5, H = pulls down SDO, TPIDATA
// LPT status port offset and bit assignment
 L_STA	=1,
 S_PE	=0x20,	// Pin 12, fed back from Pin 9 (presence key)
 S_ACK	=0x40,	// Pin 10, '299 data out
 S_BUSY	=0x80,	//!Pin 11, from R/!B, SDO, or TPIDATA (DIP8:7) pin, pulled down by B_VCC or B_GND
// LPT control port offset and bit assignment
 L_CTL	=2,
 B_VPP	=0x01,	//!STB	Pin 1, '0' drives RESET to +12V
 B_VCC	=0x02,	//!AF	Pin 14
 B_BS2	=0x04,	// INIT	Pin 16
 B_PAGEL=0x08,	//!SELIN Pin 17
};

// Control Variables
static byte RegDat, RegCtl;	// Mirrored output data

// Stub functions for in/out encapsulation
#ifdef WIN32
#ifdef _WIN64
//https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Intrin.h
#define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED
#define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED
#define _interlockedbittestandreset64 _interlockedbittestandreset64_NAME_CHANGED
#define _interlockedbittestandset64 _interlockedbittestandset64_NAME_CHANGED
#include <intrin.h>
static void _stdcall out(word a,byte b)	{__outbyte(a,b);}
static byte _stdcall in (word a)	{return __inbyte(a);}
#define INPOUT32DLL "inpoutx64.dll"
#else
#include <conio.h>	// _outp()/_inp()
static void _stdcall out(word a,byte b)	{_outp(a,b);}
static byte _stdcall in (word a)	{return _inp(a);}
#define INPOUT32DLL "inpout32.dll"
#endif

// Initialize GIVEIO (same for giveio32 and giveio64)
static bool init_driver() {
 for(int ls=0;;) {
  SC_HANDLE hsc, hsv;
  ls++;
  if(ls >= 4) return false;
  if(ls >= 3) {	/* Register GIVEIO.SYS to the SCM database */
   char filepath[_MAX_PATH], *cp;
   if (!SearchPath(NULL,"giveio.sys",NULL,sizeof(filepath),filepath,&cp)) continue;
   if ((hsc = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) != NULL) {
    if((hsv = CreateService(hsc,
	"giveio", "giveio", 
	SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_IGNORE,
	filepath,
	NULL, NULL, NULL, NULL, NULL)) != NULL) {
     CloseServiceHandle(hsv);
    }else{
     if((hsv = OpenService(hsc, "giveio", SERVICE_ALL_ACCESS)) != NULL) {
      DeleteService(hsv);
      CloseServiceHandle(hsv);
      hsv = NULL;
     }
    }
    CloseServiceHandle(hsc);
   }
   if(!hsc||!hsv) continue;
  }
  if(ls >= 2) {	// Start GIVEIO
   BOOL res=FALSE;
   if((hsc = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) != NULL) {
    if((hsv = OpenService(hsc, "giveio", SERVICE_ALL_ACCESS)) != NULL) {
     res = StartService(hsv, 0, NULL);
     CloseServiceHandle(hsv);
    }
    CloseServiceHandle(hsc);
   }
   if (!hsc||!hsv||!res) continue;
  }
  HANDLE hdev;	// Open GIVEIO to clear IOPM of this process
  if ((hdev=CreateFile("\\\\.\\giveio", GENERIC_READ, 0, NULL,
	OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)
	) == INVALID_HANDLE_VALUE) continue;
  CloseHandle(hdev);
  break;
 } // for
 return true;
}

// Pointer for in/out function, either direct or going through inpout32.dll
static void(_stdcall*Out32)(word,byte)=out;
static byte(_stdcall*Inp32)(word)=in;

static bool init_inpout32() {
 HINSTANCE hLib=LoadLibrary(INPOUT32DLL);
 if (!hLib) return false;
 if (!(Inp32=(byte(_stdcall*)(word))     GetProcAddress(hLib,"Inp32"))) return false;
 if (!(Out32=(void(_stdcall*)(word,byte))GetProcAddress(hLib,"Out32"))) return false;
 return true;
}
#else
#include <sys/io.h>	// iopl() und ioperm()
#include <sys/ioctl.h>	// ioctl()
#include <unistd.h>	// setuid()

// get permissions for IO (3 Bytes at PortBase)
bool setioperm() {
 uid_t uid = getuid ();
 if (setuid(0)) return false;		// set owner of executeable image to root (so to say)
 if ((CtrlPort.PortAddr>=0x400 && iopl(3))	// if portbase greater than ISA range, enable all ports
 || ioperm(CtrlPort.PortAddr,3,1)) return false;	// otherwise, enable three consecutive addresses
 if (setuid(uid)) return false;		// set owner back, ioperm() continues
 return true;
}
# define Out32(a,b) outb(b,a)
# define Inp32(a) inb(a)

// Over file system (comparable to InpOut32.dll)
#if 0
int hPorts;
static bool init_inpout32() {
 return (hPorts=open("/dev/port",O_RDWR))>=0;
}
static void out(word port, byte value) {
 lseek(hPorts,port,0);
 write(hports,&value,1);
}
static byte in(word port) {
 lseek(hPorts,port,0);
 byte value;
 read(hports,&value,1);
 return value;
}
#endif
#endif

static void LPT_DAT()	{PORTPROP::delay(); Out32(word(CtrlPort.PortAddr+L_DAT),RegDat);}	// actualize RegDat to data port
static void LPT_CTL()	{PORTPROP::delay(); Out32(word(CtrlPort.PortAddr+L_CTL),RegCtl^0x0B);}	// actualize RegCtl to control port

inline void LPT_DAT(byte x)	{RegDat=x;	LPT_DAT();}
inline void LPT_DAT_H(byte x)	{RegDat|=x;	LPT_DAT();}
inline void LPT_DAT_L(byte x)	{RegDat&=~x;	LPT_DAT();}
static byte LPT_DAT_R()		{PORTPROP::delay(); return Inp32(word(CtrlPort.PortAddr+L_DAT));}
static byte LPT_STA()		{PORTPROP::delay(); return byte(Inp32(word(CtrlPort.PortAddr+L_STA))^0x87);}
inline void LPT_CTL(byte x)	{RegCtl=x;	LPT_CTL();}
inline void LPT_CTL_H(byte x)	{RegCtl|=x;	LPT_CTL();}
inline void LPT_CTL_L(byte x)	{RegCtl&=~x;	LPT_CTL();}
inline byte LPT_CTL_R()		{PORTPROP::delay(); return byte(Inp32(word(CtrlPort.PortAddr+L_CTL))^0xFB);}	// unused


// Control Functions

/* Search and Open configuration file */
FILE *open_cfgfile(const char *filename) {
 FILE *fp;
 char filepath[256], *dmy;

 if((fp = fopen(filename, "rt")) != NULL) return fp;
#ifdef WIN32
 if (SearchPath(NULL, filename, NULL, sizeof(filepath), filepath, &dmy)) {
  if((fp = fopen(filepath, "rt")) != NULL) return fp;
 }
#endif
 return NULL;
}

#ifdef WIN32

static LARGE_INTEGER pf;

/* Wait for <dly> µs from timestamp <tic> and update it */
static void tac(LARGE_INTEGER&tic, int dly) {
 LARGE_INTEGER toc;
 LONGLONG d = pf.QuadPart * dly / 1000000;
 do QueryPerformanceCounter(&toc);
 while (toc.QuadPart-tic.QuadPart < d);	// allow overflow for left side
 tic=toc;
}

/* Wait for dly µs */
static void usleep(int dly) {
 LARGE_INTEGER tic;
 QueryPerformanceCounter(&tic);
 tac(tic,dly);
}
void delay_ms(int dly) {
 if (dly>=10) Sleep(dly);
 else usleep(dly*1000);
}

#else
void delay_ms(int dly) {usleep(dly*1000);}
#endif

void PORTPROP::delay() {
 if (CtrlPort.iodelay) {
#ifdef WIN32			// In Windows, subtract calculation time between INs and OUTs
  static LARGE_INTEGER tic;	// Holds the time last I/O was done
  tac(tic,CtrlPort.iodelay);
#else
  usleep(CtrlPort.iodelay);	// In Linux, hard delay
#endif
 }
}

/* Initialize interface port and return port status */
char open_ifport() {
// Get port address
 word a=CtrlPort.PortAddr;
 if (a<0x100) {
#ifdef WIN32
  if (a) --a;	// Unter Windows bei 1 starten, unter Linux bei 0 starten
#endif
  const word PortLst[] = {0x378,0x278,0x3BC};
  if (a<3) a=PortLst[a];
  else return CtrlPort.Stat=RES_NOPORT;	// Invalid port address
  CtrlPort.PortAddr=a;
 }
#ifdef WIN32
 OSVERSIONINFO vinfo = {sizeof(OSVERSIONINFO)};
// Check if high resolution timer is supported
 QueryPerformanceFrequency(&pf);
 if (!pf.QuadPart) return CtrlPort.Stat=RES_BADENV;
// Open driver if needed
 if (!GetVersionEx(&vinfo)) return CtrlPort.Stat=RES_BADENV;
 if (CtrlPort.inpout32 && !init_inpout32()
 || vinfo.dwPlatformId!=VER_PLATFORM_WIN32_NT
 || !CtrlPort.inpout32 && !init_driver())
   return CtrlPort.Stat=RES_DRVFAIL;
#else
 if (!setioperm()) return CtrlPort.Stat=RES_DRVFAIL;
#endif
// Talk with board
 LPT_CTL(0);	// power off
 LPT_DAT(0x08);
 if (LPT_DAT_R()!=0x08) return CtrlPort.Stat=RES_NOPORT;
 LPT_DAT(0x40);
 if (LPT_DAT_R()!=0x40) return CtrlPort.Stat=RES_NOPORT;
	// Check if the adapter is AVRSP (D7-PE connection)
 LPT_DAT(0x80);
 if (LPT_STA() & S_PE) {
  LPT_DAT(0);
  if (!(LPT_STA() & S_PE)) return CtrlPort.Stat=RES_OPENED;
 }
 return CtrlPort.Stat = RES_NOADAPTER;
}

// Power control
void power_off() {
 LPT_CTL_L(B_VPP);	// Upp off
 delay_ms(1);		// 1 ms
 LPT_DAT(0);		// Signals = L
 LPT_CTL_L(B_VCC);	// Ucc off
 delay_ms(10);		// 10 ms
}

static int tpiSF(int);
static bool tpiWait();

void power_on() {
 switch (CtrlPort.Mode) {	// For 6/8 pin device
  case 0:			// For parallel device
  LPT_CTL_H(B_VCC);		// Ucc on
  if (!CtrlPort.Quick) {
   usleep(3);
   for (int n=0; n<3; n++) {	// Toggle XTAL1 six times
    LPT_DAT_H(B_XT1);
    LPT_DAT_L(B_XT1);
   }
  }
  LPT_DAT(B_OE);		// OE = H
  LPT_CTL_H(B_VPP);		// Upp on
  LPT_DAT_H(B_WR);		// WR = H
  break;
  case 1:
  case 2:
  LPT_DAT(B_GND);	// Set Prog_enable state to "000"
  delay_ms(1);
  if (CtrlPort.Quick) {	// Is Quick power-up?
   LPT_CTL_H(B_VCC|B_VPP);	// Ucc+Upp on
  }else{			// Normal power-up
   LPT_CTL_H(B_VCC);		// Ucc on
   usleep(3);
   for (int n=0; n<3; n++) {	// Toggle CLK six times
    LPT_DAT_H(B_CLK);
    LPT_DAT_L(B_CLK);
   }
   LPT_CTL_H(B_VPP);	// Upp on
  }
  LPT_DAT(0);		// Signals = L
  break;
  case 3:
  LPT_DAT(B_GND);	// Set Prog_enable state to "000"
  LPT_CTL_H(B_VCC);	// Ucc on
  delay_ms(128);
  LPT_CTL_H(B_VCC|B_VPP|B_PAGEL);	// Ucc+Upp on
  delay_ms(10);
  LPT_DAT(B_CLK);	// TPICLK high, TPIDATA high
  tpiSF(0xFFFF);
  if (tpiSend(0xC2,7)	// Guard Time: 0 Bits
  && !tpiRecv(0x80)	// sollte 0 ergeben
  && tpiSend(0xE0,0))
  tpiWait();		// no idea how to handle return value here
  break;
 }
 delay_ms(1);
}

// Release port
void close_ifport() {
 if (CtrlPort.Stat==RES_OPENED && CtrlPort.PortAddr>=0x100) power_off();
}

// Set a byte for parallel device
void set_byte(byte mode, byte dat) {
 int n;

 for (n = 0; n < 8; n++) {		/* Send data into shift register */
  LPT_DAT(dat&0x80?B_OE|B_WR|B_SDAT:B_OE|B_WR);
  LPT_DAT_H(B_SCLK);
  dat <<= 1;
 }

 RegDat = B_OE | B_WR;			/* Setup XA[1:0], BS[2:1] */
 if (mode & BS_2) LPT_CTL_H(B_BS2);
 if (mode & XA_0) RegDat |= B_XA0;
 if (mode & XA_1) RegDat |= B_XA1;
 if (mode & BS_1) RegDat |= B_BS1;
 LPT_DAT();
 LPT_DAT_H(B_XT1);		/* Give XTAL1 pulse */
 LPT_DAT_L(B_XT1);
 LPT_CTL_L(B_BS2);		/* BS2 = L */
}

/* Receive a byte from parallel device */
byte rcv_byte(byte mode) {
 int n;
 byte dat = 0;

 RegDat = B_OE | B_WR;			/* Setup XA[1:0], BS[2:1], S1=H, OE=L */
 if (mode & BS_2) LPT_CTL_H(B_BS2);
 if (mode & XA_0) RegDat |= B_XA0;
 if (mode & XA_1) RegDat |= B_XA1;
 if (mode & BS_1) RegDat |= B_BS1;
 RegDat |= B_SCMD;
 RegDat &= (byte)~B_OE;
 LPT_DAT();
 LPT_DAT_H(B_SCLK);	/* Latch data into shift register */
 RegDat &= (byte)~B_SCMD;		/* Set S1=L, OE=H */
 RegDat &=(byte)~B_SCLK;
 RegDat |= B_OE;
 LPT_DAT();
 LPT_CTL_L(B_BS2);			/* BS2 = L */
 for (n = 0; n < 8; n++) {		/* Read data from shift register */
  dat <<= 1;
  if (LPT_STA() & S_ACK) dat++;
  LPT_DAT_H(B_SCLK);
  LPT_DAT_L(B_SCLK);
 }
 return dat;
}

/* Apply a PAGEL pulse for parallel devices */
void stb_pagel() {
 LPT_CTL_H(B_PAGEL);
 usleep(1);		// 150 ns
 LPT_CTL_L(B_PAGEL);
}

/* Apply a WR pulse and delay for parallel devices */
void stb_wr(byte mode, byte dly_ms) {
 RegDat = B_OE | B_WR;			/* Setup XA[1:0], BS[2:1] */
 if (mode & BS_2) LPT_CTL_H(B_BS2);
 if (mode & XA_0) RegDat |= B_XA0;
 if (mode & XA_1) RegDat |= B_XA1;
 if (mode & BS_1) RegDat |= B_BS1;
 LPT_DAT();
 LPT_DAT_L(B_WR);	/* WR = L */
 if (dly_ms) delay_ms(dly_ms);	/* delay */
 else usleep(1);	// 150 ns
 LPT_DAT_H(B_WR);			/* WR = H */
 LPT_CTL_L(B_BS2);
}

/* Send a serial command and Receive a byte for HVS devices */
byte xfer8 (byte cmd, byte dat) {
 int n, m;
 RegDat = 0;
 for (n = 0; n < 8; n++) {
  for (m = 0; m < 16; m++) {	// Apply 16 SCLKs for tn15 mode,
   LPT_DAT_H(B_CLK);
   if (CtrlPort.Mode!=2) break;	// But only a rise edge for others
   LPT_DAT_L(B_CLK);
  }
  RegDat = 0;
  if (dat&0x80) RegDat|=B_DAT;	// SCLK=L and Set next data and instruction
  if (cmd&0x80) RegDat|=B_CMD;
  LPT_DAT();
  cmd <<= 1; dat <<= 1;
  if ((LPT_STA()&S_BUSY)) dat++;/* Read SDO */
 }
 for (n = 0; n < 3; n++) {	// Push-in trailing 3 bits...
  for (m = 0; m < 16; m++) {
   LPT_DAT_H(B_CLK);
   if (CtrlPort.Mode != 2) break;
   LPT_DAT_L(B_CLK);
  }
  LPT_DAT(0);
 }
 return dat;
}

/* Wait for end of internal process */
/* (return with 0 means time out)   */
int wait_ready () {
 int n;
 switch (CtrlPort.Mode) {
  case 3: {
   n=1000;
   do{
    int r=tpiRecv(0x72);	// Query NVMBSY (Bit 7) in NVMCSR
    if (r<0) return 0;		// communication error
    if (!(r&0x80)) return 1;	// ready
   }while (--n);
   return n;	
  }
  default: {
   LPT_STA();
   for (n = 50000; n && !(LPT_STA() & S_BUSY); n--);
   return n;
  }
 }
}

/************
 * TPI code *
 ************/
// Send one frame (typ. 12 bit) via TPI until highest bit is shift out
// Or clock one bit with f==1
// Or clock 16 times with f==0xFFFF
// The last (or single) bit is always 1
// Returns levels seen on BUSY line just before CLK goes high (the SAMPLE time)
static int tpiSF(int f) {
 int ret=0,mask=1;
 do {
  LPT_DAT(f&1?0:B_GND);	// B_GND-Bit, invertiert, alles andere 0
  usleep(1);
  LPT_DAT_H(B_CLK);
  if (LPT_STA()&S_BUSY) ret|=mask;	// Pegel einfangen
  mask+=mask;
  usleep(1);
 }while (f>>=1);
 return ret;
}
// Send one byte via TPI with collision check
static bool tpiS(byte b) {
 byte parity=0;
 for (byte c=b;c;c>>=1) parity^=c&1;
 int f=b<<1|parity<<9|0xC00;	// start, data(8), parity, stop(2)
 return tpiSF(f)==f;
}
// Send BREAK sequence
bool tpiBreak() {return tpiSF(0x3000)==0x3000;}

// Receive one byte via TPI with error check: timeout, parity, frame, break
static int tpiR() {
 int r=0;
 byte i=130; do{
  if (!tpiSF(1)) goto found;	// sample start bit
 }while(--i);
 return -1;	// timeout: no start bit after 130 clock ticks
found:
 r=tpiSF(0x7FF);		// receive next 11 bits, LSB first
 if (!(r&0x3FF)) return -4;	// break received (ignore parity)
 if (~r&0x600) return -3;	// frame error: stop bits not ==1
 int parity=0;
 for (int c=r&0x1FF;c;c>>=1) parity^=c&1;
 if (parity) return -2;		// parity error
 tpiSF(3);			//???
 return r&0xFF;			// true data byte
}

bool tpiSend(byte op, byte data) {
 if (!tpiS(op)) return false;
 if (op==0xE0) {		// KEY (ignore <data> here)
  static const byte key[8]={0xFF,0x88,0xD8,0xCD,0x45,0xAB,0x89,0x12};
  for (int i=0; i<8; i++) if (!tpiS(key[i])) return false;
  return true;
 }else return tpiS(data);
}
int tpiRecv(byte op) {
 if (!tpiS(op)) return -5;	// collition while send
 return tpiR();
}
static bool tpiWait() {
 int TPIIR =tpiRecv(0x8F);	// zur Kontrolle einlesen, sollte 0x80 sein
 int TPIPCR=tpiRecv(0x82);	// sollte 0x00 sein (128 Guard-Bits): Bei meinem ATtiny5 kommt 0x83: Murks!
 tpiSend(0xC2,0x07);		// 0 Guard-Bits
 TPIPCR    =tpiRecv(0x82);	// sollte nun 0x07 ergeben: Bei meinem ATtiny5 kommt 0x83: Murks!
 for(int i=0;i<100;i++){
  int TPIPCR=tpiRecv(0x80);
  if (TPIPCR<0) return false;
  if (TPIPCR&0xFD) return false;// some error
  if (TPIPCR&2) return true;	// bit is set
 }
 return false;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded