TheNetNode-CB/os/win32/6pack.c

1484 lines
47 KiB
C
Executable File

/************************************************************************/
/* */
/* ***** ***** */
/* ***** ***** */
/* ***** ***** */
/* ***** ***** */
/* *************** *************** */
/* ***************** ***************** */
/* *************** *************** */
/* ***** ***** TheNetNode */
/* ***** ***** Portable */
/* ***** ***** Network */
/* ***** ***** Software */
/* */
/* File os/win32/6pack.c (maintained by: DG9OBU) */
/* */
/* This file is part of "TheNetNode" - Software Package */
/* */
/* Copyright (C) 1998 - 2008 NORD><LINK e.V. Braunschweig */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the NORD><LINK ALAS (Allgemeine Lizenz fuer */
/* Amateurfunk Software) as published by Hans Georg Giese (DF2AU) */
/* on 13/Oct/1992; either version 1, or (at your option) any later */
/* version. */
/* */
/* This program is distributed WITHOUT ANY WARRANTY only for further */
/* development and learning purposes. See the ALAS (Allgemeine Lizenz */
/* fuer Amateurfunk Software). */
/* */
/* You should have received a copy of the NORD><LINK ALAS (Allgemeine */
/* Lizenz fuer Amateurfunk Software) along with this program; if not, */
/* write to NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig */
/* */
/* Dieses Programm ist PUBLIC DOMAIN, mit den Einschraenkungen durch */
/* die ALAS (Allgemeine Lizenz fuer Amateurfunk Software), entweder */
/* Version 1, veroeffentlicht von Hans Georg Giese (DF2AU), */
/* am 13.Oct.1992, oder (wenn gewuenscht) jede spaetere Version. */
/* */
/* Dieses Programm wird unter Haftungsausschluss vertrieben, aus- */
/* schliesslich fuer Weiterentwicklungs- und Lehrzwecke. Naeheres */
/* koennen Sie der ALAS (Allgemeine Lizenz fuer Amateurfunk Software) */
/* entnehmen. */
/* */
/* Sollte dieser Software keine ALAS (Allgemeine Lizenz fuer Amateur- */
/* funk Software) beigelegen haben, wenden Sie sich bitte an */
/* NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig */
/* */
/************************************************************************/
#include "tnn.h"
#ifdef SIXPACK
#include "6pack.h"
/************************************************************************/
/* Initialisieren einer TNC-Struktur */
/************************************************************************/
void initTNC(UBYTE uTNC)
{
UWORD uResetCounter;
/* gewuenschte TNC-Struktur der Liste initialisieren */
if (uTNC < MAX_TNC)
{
/* Reset-Counter konservieren */
uResetCounter = RING[uTNC].uReset;
memset(&RING[uTNC], 0, sizeof(TNC));
RING[uTNC].uReset = uResetCounter;
}
}
/************************************************************************/
/* Sixpack-Kommunikation mit den angeschlossenen TNC (RX, TX, DCD) */
/************************************************************************/
void Sixpack_Housekeeping(void)
{
register UBYTE uTNC;
UBYTE cCmd[1];
/* Ring steht, wir koennen nix machen */
if (uRingStatus == RING_HALT)
return;
/* Ring laeuft, Watchdogs checken */
if (uRingStatus == RING_RUN)
{
/* Watchdog fuer haengende TNC */
for (uTNC = 0; uTNC < MAX_TNC; ++uTNC)
{
/* Gibt es diesen TNC ? Falls nicht, dann kann auch keiner mehr kommen */
if (RING[uTNC].bPresent == FALSE)
break;
/* Zu lange nichts mehr gehoert, dann Ring neu starten */
if ((tic10 - RING[uTNC].tLastHeard) > TNC_TIMEOUT)
{
RING[uTNC].uReset++;
uRingStatus = RING_IDLE;
LOGL3("TNC %u: TNC seit %ld ms still, reinitialisiere Ring !", uTNC, (tic10 - RING[uTNC].tLastHeard) * 10);
LOGL3("tic10 ist %ld, LastHeard = %ld", tic10, RING[uTNC].tLastHeard);
break;
}
/* normaler Watchdog fuer TNC-Anwesenheit */
if ((tic10 - RING[uTNC].tWatchdog) > TNC_WATCHDOG)
{
cCmd[0] = cmdLED(uTNC);
toRing(cCmd, 1);
RING[uTNC].tWatchdog = tic10;
/* RXC darf nicht ohne DCD gesetzt sein, ggf. korrigieren */
if ((RING[uTNC].bDCD == FALSE) && (RING[uTNC].cRXC))
{
RING[uTNC].cRXC = 0;
LOGL3("TNC %u: RXC-Korrektur !", uTNC);
}
}
/* Festhaengende PTT loesen */
if ((RING[uTNC].tPTTWatchdog != 0) && (tic10 > RING[uTNC].tPTTWatchdog))
{
/* sendet dieses TNC momentan ? */
if (RING[uTNC].cTXC > 0)
{
RING[uTNC].cTXC--;
LOGL3("TNC %u: TXC-Korrektur !", uTNC);
/* noch Sendeframes im TNC, dann wieder scharf machen */
if (RING[uTNC].cTXC > 0)
{
/* wir nehmen die Aussendung eines Frames maximaler Laenge, */
/* TX-Delay, TX-Tail und etwas Sicherheit an */
RING[uTNC].tPTTWatchdog = ( tic10
+ (2 * portpar[uMap_TNCtoPort[uTNC]].txdelay)
+ (21120 / portpar[uMap_TNCtoPort[uTNC]].speed)
);
}
else
{
RING[uTNC].tPTTWatchdog = 0; /* Wachhund aus */
}
}
else
{
/* TNC sendet nicht, aber Wachhund auch angekettet ? */
if (RING[uTNC].tPTTWatchdog != 0)
{
RING[uTNC].tPTTWatchdog = 0;
LOGL3("TNC %u: PTT-Watchdog entlaufen, wieder eingefangen !", uTNC);
}
}
}
}
}
/* Ring nicht initialisiert */
if (uRingStatus == RING_IDLE)
{
/* TNC-Strukturen initialisieren */
for (uTNC = 0; uTNC < MAX_TNC; ++uTNC)
initTNC(uTNC);
/* Zaehlung starten */
startCount();
/* Zurueck zu anderen Aufgaben */
return;
}
/* Daten vom Ring holen (Daten, Meldungen etc.) */
Sixpack_RX();
/* So lange wir auf das Zaehlpaket warten, keine Sendedaten auf */
/* den Ring legen, aber ggf. noch ein Zaehlpaket senden. */
if (uRingStatus == RING_INIT)
{
/* Falls nach zehn Sekunden kein Zaehlpaket kam, wieder eines senden */
if ((tic10 - tLastInit) >= PROBE_TIMEOUT)
{
uRingStatus = RING_IDLE;
LOGL2("-RING-: Ring laeuft nicht, reinitialisiere ...");
}
return;
}
/* Sendedaten zu den TNC bringen */
Sixpack_TX();
}
/************************************************************************/
/* Liefert den DCD-Status eines TNC */
/* ACHTUNG, die "+" beim Zusammenbau des Status sollen da sein ! */
/* (normalerweise waeren es "|=", aber die funktionieren hier nicht) */
/************************************************************************/
WORD Sixpack_DCD(UWORD uPort)
{
WORD uDCD = 0;
register UBYTE uTNC; /* Nummer des zustaendigen TNC ermitteln */
if (uPort <= L2PNUM)
uTNC = uMap_PorttoTNC[uPort];
else
return (0);
/* Kein TNC fuer diesen Port */
if (uTNC == 0xFF)
return (0);
/* DCD vom TNC gemeldet ? */
if (RING[uTNC].bDCD == TRUE)
uDCD |= (WORD)(DCDFLAG);
/* Sendet der TNC gerade oder Calibrate ? */
if ( (RING[uTNC].cTXC > 0)
|| (RING[uTNC].cTestCount > 0)
)
uDCD |= (WORD)(PTTFLAG);
/* Empfaengt der TNC momentan ein Frame ? */
/*
if (RING[uTNC].cRXC > 0)
uDCD += (WORD)(RXBFLAG);
*/
/* DCD-Zustand melden */
return (uDCD);
}
/************************************************************************/
/* Verarbietung von normalen 6PACK-Meldungen */
/************************************************************************/
void processSixpackCmd(UBYTE cByte)
{
UBYTE uTNC = (cByte & MASK_TNCNUM);
UBYTE cCmdBuf[1];
/* Puffer holen und Empfangsport eintragen */
MBHEAD *rxfhd = NULL;
/* LED-Kommandos, die keinen TNC fanden ignorieren wir */
if ((cByte & CMD_LED) == CMD_LED)
return;
/* Start of Frame, TX-Underrun, RX-Overrun, RX-Buffer Overflow */
if ((cByte & MASK_NORMCMD) == MASK_NORMCMD)
{
/* nur die oberen Bits interessieren uns hier */
if ((cByte & MASK_CMD) == CMD_SOF)
{
/* Start of Frame */
LOGL4("TNC %u: 'Start of Frame' (%sleitend)", uTNC, ((cReceiveFrom != 0xFF) ? "aus" : "ein"));
switch (uRXStatus)
{
/* Einleitendes SOF */
case RX_WAIT_SOF:
/* RX beginnt, erstes SOF am Frameanfang */
if (cReceiveFrom == 0xFF)
{
LOGL2("TNC %u: RX beginnt, RXC ist jetzt %u)", uTNC, RING[uTNC].cRXC);
if (RING[uTNC].cRXC == 0)
LOGL3("TNC %u: 'Start of Frame' waehrend RXC noch null !!!", uTNC);
if (uRXRawBufferSize != 0)
LOGL3("TNC %u: RX-Rohdatenpuffer schon vorgefuellt !!!", uTNC);
/* merken, von welchem TNC wir empfangen */
cReceiveFrom = uTNC;
/* Puffer ruecksetzen */
/*
memset(cRXRawBuffer, 0, sizeof(cRXRawBuffer));
uRXRawBufferSize = 0;
memset(cRXBuffer, 0, sizeof(cRXBuffer));
uRXBufferSize = 0;
*/
/* CON-LED des betreffenden TNC einschalten */
setLED(uTNC, LED_CON);
cCmdBuf[0] = cmdLED(uTNC);
toRing(cCmdBuf, 1);
}
else
LOGL3("TNC %u: RX-TNC bei erstem SOF schon gesetzt !", uTNC);
/* Empfangszustand merken */
uRXStatus = RX_WAIT_END;
break;
/* Wir empfangen und haben beendendes SOF erhalten */
case RX_WAIT_END:
if (cReceiveFrom != uTNC)
LOGL3("TNC %u: RX-TNC bei Frameende ungleich TNC (%u) bei Frameanfang !", uTNC, cReceiveFrom);
/* Pruefen, ob Empfangsdaten da sind, wenn nein, dann sind */
/* wir sehr wahrscheinlich out of sync und empfangen einfach */
/* mal weiter, da wir jetzt erst wahrscheinlich am Frame- */
/* anfang sind. */
if (uRXRawBufferSize == 0)
{
/* wie bei normalem RX-Beginn ... */
/* merken, von welchem TNC wir empfangen */
cReceiveFrom = uTNC;
/* Puffer ruecksetzen */
/*
memset(cRXRawBuffer, 0, sizeof(cRXRawBuffer));
uRXRawBufferSize = 0;
memset(cRXBuffer, 0, sizeof(cRXBuffer));
uRXBufferSize = 0;
*/
/* CON-LED des betreffenden TNC einschalten */
setLED(uTNC, LED_CON);
cCmdBuf[0] = cmdLED(uTNC);
toRing(cCmdBuf, 1);
LOGL2("TNC %u: Resync !!!", uTNC);
return;
}
/* Empfangene Daten dekodieren */
if (DecodeRawBuffer(cReceiveFrom))
{
/* Checksumme ist korrekt, Daten an L2 geben */
/* Hier koennen auch Daten von TNC ankommen, */
/* die keinem Port zugeordnet sind. Deren Daten */
/* werfen wir weg. */
if (portenabled(uMap_TNCtoPort[cReceiveFrom]))
{
/* Port ist eingeschaltet, dann Daten in den Empfangspuffer */
register UWORD uByteCounter;
/* Puffergroesse korrigieren (TX-Delay und Checksumme) */
uRXBufferSize -= 2;
/* Puffer holen */
(rxfhd = (MBHEAD *)allocb(ALLOC_MBHEAD))->l2port = uMap_TNCtoPort[cReceiveFrom];
/* Frame in Buffer umkopieren */
for (uByteCounter = 1; uByteCounter <= uRXBufferSize; ++uByteCounter)
putchr(cRXBuffer[uByteCounter], rxfhd);
/* Frame zum L2 geben */
relink((LEHEAD *)rxfhd, (LEHEAD *)rxfl.tail);
}
}
else
{
/* Checksumme fehlerhaft */
LOGL3("TNC %u: Checksumme fehlerhaft !!!", uTNC);
RING[cReceiveFrom].uChecksumErr++;
}
/* CON-LED ausschalten */
resetLED(cReceiveFrom, LED_CON);
cCmdBuf[0] = cmdLED(cReceiveFrom);
toRing(cCmdBuf, 1);
/* Frameempfang vermerken */
if (RING[cReceiveFrom].cRXC > 0)
RING[cReceiveFrom].cRXC--;
/* Empfangsvariablen neu initialisieren und Puffer ruecksetzen */
cReceiveFrom = 0xFF;
uRXStatus = RX_WAIT_SOF;
memset(cRXRawBuffer, 0, sizeof(cRXRawBuffer));
uRXRawBufferSize = 0;
memset(cRXBuffer, 0, sizeof(cRXBuffer));
uRXBufferSize = 0;
LOGL3("TNC %u: RX beendet, RXC ist jetzt %u", uTNC, RING[uTNC].cRXC);
break;
default: break;
}
} /* if (SOF) */
switch (cByte & MASK_CMD)
{
/* TX-Underrun */
case MSG_TXU : /* TX-Underrun waehrend des Sendens des Testsignals ? */
if (RING[uTNC].cTestCount > 0)
{
RING[uTNC].cTestCount = 0;
resetLED(uTNC, LED_STA | LED_CON);
cCmdBuf[0] = cmdLED(uTNC);
toRing(cCmdBuf, 1);
}
RING[uTNC].uTXU++;
RING[uTNC].cTXC = 0;
LOGL3("TNC %u: TX-Underrun (neuer Zaehlerstand: %u)", uTNC, RING[uTNC].uTXU);
break;
/* RX-Overrun */
case MSG_RXO : RING[uTNC].uRXO++;
LOGL3("TNC %u: RX-Overrun (neuer Zaehlerstand: %u)", uTNC, RING[uTNC].uRXO);
break;
/* RX Buffer Overflow */
case MSG_RXB : RING[uTNC].uRXB++;
LOGL3("TNC %u: RX Buffer-Overflow (neuer Zaehlerstand: %u)", uTNC, RING[uTNC].uRXB);
break;
default : break;
} /* switch */
} /* if (... NORMCMD) */
}
/************************************************************************/
/* Verarbeitung von 6PACK-Prioritaetskommandos */
/************************************************************************/
void processSixpackPrioCmd(UBYTE cByte)
{
UBYTE uTNC = (cByte & MASK_TNCNUM);
/* Prioritaetsmeldungen */
if ((cByte & MASK_CALCNT) != 0)
{
LOGL4("TNC %u: Prioritaetskommando \"Zaehlung/Calibrate\" empfangen", uTNC);
/* Calibrate, Kanalzuweisung */
if ((cByte & MASK_CHAN) != 0)
{
/* Kanalzuweisung / Zaehlung */
LOGL1("TNC %u: Kanalzuweisung empfangen", uTNC);
}
else
{
UBYTE cCmdBuf[2];
/* Calibrate */
LOGL2("TNC %u: Calibrate-Paket empfangen, Calibrate-Zaehler des TNC ist %u", uTNC, RING[uTNC].cTestCount);
/* TNC meldet beendetes Calibrate, wenn noch Calibrates uebrig, dann */
/* wieder ein Kommando schicken und STA-Led einschalten */
if (RING[uTNC].cTestCount > 1)
{
RING[uTNC].cTestCount--;
setLED(uTNC, LED_STA);
cCmdBuf[0] = cmdLED(uTNC);
cCmdBuf[1] = cmdCAL(uTNC);
toRing(cCmdBuf, 2);
LOGL2("TNC %u: Calibrate-Paket gesendet, Calibrate-Zaehler des TNC ist jetzt %u", uTNC, RING[uTNC].cTestCount);
}
else
{
/* letztes Calibrate beendet, dann STA-Led ausschalten */
RING[uTNC].cTestCount = 0;
resetLED(uTNC, LED_STA);
cCmdBuf[0] = cmdLED(uTNC);
toRing(cCmdBuf, 1);
LOGL2("TNC %u: Calibrate beendet", uTNC);
}
}
}
else
{
/* DCD, RX-Zaehler, TX-Zaehler */
/* DCD immer uebernehmen */
RING[uTNC].bDCD = ((cByte & MASK_DCD) == MASK_DCD ? TRUE : FALSE);
/* RX-Zaehler + 1 */
/* Das Kommando ist nur erlaubt, wenn auch DCD gesetzt ist */
if ((cByte & MASK_RXC) == MASK_RXC)
{
if (RING[uTNC].bDCD == TRUE)
{
RING[uTNC].cRXC++;
LOGL2("TNC %u: 'RX-Zaehler + 1', RXC ist jetzt %u", uTNC, RING[uTNC].cRXC);
}
else
LOGL2("TNC %u: 'RX-Zaehler + 1' ohne DCD, ignoriere.", uTNC);
}
/* TX-Zaehler + 1 */
if ((cByte & MASK_TXC) == MASK_TXC)
{
if (RING[uTNC].cTXC > 0)
{
RING[uTNC].cTXC--;
/* noch Frames in der Sendung, dann Wachhund scharf machen */
if (RING[uTNC].cTXC > 0)
{
/* wir nehmen die Aussendung eines Frames maximaler Laenge, */
/* TX-Delay, TX-Tail und etwas Sicherheit an */
RING[uTNC].tPTTWatchdog = ( tic10
+ (2 * portpar[uMap_TNCtoPort[uTNC]].txdelay)
+ (21120 / portpar[uMap_TNCtoPort[uTNC]].speed)
);
}
else /* Aussendung beendet, Wachhund wieder anketten */
{
RING[uTNC].tPTTWatchdog = 0;
}
}
else /* keine Sendung, ist der Hund auch angekettet ? */
{
if (RING[uTNC].tPTTWatchdog != 0)
RING[uTNC].tPTTWatchdog = 0;
}
LOGL2("TNC %u: 'TX-Zaehler + 1', TXC ist jetzt %u", uTNC, RING[uTNC].cTXC);
}
/* TNC lebt noch */
RING[uTNC].tLastHeard = tic10;
}
}
/************************************************************************/
/* 6PACK-Empfang */
/************************************************************************/
void Sixpack_RX(void)
{
UBYTE cBuf[520];
LONG iRet;
register int iCounter;
register int iSelRet;
UBYTE cRXByte;
UBYTE uNumTNC;
/* ganz wichtig, Linux modifiziert in select() diese Werte !!! */
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rset);
FD_SET((unsigned int)iDescriptor, &rset);
iSelRet = select(iDescriptor + 1, &rset, NULL, NULL, &tv);
/* Immer pruefen ob daten anliegen. */
iSelRet = TRUE;
/* Fehler bei select() ? */
if (iSelRet < 0)
{
LOGL4("-RING-: Fehler %n bei select() : %s", errno, strerror(errno));
Sixpack_l1exit();
Sixpack_l1init();
return;
}
/* select() hat keinen lesbaren Descriptor gefunden, */
/* oder es ist nicht unserer */
if ((iSelRet == 0) || (!FD_ISSET(iDescriptor, &rset)))
return;
memset(cBuf, 0, sizeof(cBuf));
/* Daten holen */
iRet = read(iDescriptor, cBuf, 512);
/* Ist was gekommen ? */
if (iRet > 0)
{
/* zeichenweise Verarbeitung */
for (iCounter = 0; iCounter < iRet; ++iCounter)
{
/* Zeichen aus dem Puffer holen */
cRXByte = cBuf[iCounter];
/* Wir warten noch auf das Zaehlpaket, so lange kein anderer Empfang */
if (uRingStatus == RING_INIT)
{
/* Zaehlkommando zurueckgekommen ? */
if ((cRXByte & CMD_INIT) == CMD_INIT)
{
/* Kanalzuweisung / Zaehlung */
uNumTNC = (cRXByte - CMD_INIT);
LOGL1("-RING-: TNC-Zaehlung beendet, %u TNC gefunden", uNumTNC);
/* Ring "kurzgeschlossen", noch einmal initialisieren */
if (uNumTNC == 0)
{
uRingStatus = RING_IDLE;
LOGL3("-RING-: kein TNC im Ring, Ring ist kurzgeschlossen !");
return;
}
/* Ports mit TNC verbinden */
if (assignPorts(uNumTNC) != uNumTNC)
LOGL1("-RING-: TNC-Zaehlung weicht von konfigurierter Portanzahl ab !!!");
}
/* Falls nach zehn Sekunden kein Zaehlpaket kam, wieder eines senden */
if ((tic10 - tLastInit) >= PROBE_TIMEOUT)
{
uRingStatus = RING_IDLE;
LOGL2("-RING-: Ring laeuft nicht, reinitialisiere ...");
return;
}
continue;
}
/* Meldungen und Daten verarbeiten */
/* Prioritaetsmeldungen */
if ((cRXByte & MASK_PRIOCMD) == MASK_PRIOCMD)
{
processSixpackPrioCmd(cRXByte);
continue;
}
/* normale Meldungen */
if ((cRXByte & MASK_NORMCMD) == MASK_NORMCMD)
{
processSixpackCmd(cRXByte);
continue;
}
/* Daten */
if ((cRXByte & MASK_DATA) == 0)
{
/* Daten zum falschen Zeitpunkt ? */
if ((uRXStatus != RX_WAIT_END) || (cReceiveFrom == 0xFF))
{
LOGL3("-RING-: Daten ohne SOF (%x) !!! rxsize=%u", cRXByte, uRXRawBufferSize);
/* continue; */
}
/* In den rohen Puffer damit */
cRXRawBuffer[uRXRawBufferSize++] = cRXByte;
continue;
}
LOGL1("-RING-: Daten ohne Ziel !!! (hex 0x%x, dez %u)", cRXByte, cRXByte);
} /* for (...) */
} /* if (iRet > 0) */
else
{
/* wenn nach zehn Sekunden kein Zaehlpaket kam, dann noch eines senden */
if ( (iRet == 0)
&& (uRingStatus == RING_INIT)
&& ((tic10 - tLastInit) >= PROBE_TIMEOUT)
)
uRingStatus = RING_IDLE;
/* Schnittstellenfehler ! Das sollte NIE passieren ... */
if ((iRet < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK))
{
/* Oh oh ... alles versuchen neu zu starten ... */
LOGL2("-RING-: Fehler auf der Schnittstelle, start Ring neu !!!");
Sixpack_l1exit();
Sixpack_l1init();
}
}
}
/************************************************************************/
/* Dekodiert den gefuellten 6PACK-Rohdatenpuffer in den Nutzdatenpuffer */
/************************************************************************/
BOOLEAN DecodeRawBuffer(UBYTE uTNC)
{
register UWORD uBufferPos;
UBYTE cChecksum = uTNC;
UBYTE cTmpByte = 0;
UBYTE cBufCorrection = 0;
/* Puffer auffuellen */
while ((uRXRawBufferSize % 4) != 0)
{
++uRXRawBufferSize;
++cBufCorrection; /* Merken fuer Korrektur des dekodierten Puffers */
}
/* rohen Empfangspuffer decodieren */
for (uBufferPos = 0; uBufferPos <= uRXRawBufferSize; ++uBufferPos)
{
register UBYTE cByte = cRXRawBuffer[uBufferPos];
switch (uBufferPos % 4)
{
/* 1. 6PACK */
case 0: cTmpByte = cByte;
break;
/* 2. 6PACK */
case 1: cRXBuffer[uRXBufferSize++] = (cTmpByte | ((cByte << 2) & 0xC0));
cTmpByte = (cByte & 0x0F);
break;
/* 3. 6PACK */
case 2: cRXBuffer[uRXBufferSize++] = (cTmpByte | ((cByte << 2) & 0xF0));
cTmpByte = (cByte & 0x03);
break;
/* 4. 6PACK */
case 3: cRXBuffer[uRXBufferSize++] = (cTmpByte | (cByte << 2));
break;
default: break;
}
}
/* Checksumme ueber den dekodierten Puffer berechnen */
for (uBufferPos = 0; uBufferPos < uRXBufferSize; ++uBufferPos)
cChecksum += cRXBuffer[uBufferPos];
/* dekodierten Puffer in der Groesse korrigieren */
uRXBufferSize -= cBufCorrection;
/* Checksumme pruefen und entsprechend melden */
if (cChecksum == 0xFF)
return (TRUE);
else
{
LOGL3("TNC %u: Checksummenfehler : %u Bytes roh, %u Byte dekodiert, Checksumme %u",
uTNC, uRXRawBufferSize, uRXBufferSize, cChecksum);
return (FALSE);
}
}
/************************************************************************/
/* Sendet alle fuer einen Port anstehenden Frames zum TNC */
/************************************************************************/
void sendSixpack(UBYTE uTNC)
{
UBYTE cCheckSum;
UBYTE cRawBuffer[512];
UBYTE cSendBuffer[512];
UWORD uRawBufferSize;
UWORD uSendBufferSize;
register UWORD uCounter;
MBHEAD *txfhdl;
LHEAD *l2flp;
/* Portnummer fuer den TNC holen */
UWORD uPort = uMap_TNCtoPort[uTNC];
/* Sicher ist sicher ... */
if (kissmode(uPort) != KISS_6PACK)
return;
/* Sendeliste des Ports holen */
l2flp = (LHEAD *)&txl2fl[uPort];
/* Bei ausgeschalteten Ports die Sendeliste ohne Sendung kopieren */
if (!portenabled(uPort))
{
while (l2flp->head != l2flp)
relink(ulink((LEHEAD *) l2flp->head), (LEHEAD *)stfl.tail);
return;
}
/* So lange Sendepakete da sind, diese auf den Ring geben */
/* TNC aber nicht ueberfuellen */
while (kick[uPort])
{
/* Sendelistenzeiger pruefen */
if (l2flp->head == l2flp)
{
/* Zeiger steht auf Anfang, also doch nix zu senden */
kick[uPort] = FALSE;
return;
}
/* Puffer loeschen fuer neues Frame */
memset(cSendBuffer, 0, sizeof(cSendBuffer));
uSendBufferSize = 0;
memset(cRawBuffer, 0, sizeof(cRawBuffer));
uRawBufferSize = 0;
/* STA-LED waehrend der Datenuebertragung zum TNC einschalten */
setLED(uTNC, LED_STA);
cSendBuffer[uSendBufferSize++] = cmdLED(uTNC);
/* "TX-Zaehler + 1" und "Start of Frame" senden */
cSendBuffer[uSendBufferSize++] = cmdTXC(uTNC);
cSendBuffer[uSendBufferSize++] = cmdSOF(uTNC);
/* TX-Delay in den rohen Puffer */
cRawBuffer[uRawBufferSize++] = (UBYTE)(portpar[uPort].txdelay & 0xFF);
/* Checksumme mit der TNC-Nummer initialisieren und TX-Delay hinzufuegen */
cCheckSum = uTNC;
cCheckSum += (UBYTE)(portpar[uPort].txdelay & 0xFF);
/* Zeiger auf Sendeliste holen */
ulink((LEHEAD *)(txfhdl = (MBHEAD *)l2flp->head));
/* Rohdaten kopieren und Checksumme bilden */
while (txfhdl->mbgc < txfhdl->mbpc)
{
register UBYTE cByte = getchr(txfhdl);
cRawBuffer[uRawBufferSize++] = cByte;
cCheckSum += cByte;
}
/* Checksumme dem Puffer hinzufuegen */
cRawBuffer[uRawBufferSize] = (UBYTE)(0xFF - cCheckSum);
/* Puffer codieren */
for (uCounter = 0; uCounter <= uRawBufferSize; ++uCounter)
{
switch (uCounter % 3)
{
/* 1. Byte -> 1. & 2. 6PACK */
case 0: cSendBuffer[uSendBufferSize++] = (cRawBuffer[uCounter] & 0x3F);
cSendBuffer[uSendBufferSize] = ((cRawBuffer[uCounter] >> 2) & 0x30);
break;
/* 2. Byte -> 2. & 3. 6PACK */
case 1: cSendBuffer[uSendBufferSize++] |= (cRawBuffer[uCounter] & 0x0F);
cSendBuffer[uSendBufferSize] = ((cRawBuffer[uCounter] >> 2) & 0x3C);
break;
/* 3. Byte -> 3. & 4. 6PACK */
case 2: cSendBuffer[uSendBufferSize++] |= (cRawBuffer[uCounter] & 0x03);
cSendBuffer[uSendBufferSize++] = (cRawBuffer[uCounter] >> 2);
default: break; /* das sollte _nie_ passieren ... */
}
}
/* Sendepuffer "auffuellen" (terminiert das in Bearbeitung befindliche 6PACK) */
if ((uRawBufferSize % 3) != 2)
++uSendBufferSize;
/* abschliessendes SOF */
cSendBuffer[uSendBufferSize++] = cmdSOF(uTNC);
/* STA-LED wieder ausschalten */
resetLED(uTNC, LED_STA);
cSendBuffer[uSendBufferSize++] = cmdLED(uTNC);
/* Auf den Ring damit */
toRing(cSendBuffer, uSendBufferSize);
/* Merken, dass ein Sendeframe unterwegs ist und Wachhunde scharf machen */
RING[uTNC].cTXC++;
RING[uTNC].tTXWatchdog = tic10;
RING[uTNC].tPTTWatchdog = ( tic10
+ (2 * portpar[uMap_TNCtoPort[uTNC]].txdelay)
+ (21120 / portpar[uMap_TNCtoPort[uTNC]].speed)
);
/* Frame in die gesendet-Liste umhaengen */
relink((LEHEAD *)txfhdl, (LEHEAD *)stfl.tail);
/* ein Frame gesendet, Sendewarteschlange aktualisieren */
kick[uPort] = ((LHEAD *)l2flp->head != l2flp);
LOGL3("TNC %u: Frame an TNC gesendet, TXC ist jetzt %u", uTNC, RING[uTNC].cTXC);
} /* while (...) */
}
/************************************************************************/
/* Sendet ausstehende Frames zu den TNC, Kanalarbitrierung */
/************************************************************************/
void Sixpack_TX(void)
{
register UBYTE uTNC;
register UWORD uPort;
UBYTE cCmdBuf[1];
/* Merker, ob wir senden duerfen */
BOOLEAN bSendOK = FALSE;
for (uTNC = 0; uTNC < MAX_TNC; ++uTNC)
{
/* Nur fuer angeschlossene TNC. Da die TNC-Nummern nur ununterbrochen */
/* aufsteigend sein koennen, beim ersten nicht vorhandenen TNC raus, */
/* es kann keiner mehr danach kommen. */
if (RING[uTNC].bPresent == FALSE)
break;
/* Wenn wir senden und die TX-Watchdogzeit abgelaufen ist, */
/* dann den aktuellen LED-Zustand zum TNC senden (TX-Watchdog) */
if ( ( (RING[uTNC].cTestCount > 0) /* Testsignal */
|| (RING[uTNC].cTXC > 0) /* unbestaetigte Frames */
)
&& ((RING[uTNC].tTXWatchdog + TXWDTIME) < tic10))
{
/* aktuellen LED-Zustand als Watchdogpaket senden */
cCmdBuf[0] = cmdLED(uTNC);
toRing(cCmdBuf, 1);
/* letzten Watchdogzeitpunkt merken */
RING[uTNC].tTXWatchdog = tic10;
}
/* Port holen, der diesem TNC zugeordnet ist */
uPort = uMap_TNCtoPort[uTNC];
/* Arbiter fuer Kanalzugriff */
/* Port hat was zu senden, TNC nicht ueberfuellen */
if (kick[uPort] /* && RING[uTNC].cTXC < 5 */ )
{
LHEAD *l2flp;
LOGL4("TNC %u: Port %u ist fuer Sendung markiert und hat Platz", uTNC, uPort);
/* Sendeliste des Ports holen */
l2flp = (LHEAD *)&txl2fl[uPort];
/* Sendelistenzeiger pruefen */
if (l2flp->head == l2flp)
{
/* Zeiger steht auf Anfang, doch nix zu senden */
LOGL4("TNC %u: keine Sendedaten, eventuell Calibrate, keine Arbitrierung", uTNC);
kick[uPort] = FALSE;
return;
}
/* Arbitrierung notwendig ? */
if ((fullduplex(uPort)) || (RING[uTNC].cTXC > 0))
{
LOGL4("TNC %u: Port ist Vollduplex oder sendet bereits, sende sofort", uTNC);
bSendOK = TRUE;
}
else
{
/* DCD beruecksichtigen */
if (!(Sixpack_DCD(uPort) & (WORD)(DCDFLAG)))
{
LOGL4("TNC %u: Kanal ist frei, Slottime ist %u ms", uTNC, portpar[uPort].slottime * 10);
/* Hatten wir schon mal versucht zu senden ? (Slottime) */
if ( (RING[uTNC].tNextArbit != 0L)
&& (RING[uTNC].tNextArbit > tic10)
)
{
LOGL4("TNC %u: nicht genug Zeit seit letztem Arbit vergangen", uTNC);
}
else
{
LOGL4("TNC %u: genug Zeit seit letztem Arbit vergangen", uTNC);
/* Kanalzugriff auswuerfeln (Persistenz) */
if ((rand() % 255) <= (dama(uPort) ? 0xFF : (portpar[uPort].persistance & 0xFF)))
{
LOGL4("TNC %u: Zufallszahl kleiner, Sendung moeglich", uTNC);
bSendOK = TRUE;
}
else
{
LOGL4("TNC %u: Zufallszahl zu gross, keine Sendung", uTNC);
/* Merken, wann wir es zuletzt versucht hatten zu senden, */
/* naechster Versuch erst nach Ablauf der Slottime. */
RING[uTNC].tNextArbit = (tic10 + portpar[uPort].slottime);
}
}
}
else
{
LOGL4("TNC %u: DCD erkannt, kann nicht mehr senden", uTNC);
/* Waren wir selbst grad in der Arbitrierung und wurden */
/* durch die RX-DCD unterbrochen, dann Sendeversuch abbrechen. */
if (RING[uTNC].tNextArbit != 0L)
{
RING[uTNC].tNextArbit = 0L;
LOGL4("TNC %u: laufende Arbitrierung abgebrochen", uTNC);
}
}
}
if (bSendOK == TRUE)
{
/* Kanal frei und Sendung ok */
LOGL4("TNC %u: Arbitrierung, sende !", uTNC);
sendSixpack(uTNC);
/* wir konnten Senden */
RING[uTNC].tNextArbit = 0L;
}
else
{
/* Kanal blockiert, nicht senden */
LOGL4("TNC %u: Arbitrierung nicht erfolgreich", uTNC);
}
} /* if ( kick[uPort] ... ) */
} /* for ( ... ) */
}
/************************************************************************/
/* TNCs im Ring zaehlen / Zaehlpaket senden */
/************************************************************************/
void startCount(void)
{
UBYTE cCmdBuf[1];
/* Sicherheitscheck, ob wir die Zuordnungen jetzt aendern duerfen */
if (uRingStatus != RING_IDLE)
return;
/* Zaehlkommando zusammenbauen und senden */
cCmdBuf[0] = cmdINIT(0);
toRing(cCmdBuf, 1);
/* Zeitpunkt und Sendung merken */
tLastInit = tic10;
++uNumProbes;
/* Merken, dass wir auf das Zaehlpaket warten */
uRingStatus = RING_INIT;
LOGL4("-RING-: Zaehlpaket gesendet");
}
/************************************************************************/
/* Datenpuffer auf den Ring senden */
/* (basierend auf writen() aus einem Stevens-Buch) */
/************************************************************************/
void toRing(UBYTE *pBuffer, UWORD uSize)
{
UWORD uErrorCounter = 0;
size_t tLeft = uSize;
LONG tWritten;
const UBYTE *pBufPtr;
pBufPtr = pBuffer;
/* nur wenn der Ring offen ist duerfen wir hier rein */
if (uRingStatus == RING_HALT)
return;
/* solange noch Daten zu senden sind */
while (tLeft > 0)
{
if ((tWritten = write(iDescriptor, (void *)pBufPtr, tLeft)) <= 0)
{
if (errno == EINTR)
tWritten = 0;
else
{
if (++uErrorCounter >= 0xFF)
{
LOGL3("-RING-: Schnittstellenfehler %u (%s)", errno, strerror(errno));
Sixpack_l1exit();
Sixpack_l1init();
return;
}
}
}
tLeft -= tWritten;
pBufPtr += tWritten;
}
}
/************************************************************************/
/* Ordnet jedem 6PACK-Port einen freien TNC-Port */
/************************************************************************/
UBYTE assignPorts(UBYTE uAvailTNC)
{
register UBYTE i;
register UBYTE j = 0;
UBYTE cCmdBuf[2];
/* Sicherheitscheck, ob wir die Zuordnungen jetzt aendern duerfen */
if (uRingStatus != RING_INIT)
return (0);
/* zuerst alle TNC als nicht vorhanden markieren */
for (i = 0; i < MAX_TNC; ++i)
{
RING[i].bPresent = FALSE;
uMap_TNCtoPort[i] = 0;
}
/* und das Gleiche mit den Ports machen */
for (i = 0; i < L2PNUM; ++i)
uMap_PorttoTNC[i] = 0xFF; /* kein TNC */
/* Alle L2-Ports durchgehen */
for (i = 0; (i < L2PNUM && j < uAvailTNC); ++i)
{
/* andere Ports nicht beachten */
if (kissmode(i) != KISS_6PACK)
continue;
LOGL2("-RING-: Assoziiere TNC %u mit Port %u", j, i);
/* TNC mit Port verbinden */
RING[j].bPresent = TRUE;
RING[j].tWatchdog = tic10;
RING[j].tLastHeard = tic10;
RING[j].tPTTWatchdog = 0;
uMap_PorttoTNC[i] = j;
uMap_TNCtoPort[j] = i;
/* bei gefundenen TNC die CON-LED einschalten */
resetLED(j, LED_STA);
setLED(j, LED_CON);
cCmdBuf[0] = cmdLED(j);
resetLED(j, LED_CON);
toRing(cCmdBuf, 1);
++j;
}
/* Empfang und initialisieren */
cReceiveFrom = 0xFF;
uRXStatus = RX_WAIT_SOF;
/* Ring freigeben fuer Datenverkehr */
uRingStatus = RING_RUN;
/* melden, wie viele Zuordnungen gemacht worden sind */
return (j);
}
/************************************************************************/
/* L1-Kontrolle (Reset, Portparameter etc.) */
/************************************************************************/
void Sixpack_l1ctl(int iReq, UWORD uPort)
{
UBYTE cCmdBuf[2];
register UBYTE uTNC = uMap_PorttoTNC[uPort];
/* nur 6PACK-Ports und davon auch nur die existierenden duerfen hier rein */
if ( (kissmode(uPort) != KISS_6PACK)
|| (RING[uTNC].bPresent == FALSE)
)
return;
/* Anfragen unterscheiden */
switch (iReq)
{
/* Reset eines TNC. Das geht bei 6PACK nicht direkt, also muss der ganze */
/* Ring dran glauben ... */
case L1CRES : RING[uTNC].uReset++;
/* den Ring neu initialiseren */
uRingStatus = RING_IDLE;
LOGL2("TNC %u: TNC-Reset (neuer Zaehlerstand: %u)", uTNC, RING[uTNC].uReset);
break;
/* Testsignal initiieren (4 x 15 Sekunden) */
case L1CTST : RING[uTNC].cTestCount = 4;
setLED(uTNC, LED_STA);
cCmdBuf[0] = cmdCAL(uTNC);
cCmdBuf[1] = cmdLED(uTNC);
toRing(cCmdBuf, 2);
LOGL2("TNC %u: Testsignal initiiert", uTNC);
break;
/* Portparameter zum TNC bringen, das braucht 6PACK nicht, */
/* alles ausser TX-Delay machen wir selbst. */
case L1CCMD :
default : break;
}
}
/************************************************************************/
/* Device einrichten */
/************************************************************************/
void Sixpack_l1init(void)
{
register UWORD i;
UWORD uFehler = 0;
DEVICE *l1pp;
BOOLEAN bPortFound = FALSE;
/* Zufallsgenerator initialisieren */
srand(0);
/* Erstes 6PACK-Device suchen fuer Lockfile */
/* Alle L1-Ports durchgehen */
for (i = 0; i < L1PNUM; ++i)
{
l1pp = &l1port[i];
if (l1pp->kisstype == KISS_6PACK)
{
bPortFound = TRUE;
break;
}
}
/* haben wir einen Port gefunden ? */
if (bPortFound == FALSE)
return;
/* Lock-File schreiben */
l1pp->lock = -1;
/* Seriellen Port fuer Ein- und Ausgabe oeffnen */
if (uFehler == 0) /* nur wenn Lock-File geschrieben */
{ /* wurde oder nicht gefordert war */
if ((iDescriptor = open_win(l1pp->device, l1pp->speed)) == -1)
{
uFehler = 2;
printf("Error: can't open device %s\n", l1pp->device);
printf(" (%s)\n", strerror(errno));
LOGL1("Error: can't open device %s", l1pp->device);
LOGL1(" (%s)", strerror(errno));
}
}
/* Serielle Schnittstelle auf neue Parameter einstellen */
if (uFehler == 0)
{
l1pp->port_active = TRUE;
/* Ring ist nun offen, aber noch nicht initialisiert */
uRingStatus = RING_IDLE;
LOGL1("-RING-: Port erfolgreich geoeffnet");
return;
}
/* Port war schon offen, aber noch nicht veraendert, nur schliessen */
if (uFehler > 2)
{
CloseHandle((void *)iDescriptor);
iDescriptor = -1;
}
/* Port war nicht offen, aber es existiert ein Lockfile, schliessen */
if (uFehler > 1)
{
if (l1pp->lock != -1)
{
close(l1pp->lock);
l1pp->lock = -1;
}
}
/* Lockfile loeschen */
if ((*(l1pp->tnn_lockfile) != '\0') && (l1pp->lock != -1))
unlink(l1port[i].tnn_lockfile);
LOGL1("-RING-: Port %s konnte nicht geoeffnet werden !", l1pp->device);
}
/************************************************************************/
/* Device schliessen */
/************************************************************************/
void Sixpack_l1exit(void)
{
register UWORD i;
/* Erstes 6PACK-Device suchen */
/* Alle L1-Ports durchgehen */
for (i = 0; i < L1PNUM; ++i)
{
if (l1port[i].kisstype == KISS_6PACK)
break;
}
/* Keinen 6PACK-Port gefunden ? */
if (i == L1PNUM)
return;
/* Ring wird angehalten */
uRingStatus = RING_HALT;
/* Serieller Port offen ? Dann schliessen */
if (iDescriptor != -1)
{
/* Port schliessen */
CloseHandle((void *)iDescriptor);
iDescriptor = -1;
}
else
return;
/* Lockfile gesetzt ? */
if (*(l1port[i].tnn_lockfile) != '\0')
{
/* Lockfile offen ? Dann schliessen und loeschen */
if (l1port[i].lock != -1)
{
close(l1port[i].lock);
l1port[i].lock = -1;
unlink(l1port[i].tnn_lockfile);
}
}
/* Port nicht mehr aktiv */
l1port[i].port_active = FALSE;
LOGL1("-RING-: Port geschlossen");
}
/************************************************************************/
/* Anzeige der TNC-Statistiken */
/************************************************************************/
void ccp6pack(void)
{
MBHEAD *mbp;
register UBYTE uTNC;
register UBYTE i;
register UBYTE uNumPorts = 0;
ULONG uUpDays = (tic10 - tLastInit) / 100; /* eigentlich sinds hier noch Sekunden ... */
ULONG uUpHours, uUpMinutes, uUpSeconds;
#define BUFLEN 32
char cBuf[BUFLEN + 1];
/* Alle L2-Ports durchgehen */
for (i = 0; i < L2PNUM; ++i)
if (kissmode(i) == KISS_6PACK)
++uNumPorts;
/* ueberhaupt ein Port da ? */
if (uNumPorts == 0)
{
mbp = putals("No 6PACK-Ports configured.");
/* und ab die Post ... */
putprintf(mbp, "\r");
prompt(mbp);
seteom(mbp);
return;
}
/* Sysop will aendern und noch was in der Zeile da ? */
if (issyso() && skipsp(&clicnt, &clipoi))
{
/* Frischer Buffer */
memset(cBuf, 0, sizeof(cBuf));
/* Operation lesen (add, delete) */
for (i = 0; i < BUFLEN; ++i)
{
if ((!clicnt) || (*clipoi == ' '))
break;
clicnt--;
cBuf[i] = toupper(*clipoi++);
}
/* Hinzufuegen (add oder +) */
if ( (strcmp(&cBuf[0], "LOGLEVEL") == 0)
|| (strcmp(&cBuf[0], "LOG") == 0)
)
{
WORD uNewLevel;
/* Noch was da in der Kommandozeile ? */
if (!skipsp(&clicnt, &clipoi))
{
mbp = getmbp();
putprintf(mbp, "6PACK's actual loglevel is %u\r", uLogLevel);
/* und ab die Post ... */
prompt(mbp);
seteom(mbp);
return;
}
/* neues Loglevel lesen */
uNewLevel = nxtnum(&clicnt, &clipoi);
if (((int)uNewLevel < 0) || (uNewLevel > 4))
{
putmsg("error: loglevel out of range (0-4) !!!\r");
return;
}
/* Wert uebernehmen */
SixpackLog("-RING-: Loglevel %u -> %u", uLogLevel, uNewLevel);
uLogLevel = (UWORD)uNewLevel;
/* und ab die Post ... */
mbp = getmbp();
prompt(mbp);
seteom(mbp);
return;
}
}
mbp = putals("6PACK Error-Statistics:\r");
putprintf(mbp, " TX- RX- RX-Buffer-\r");
putprintf(mbp, "-TNC#-Port#-Underrun-Overrun--Overflow--Checksum-Resets-Cal-RXC-TXC-LHeard[ms]\r");
/* alle TNC durchgehen */
for (uTNC = 0; uTNC < MAX_TNC; ++uTNC)
{
/* nicht vorhandene TNC */
if (RING[uTNC].bPresent == FALSE)
{
putprintf(mbp, " %u N/A - - - - - - - - -\r", uTNC);
continue;
}
/* vorhandene TNC */
putprintf(mbp, " %u %2u %5u %5u %5u %5u %5u %1u %2u %2u %5u\r",
uTNC, /* TNC */
uMap_TNCtoPort[uTNC], /* Port */
RING[uTNC].uTXU, /* TX-Underrun */
RING[uTNC].uRXO, /* RX-Overrun */
RING[uTNC].uRXB, /* RX-Buffer Overflow */
RING[uTNC].uChecksumErr, /* Anz. Checksummenfehler */
RING[uTNC].uReset, /* Anz. Resets */
RING[uTNC].cTestCount, /* Anz. Calibrates */
RING[uTNC].cRXC, /* RX-Zaehler */
RING[uTNC].cTXC, /* TX-Zaehler */
((tic10 - RING[uTNC].tLastHeard) * 10)); /* LastHeard */
} /* for (...) */
putprintf(mbp, "\rRing is ");
switch (uRingStatus)
{
case RING_HALT : putprintf(mbp, "halted due to unrecoverable errors ! Please restart.\r");
break;
case RING_IDLE : putprintf(mbp, "waiting for TNC-initialization.\r");
break;
case RING_INIT : putprintf(mbp, "awaiting the return of the probe packet.\r");
putprintf(mbp, "(%u probes sent so far, last probe sent %u ms ago)\r", uNumProbes, ((tic10 - tLastInit) * 10) );
break;
case RING_RUN : putprintf(mbp, "up and running for");
uUpSeconds = uUpDays % 60L;
uUpDays /= 60L;
uUpMinutes = uUpDays % 60L;
uUpDays /= 60L;
uUpHours = uUpDays % 24L;
uUpDays /= 24L;
if (uUpDays < 1L)
putprintf(mbp, " %2lu:%02lu:%02lu\r", uUpHours, uUpMinutes, uUpSeconds); /* hh:mm:ss */
else
{
if (uUpDays < 99L)
putprintf(mbp, " %2lu/%02lu:%02lu\r", uUpDays, uUpHours, uUpMinutes); /* dd/hh:mm */
else
putprintf(mbp, " %5lu/%02lu\r", uUpDays, uUpHours); /* ddddd/hh */
}
break;
default: putprintf(mbp, "an undefined state !!! You really shouldn't see this ... Please restart.\r"); break;
}
/* und ab die Post ... */
putprintf(mbp, "\r");
prompt(mbp);
seteom(mbp);
}
/************************************************************************/
/* Log-Funktion zum Debugging */
/************************************************************************/
void SixpackLog(const char *format, ...)
{
FILE *fp;
va_list arg_ptr;
struct timeval tv;
static char str[30];
char *ptr;
fp = fopen("6pack.log", "a");
if (fp != NULL)
{
gettimeofday(&tv, NULL);
ptr = ctime(&tv.tv_sec);
strcpy(str, &ptr[11]);
fprintf(fp, "%s:", str);
va_start(arg_ptr, format);
vfprintf(fp, format, arg_ptr);
va_end(arg_ptr);
fprintf(fp, "\n");
fclose(fp);
}
else
{
/* Fehler beim Schreiben in die Logdatei, dann das Logging beenden */
uLogLevel = 0;
}
}
/**************************************************************************/
#endif
/* End of os/win32/6pack.c */