TheNetNode-CB/os/go32/l1.c

582 lines
22 KiB
C
Executable File

/************************************************************************/
/* */
/* ***** ***** */
/* ***** ***** */
/* ***** ***** */
/* ***** ***** */
/* *************** *************** */
/* ***************** ***************** */
/* *************** *************** */
/* ***** ***** TheNetNode */
/* ***** ***** Portable */
/* ***** ***** Network */
/* ***** ***** Software */
/* */
/* File os/go32/l1.c (maintained by: ???) */
/* */
/* 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"
#include "hardware.h" /* Hardwaredefinition */
#include "pc.h"
/* Die Tokenring-Variablen befinden sich jetzt hier, damit oberhalb des */
/* L1 keine Defines mehr notwendig sind. Dies ist ein vertretbarer */
/* Overhead, wenn man keinen Tokenring hat. */
int tkcom = -1; /* Tokenring-Schnittstelle */
UWORD tkbaud = 96; /* Baudrate Schnittstelle */
LONG rounds_pro_sec = 0; /* Anzahl Rounds/Sekunde */
LONG rounds_max_sec = 0; /* Maximale Anzahl */
LONG rounds_min_sec = 0; /* Minimale Anzahl */
LONG rounds_count = 0; /* Zaehler fuer Rounds */
static ULONG cd_timer[L2PNUM]; /* 0 = Frame wurde gesendet.. */
/* != 0 Frame an TNC ubergeben, wird */
/* bearbeitet bzw. Sendung laeuft. */
#define CD_TIMEOUT 3000 /* 30Sekunden Timeout, falls Bestaetigung*/
/* verloren gegangen ist (Tokenring) */
#define BLOCKSIZE 2048 /* maximale Framegroesse */
static char *blkbuf; /* Buffer fuer Blocktransfer */
#define TX_CHAR(x) *out++ = x /* ein Zeichen in den Puffer */
#define TX_BEG(); out = blkbuf; /* den Puffer initialisieren */
void register_majors(void);
/* Tokenring und Kisslink teilen sich diese Definitionen, deshalb */
/* stehen die auch gleich hier. */
#define FEND 0xC0 /* Kiss Frame Ende */
#define FESC 0xDB /* Kiss Frame Escape */
#define TFEND 0xDC /* transponiertes Frame Ende */
#define TFESC 0xDD /* transponiertes Escape */
#define WFEND 0 /* warten auf FEND */
#define GPORT 1 /* Portnummer lesen */
#define GTYPE 2 /* Daten oder Parameterframe? */
#define GFRAM 3 /* Frame empfangen */
#define GFRMT 4 /* TFESC gesendet, Byte einfuegen */
#define TOKEN 5 /* Token empfangen */
#define TOKN2 6 /* Token gueltig, Daten und neues Token */
#define RESET 7 /* reset empfangen */
#define DAMAP 8 /* DAMA PTT empfangen */
/* Geraeteverwaltung major/minor */
/* Jedem MAC-Treiber wird dynamisch eine major-Nummer zugeteilt. Wenn es */
/* zwischen weiteren minors (Untergeraete) unterscheiden moechte, muss */
/* eine eigene l1istome-Routine diese aus dem String auslesen (z.B. */
/* KISS3). Die Default-Routine vergleicht nur den Namen (z.B. VANESSA). */
/* Wenn keine minors explizit verwaltet werden, findet ein direktes */
/* Mapping Port->Minor statt. */
static MAJOR majortab[MAX_MAJOR+1]; /* Tabelle der MAC-Treiber */
static int num_major = 0;
/* Default-Handler. Diese koennen die MAC-Treiber-Handler ersetzen oder */
/* ergaenzen, indem der Aufruf durchgereicht wird. */
/* Geraet-Initialisierung. */
/* Das Geraet und alle Minors werden vorinitialisiert. Diese Funktion */
/* wird beim Systemstart nach erfolgreicher registrierung aufgerufen. */
static void default_l1init(void)
{
}
/* Geraet-Deinitialisierung. */
/* Beim Runterfahren des Systems wird diese Routine aufgerufen. */
#define default_l1exit default_l1init
/* Geraet-Behandlung */
/* Hier koennen staendige Aufgaben erledigt werden, z.B. die Interrupt- */
/* Buffer leeren und Frames an den L2 schicken. Nur hier darf auf L1- */
/* Strukturen zugegriffen werden, nie aus dem Interrupt. */
#define default_l1handle default_l1init
/* Geraet-Konfiguration */
/* Hier werden Konfigurations/Test/Reset-Anfragen bearbeit. */
static void
default_l1ctl(int req, int port)
{
PORTINFO *p = &portpar[port];
if (p->reset_port)
portstat[port].reset_count++;
p->reset_port =
commandflag[port] =
testflag[port] = FALSE;
}
/* Geraet-DCD */
/* Wenn moeglich sollte das Geraet hier seinen tatsaechlichen DCD-Status */
/* liefern. Es duerfen keine Interpretationen (z.B. Vollduplex) geschehen,*/
/* das macht der L2 selbst. */
static WORD default_l1dcd(PORTINFO *port)
{
return(0);
}
/* Feststellen, ob wir mit dem L1-MAC-Namen gemeint sind. Es wird */
/* immer minor 0 geliefert (nicht geeignet fuer KISS usw). */
static int default_l1istome(int major, char *devname)
{
if (strnicmp(majortab[major].name, devname, strlen(devname))==0)
return(0);
return(NO_MINOR);
}
/* Geraet-Timerbehandlung. */
static void default_l1timer(UWORD ticks)
{
}
/* Geraet anschliessen */
/* Ein Geraet und Minor an einen Port anschliessen. Dies muss */
/* nicht immer Erfolg haben. Der Minor wurde mittels dem jeweiligen */
/* l1istome ermittelt. */
/* Ist checkonly TRUE, dann soll nur geprueft werden, ob ein Eintrag */
/* moeglich WAERE, aber NICHTS ausgefuehrt werden. */
static int default_l1attach(int port, int unused_minor, BOOLEAN unused_checkonly)
{
return(1);
}
/* Geraet-Abschalten */
/* Einen Port abschalten, wenn das Geraet eine Trennung zulaesst. */
static int default_l1detach(int unused_port)
{
return(1);
}
/* Geraet-Informationen */
/* what=HW_INF_IDENT sollte der Geraetename geliefert werden. Wenn das */
/* Geraet mehrere hat (z.B. KISS/SMACK/RKISS), soll es */
/* den aktuellen liefern (benutzt von SPARAM). */
/* what=HW_INF_INFO sollte max. 10 Zeichen Geraetekonfiguration liefern, */
/* im Normalfalle auch der Name, eventuell mit etwas */
/* mehr Info. */
/* what=... sollte spaeter mal hsbus_stat() u.ae. ersetzen */
static void
default_l1info(int what, int port, MBHEAD *mbp)
{
switch (what)
{
case HW_INF_IDENT :
case HW_INF_INFO :
putstr(majortab[portpar[port].major].name, mbp);
}
}
/* MAC-Treiber registrieren. Es werden die default-Handler eingetragen, */
/* nur die benoetigten Routinen muessen vom Treiber ersetzt werden. Jeder */
/* Treiber sollte vor der Registrierung pruefen, ob ueberhaupt ein Geraet */
/* im System vorhanden ist und sich ggfs garnicht installieren. Dies */
/* beschleunigt den allgemeinen Ablauf im L1. */
static MAJOR *register_major(void)
{
MAJOR *m;
if (num_major > MAX_MAJOR)
return(NULL);
m = &majortab[++num_major];
m->istome = default_l1istome;
m->init = default_l1init;
m->exit = default_l1exit;
m->handle = default_l1handle;
m->ctl = default_l1ctl;
m->dcd = default_l1dcd;
m->attach = default_l1attach;
m->detach = default_l1detach;
m->info = default_l1info;
m->timer = default_l1timer;
return(m);
}
/* Einen Port mit einem MAC-Treiber verbinden. Falls der Port bereits */
/* belegt ist, wird vorher l1detach() aufgerufen. Dieser darf aber auch */
/* fehlschlagen (wenn der Port nicht umkonfigurierbar ist, z.B. Host- */
/* terminal). Der Geraetetreiber setzt nur seinen minor, den major */
/* setzt l1attach selbst (der Geraetetreiber kennt eigentlich seinen */
/* major nicht). */
int l1attach(int port, char *devname)
{
int major;
int minor;
PORTINFO *p = &portpar[port];
/* als erstes suchen wir einen passenden major */
for (major = 1; major <= num_major; major++)
if ((minor = majortab[major].istome(major, devname)) != NO_MINOR)
break;
if (major > num_major)
return(0); /* Geraet unbekannt */
if (!majortab[major].attach(port, minor, TRUE/*check only*/))
return(0); /* erstmal nur antesten */
/* hier angekommen ist das neue Geraet bereit, ein eventuell altes */
/* schalten wir aus. */
if (p->major != NO_MAJOR)
l1detach(port);
if (p->major == NO_MAJOR) { /* nur wenn Abschaltung erfolgreich war */
if (majortab[major].attach(port, minor, FALSE/*attach*/)) {
p->major = major;
return(1);
}
}
return(0); /* Port belegt / nicht belegbar */
}
/* Einen Port von einem MAC-Treiber trennen. Dies darf fehlschlagen, */
/* wenn z.B. der L1 anderweitig konfiguriert wurde (Linux). */
void l1detach(int port)
{
int major;
MBHEAD *mbp;
LHEAD *mlp;
major = portpar[port].major;
if (major != NO_MAJOR)
if (majortab[major].detach(port))
portpar[port].major = NO_MAJOR;
if (portpar[port].major == NO_MAJOR) {
mlp = &txl2fl[port];
while ((LHEAD *)mlp->head != mlp) {
mbp = (MBHEAD *)ulink((LEHEAD *)mlp->head);
relink((LEHEAD *)mbp, (LEHEAD *)stfl.tail);
}
kick[port] = FALSE;
}
}
/************************************************************************/
/* Initialisierung der Variablen und der Hardware */
/************************************************************************/
void l1init(void)
{
MAJOR *m;
int i;
PORTINFO *p;
for (i=0, p = portpar; i<L2PNUM; i++, p++)
{
p->reset_port = FALSE; /* Tnx an Odo */
p->speed = 12;
p->major = NO_MAJOR;
p->minor = -1;
cd_timer[i] = 0;
kick[i] = FALSE; /* kein TNC sendet */
testflag[i] = FALSE;
commandflag[i] = FALSE;
}
blkbuf = malloc(BLOCKSIZE);
if (blkbuf == NULL) memerr();
show_recovery = TRUE;
register_majors();
for (m = &majortab[1]; m <= &majortab[num_major]; m++)
m->init();
l1sclr("*");
}
/************************************************************************/
/* Layer 1 deinitialisieren */
/*----------------------------------------------------------------------*/
void l1exit (void)
{
MAJOR *m;
for (m = &majortab[1]; m <= &majortab[num_major]; m++)
m->exit();
}
/************************************************************************/
/* Timer-Behandlung fuer den L1 */
/* wird aus timsrv() herraus aufgerufen */
/*----------------------------------------------------------------------*/
void l1timr(UWORD ticks)
{
MAJOR *m;
for (m = &majortab[1]; m <= &majortab[num_major]; m++)
m->timer(ticks);
}
/************************************************************************/
/* Level1 RX/TX */
/* wird staendig in der main() Hauptschleife aufgerufen. */
/*----------------------------------------------------------------------*/
void l1rxtx(void)
{
MAJOR *m;
int major;
rounds_count++; /* Anzahl der Hauptschleifendurchlaeufe */
for (m = &majortab[major = 1]; major <= num_major; major++, m++)
m->handle();
}
/************************************************************************/
/* Markieren, dass fuer TNC #port Frames zum Senden vorliegen */
/*----------------------------------------------------------------------*/
void kicktx(int port)
{
kick[port] = TRUE;
}
/************************************************************************/
/* */
/* iscd(): Carrier Detect */
/* */
/* Ueberpruefung des Kanalstatus fuer einen Port. Wenn auf einem */
/* Kanal der Sender getastet ist, wird 2 geliefert. Wenn auf dem */
/* Kanal Daten empfangen werden, wird 1 geliefert. 0 wird geliefert, */
/* wenn der Kanal frei ist, oder auf Vollduplex geschaltet ist. */
/* */
/* Hinweis: Einige Geraete unterstuetzen diese Funktion nur ungenau */
/* (z.B. Tokenring) */
/* */
/*----------------------------------------------------------------------*/
WORD iscd(int port)
{
int state = 0;
int major;
PORTINFO *p;
p = portpar+port;
major = p->major;
if (major != NO_MAJOR) {
if ((LHEAD *)txl2fl[port].head != &txl2fl[port])
state |= TXBFLAG;
return(state | majortab[major].dcd(p));
}
return(state);
}
/************************************************************************/
/* Level1 Control */
/* eine Aktion fuer einen Hardware-Port nachfragen */
/*----------------------------------------------------------------------*/
void l1ctl(int req, int port)
{
int major;
switch(req) {
case L1CRES: portpar[port].reset_port = TRUE;
break;
case L1CCMD: commandflag[port] = TRUE;
break;
case L1CTST: testflag[port] = TRUE;
}
major = portpar[port].major;
if (major != NO_MAJOR)
majortab[major].ctl(req, port);
}
/* Infostring fuer den Port-Befehl zusammenbauen. */
void l1hwstr(int port, MBHEAD *mbp)
{
if (portpar[port].major == NO_MAJOR)
putstr("OFF", mbp);
else
majortab[portpar[port].major].info(HW_INF_INFO, port, mbp);
}
/* Infostring fuer SAVEPARM zusammenbauen. */
void l1hwcfg(int port, MBHEAD *mbp)
{
if (portpar[port].major == NO_MAJOR)
putstr("OFF", mbp);
else
majortab[portpar[port].major].info(HW_INF_IDENT, port, mbp);
}
/*
* Blocktransferroutinen fuer den Level 1
* Message-Buffer koennen Blockweise in einen linearen Buffer umgewandelt
* werden und wieder zurueck. Dies darf aber so ohne weiteres nur im
* Level 1 erfolgen, da der Buffer zurueckgespult wird.
*/
static int cpymbflat(char *buf, MBHEAD *fbp) {
MB *bp;
LHEAD *llp = &fbp->mbl; /* Zeiger auf den Listenkopf */
int i = fbp->mbpc; /* Anzahl der Bytes im Frame */
for (bp = (MB *)llp->head; bp != (MB *)llp;
bp = bp->nextmb, i -= sizeof_MBDATA, buf += sizeof_MBDATA)
memcpy(buf, bp->data, sizeof_MBDATA);
return(fbp->mbpc);
}
static MBHEAD *cpyflatmb(char *buf, int size) {
MBHEAD *mbhd;
MB *bp;
LHEAD *llp;
mbhd = (MBHEAD *)allocb(ALLOC_MBHEAD); /* einen Buffer fuer den Kopf */
mbhd->mbpc = size; /* soviel wird mal drinstehen */
llp = &mbhd->mbl; /* Zeiger auf den Listenkopf */
for ( ; size > 0; size -= sizeof_MBDATA, buf += sizeof_MBDATA) {
memcpy((bp = (MB *)allocb(ALLOC_MB))->data, buf, sizeof_MBDATA);
relink((LEHEAD *)bp, (LEHEAD *)llp->tail);
}
rwndmb(mbhd); /* mbbp richtig setzen */
return(mbhd);
}
#ifdef VANESSA
#include "vanessa.c"
#endif
#ifdef EXTDEV
#include "extdev.c"
#endif
#ifdef TOKENRING
#include "tokenrng.c"
#endif
#ifdef COMKISS
#include "kiss.c"
#endif
#ifdef SCC
#include "scc.c"
#endif
#ifdef PAR96
#include "par.c"
#endif
#include "loop.c"
#ifdef TCP_STACK
#include "tcp.c"
#endif /* TCP_STACK */
/* Geraetetabelle. */
/* In dieser Tabelle werden alle Geraete registriert. Bezuege aus L1.C */
/* auf Variablen der Treiber sind verboten. */
/* Die Reihenfolge hier bestimmt auch die Reihenfolge bei der STAT- */
/* Ausgabe. */
DEVTABLE devtable[] =
{
REGISTER_DEVICE("LOOPBACK", register_loopback),
#ifdef TOKENRING
REGISTER_DEVICE("TOKENRING", register_tokenring),
#endif
#ifdef COMKISS
REGISTER_DEVICE("KISS", register_kiss),
#endif
#ifdef VANESSA
REGISTER_DEVICE("VANESSA", register_vanessa),
#endif
#ifdef EXTDEV
REGISTER_DEVICE("EXTDEV", register_extdev),
#endif
#ifdef SCC
REGISTER_DEVICE("SCC", register_scc),
#endif
#ifdef PAR96
REGISTER_DEVICE("PAR96", register_par),
#endif
#ifdef TCP_STACK
REGISTER_DEVICE("TCPIP", RegisterTCP),
#endif /* TCP_STACK */
REGISTER_DEVICE(NULL, NULL)
};
/* Hier werden alle majors nacheinander gebeten, sich zu registrieren. */
/* Sie muessen es aber nicht, falls z.B. keine Schnittstellen frei oder */
/* vorhanden sind. */
void register_majors(void)
{
DEVTABLE *t;
for (t = devtable; t->reg_func; t++)
t->major = t->reg_func();
}
/* Aufzaehlen der vorhandenen Layer 1 Geraete. Dies sind nicht die */
/* tatsaechlich installierten, sondern die compilierten. */
void l1enum(MBHEAD *mbp)
{
DEVTABLE *t;
for (t = devtable; t->reg_func; t++) {
putstr(t->major ? " *" : " ", mbp);
putstr(t->name, mbp);
}
}
/* Information zu einem Geraet abrufen (Statistik). */
void l1stat(const char *devname, MBHEAD *mbp)
{
DEVTABLE *t;
for (t = devtable; t->reg_func; t++)
if (strcmp(t->name, devname) == 0 || *devname == '*') /* Name stimmt? */
if (t->major)
majortab[t->major].info(HW_INF_STAT, 0, mbp);
}
/* Portstatistik loeschen */
void l1sclr(const char *devname)
{
DEVTABLE *t;
for (t = devtable; t->reg_func; t++)
if (strcmp(t->name, devname) == 0 || *devname == '*') /* Name stimmt? */
if (t->major)
majortab[t->major].info(HW_INF_CLEAR, 0, NULL);
}
/* End of os/go32/l1.c */