/*--- 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
|
|