610 lines
23 KiB
C
Executable file
610 lines
23 KiB
C
Executable file
/************************************************************************/
|
|
/* */
|
|
/* ***** ***** */
|
|
/* ***** ***** */
|
|
/* ***** ***** */
|
|
/* ***** ***** */
|
|
/* *************** *************** */
|
|
/* ***************** ***************** */
|
|
/* *************** *************** */
|
|
/* ***** ***** TheNetNode */
|
|
/* ***** ***** Portable */
|
|
/* ***** ***** Network */
|
|
/* ***** ***** Software */
|
|
/* */
|
|
/* File os/go32/16550.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 "pc.h"
|
|
|
|
#define TRX (base+0)
|
|
#define IER (base+1) /* interrupt Enable register */
|
|
#define IIR (base+2) /* Interrupt ident. register */
|
|
#define FCR (base+2) /* Fifo Control register */
|
|
#define LCR (base+3) /* Line Control register */
|
|
#define MCR (base+4) /* Modem Control register */
|
|
#define LSR (base+5) /* Line Status register */
|
|
#define MSR (base+6) /* Modem Status register */
|
|
|
|
typedef struct {
|
|
unsigned irq;
|
|
unsigned base;
|
|
unsigned oldmask;
|
|
unsigned in_buffersize;
|
|
char *in_buffer;
|
|
unsigned in_buffer_in;
|
|
unsigned in_buffer_out;
|
|
unsigned out_buffersize;
|
|
char *out_buffer;
|
|
unsigned out_buffer_in;
|
|
unsigned out_buffer_out;
|
|
int out_aktiv;
|
|
int is_16550;
|
|
int active;
|
|
} __com_struct;
|
|
|
|
__com_struct asydev[MAXCOMS];
|
|
|
|
/**********************************************************************/
|
|
/* Aus dem Enviroment die Konfiguration fuer eine Schnittstelle lesen */
|
|
/**********************************************************************/
|
|
void read_envcom(char *what, int *dev, int *adr, int *irq)
|
|
{
|
|
*dev = 0;
|
|
*adr = 0;
|
|
*irq = 0;
|
|
|
|
if (getenv(what) != NULL)
|
|
sscanf(getenv(what), "%u,%x,%u", dev, adr, irq);
|
|
|
|
if (*adr == 0)
|
|
{
|
|
switch (*dev)
|
|
{
|
|
case 1 : *adr = 0x3f8; break;
|
|
case 2 : *adr = 0x2f8; break;
|
|
case 3 : *adr = 0x3e8; break;
|
|
case 4 : *adr = 0x2e8; break;
|
|
}
|
|
}
|
|
|
|
if (*irq == 0)
|
|
{
|
|
switch (*dev)
|
|
{
|
|
case 1 : *irq = 4; break;
|
|
case 2 : *irq = 3; break;
|
|
case 3 : *irq = 5; break;
|
|
case 4 : *irq = 7; break;
|
|
}
|
|
}
|
|
|
|
if (*irq == 0 || *adr == 0 || *dev == 0) {
|
|
*adr = *irq = *dev = 0;
|
|
}
|
|
(*dev)--;
|
|
}
|
|
|
|
void rs232_int(int dev) {
|
|
|
|
int iir; /* Interrupt Ident Register */
|
|
register int i = 16;
|
|
__com_struct *ap = &asydev[dev];
|
|
register int base;
|
|
|
|
base = ap->base;
|
|
|
|
if (!ap->is_16550) {
|
|
/*********************************************************************
|
|
* <<<<<<<<<<< interrupt function for 8250 and 16450 >>>>>>>>>>>>>>> *
|
|
*********************************************************************/
|
|
while ((iir = inportb(IIR)) != 1) { /* while interrup pending */
|
|
switch (iir) {
|
|
case 0x04:
|
|
/*************************************************************
|
|
* interrupt type is - Receiver data available *
|
|
* interrupt reset with : read rx buffer register *
|
|
************************************************************/
|
|
/* read data + clear interrupt */
|
|
ap->in_buffer[ap->in_buffer_in++] = inportb(TRX);
|
|
if (ap->in_buffer_in == ap->in_buffersize)
|
|
ap->in_buffer_in = 0;
|
|
break;
|
|
|
|
case 0x02:
|
|
/*************************************************************
|
|
* interrupt type is - Transmitter Holding Registre Empty *
|
|
* source int. is - Transmitter Holding Registre Empty *
|
|
* interrupt reset with : reading iir if source *
|
|
* or write in tx holding register *
|
|
************************************************************/
|
|
if (ap->out_buffer_in != ap->out_buffer_out) {
|
|
outportb(TRX, ap->out_buffer[ap->out_buffer_out++]);
|
|
if (ap->out_buffer_out == ap->out_buffersize)
|
|
ap->out_buffer_out = 0;
|
|
}
|
|
else
|
|
ap->out_aktiv = FALSE;
|
|
break;
|
|
|
|
case 0x06:
|
|
/*************************************************************
|
|
* interrupt type is - receiver line status *
|
|
* source int is - Overun error or - Parity erorr *
|
|
* - framming error or - Break interrupt *
|
|
* interrupt reset with : reading the Line Status Register *
|
|
************************************************************/
|
|
ap->in_buffer[ap->in_buffer_in++] = 0xdb;
|
|
if (ap->in_buffer_in == ap->in_buffersize)
|
|
ap->in_buffer_in = 0;
|
|
ap->in_buffer[ap->in_buffer_in++] = 0x00; /* erzeugt Bad Frame*/
|
|
if (ap->in_buffer_in == ap->in_buffersize)
|
|
ap->in_buffer_in = 0;
|
|
inportb(LSR); /* reset this interupt */
|
|
break;
|
|
|
|
case 0x00:
|
|
/*************************************************************
|
|
* interrupt type is - Modem status (normal never activ) *
|
|
* source int is - CTS or DSR or Ring or DCD *
|
|
* interrupt reset with : reading Modem Status Register *
|
|
************************************************************/
|
|
inportb(MSR); /* reset interrupt */
|
|
break;
|
|
} /* end switch */
|
|
} /* end while */
|
|
} /* end if */
|
|
|
|
else {
|
|
/*********************************************************************
|
|
* <<<<<<<<<<<<<<<<< interrupt function for 16550 >>>>>>>>>>>>>>>>>> *
|
|
********************************************************************/
|
|
while ((iir = inportb(IIR)&0x8f) != 0x81){ /* while interrup pending */
|
|
switch (iir) {
|
|
case 0x84:
|
|
case 0x8C:
|
|
/*************************************************************
|
|
* interrupt type is - Receiver data available (0x84) or *
|
|
* - Caractere timeout (0x8C) *
|
|
* interrupt reset with : read rx buffer register Drops *
|
|
* below the trigger Level (0x84) *
|
|
* : read all the caracter in rx buffer *
|
|
************************************************************/
|
|
/* while data ready ? */
|
|
while((inportb(LSR) & 0x01) == 0x01) {
|
|
ap->in_buffer[ap->in_buffer_in++] = inportb(TRX);
|
|
if (ap->in_buffer_in == ap->in_buffersize)
|
|
ap->in_buffer_in = 0;
|
|
}
|
|
break;
|
|
|
|
case 0x82:
|
|
/*************************************************************
|
|
* interrupt type is - Transmitter Holding Register Empty *
|
|
* source int. is - Transmitter Holding Register Empty *
|
|
* interrupt reset with : reading iir if source *
|
|
* or write in tx holding register *
|
|
************************************************************/
|
|
if (ap->out_buffer_in == ap->out_buffer_out )
|
|
ap->out_aktiv = FALSE;
|
|
else {
|
|
do {
|
|
outportb(TRX, ap->out_buffer[ap->out_buffer_out++]);
|
|
if (ap->out_buffer_out == ap->out_buffersize)
|
|
ap->out_buffer_out = 0;
|
|
}
|
|
while((ap->out_buffer_in != ap->out_buffer_out) && (--i > 0));
|
|
}
|
|
break;
|
|
|
|
case 0x86:
|
|
/*************************************************************
|
|
* interrupt type is - receiver line status *
|
|
* source int is - Overun error or - Parity erorr *
|
|
* - framming error or - Break interrupt *
|
|
* interrupt reset with : reading the Line Status Register *
|
|
************************************************************/
|
|
ap->in_buffer[ap->in_buffer_in++] = 0xdb;
|
|
if (ap->in_buffer_in == ap->in_buffersize)
|
|
ap->in_buffer_in = 0;
|
|
ap->in_buffer[ap->in_buffer_in++] = 0x00; /* erzeugt Bad Frame */
|
|
if (ap->in_buffer_in == ap->in_buffersize)
|
|
ap->in_buffer_in = 0;
|
|
inportb(LSR); /* reset this interupt */
|
|
break;
|
|
|
|
case 0x80:
|
|
/*************************************************************
|
|
* interrupt type is - Modem status (normal never activ) *
|
|
* source int is - CTS or DSR or Ring or DCD *
|
|
* interrupt reset with : reading Modem Status Register *
|
|
************************************************************/
|
|
inportb(MSR); /* reset interrupt */
|
|
break;
|
|
|
|
} /* end switch */
|
|
} /* end while int */
|
|
} /* end else 8250 16450 / 16550 */
|
|
} /* end function */
|
|
|
|
void clear_sio(int dev) {
|
|
int oldier;
|
|
int c = 1;
|
|
__com_struct *ap = &asydev[dev];
|
|
register int base;
|
|
|
|
base = ap->base;
|
|
|
|
/* Empfangs-Overrun loeschen */
|
|
while (inportb(LSR) != 0x60 && (c++ <= 1600)) inportb(TRX);
|
|
|
|
/* Interruptfehlerquellen loeschen */
|
|
oldier = inportb(IER); /* alle Interrupts sperren */
|
|
outportb(IER, 0);
|
|
|
|
c = 1;
|
|
while (inportb(IIR) == 0 && c <= 10) {
|
|
if ((inportb(IIR) & 6) == 6) {
|
|
inportb(LSR);
|
|
eoi();
|
|
}
|
|
if ((inportb(IIR) & 4) == 4) {
|
|
inportb(TRX);
|
|
eoi();
|
|
}
|
|
if (inportb(IIR) == 0) {
|
|
inportb(MSR);
|
|
eoi();
|
|
}
|
|
c++;
|
|
}
|
|
|
|
outportb(IER, oldier);
|
|
}
|
|
|
|
void clear_rs232(int dev) {
|
|
|
|
__com_struct *ap = &asydev[dev];
|
|
register int base;
|
|
|
|
if (!ap->active) return;
|
|
|
|
base = ap->base;
|
|
|
|
clear_sio(dev);
|
|
|
|
disable(); /* Int vorsichtshalber abschalten */
|
|
if (ap->is_16550) {
|
|
outportb(FCR, 0x87); /* FIFO ON , reset rx and tx FIFO */
|
|
outportb(FCR, 0x81); /* trigger level = 14 bytes */
|
|
}
|
|
|
|
do
|
|
{
|
|
inportb(TRX); /* Datenregister leeren */
|
|
}
|
|
while((inportb(LSR) & 0x01) == 0x01);
|
|
|
|
ap->in_buffer_in = ap->in_buffer_out = ap->out_buffer_in =
|
|
ap->out_buffer_out = 0;
|
|
ap->out_aktiv = FALSE;
|
|
|
|
enable(); /* int wieder an */
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Initialisieren der Schnittstelle, OHNE Baudrate setzen! */
|
|
/* Buffer initialisieren, Interrupt Vektoren setzen */
|
|
/* */
|
|
/* 16550A-FIFO-Bits (Portadresse [Basis+2]) */
|
|
/* ================ */
|
|
/* */
|
|
/* Bit Read: IIR Write: FIFO Control */
|
|
/* --------------------------------------------------- */
|
|
/* 0 Interrupt Pending FIFO enable */
|
|
/* 1 ID-Bit 1 FIFO Reset Receive */
|
|
/* 2 ID-Bit 2 FIFO Reset Transmit */
|
|
/* 3 ID-Bit 3 (Timeout) 0 (im PC/AT) */
|
|
/* 4 0 0 */
|
|
/* 5 0 0 */
|
|
/* 6 FIFO ON (nur 16550A) Trigger Level TL0 */
|
|
/* 7 FIFO ON Trigger Level TL1 */
|
|
/* */
|
|
/* TL1 TL0 Trigger-Level */
|
|
/* 0 0 1 Byte */
|
|
/* 0 1 4 Bytes */
|
|
/* 1 0 8 Bytes */
|
|
/* 1 1 14 Bytes */
|
|
/*----------------------------------------------------------------------*/
|
|
int open_rs232(int dev, int base, int irq, int insize, int outsize)
|
|
{
|
|
__com_struct *ap = &asydev[dev];
|
|
|
|
if (ap->active) {
|
|
printf("*** WARNING: COM%u is already in use!\n",dev+1);
|
|
return(-1);
|
|
}
|
|
|
|
ap->active = TRUE;
|
|
|
|
ap->base = base;
|
|
ap->irq = irq;
|
|
|
|
clear_sio(dev);
|
|
|
|
outportb(FCR, 0x01); /* enable FIFO mode (16550) */
|
|
|
|
printf("--- /dev/COM%u (0x%03X,IRQ%u), UART is ", dev+1, base, irq);
|
|
|
|
if ((inportb(FCR) & 0x80) == 0x80) { /* test if FIFO enabled */
|
|
ap->is_16550 = TRUE;
|
|
printf("16550A");
|
|
outportb(FCR,0x87); /* FIFO ON, clear rx and tx FIFO */
|
|
}
|
|
else {
|
|
ap->is_16550 = FALSE;
|
|
printf("8250/16450");
|
|
}
|
|
|
|
do {
|
|
inportb(TRX); /* Datenregister leeren */
|
|
} while((inportb(LSR) & 0x01) == 0x01);
|
|
|
|
outportb(LCR, 0x07); /* 8bit + 2 stop, DLAB auf 0 */
|
|
outportb(IER, 0x07); /* Interrupt: Rx, TX, RLS */
|
|
outportb(MCR, 0x08); /* Interrupt frei (out2) */
|
|
|
|
ap->in_buffer_in = ap->in_buffer_out = ap->out_buffer_in =
|
|
ap->out_buffer_out = 0;
|
|
ap->out_aktiv = FALSE;
|
|
|
|
disable();
|
|
allocirq(irq, 0, rs232_int, dev); /* Vektor setzen */
|
|
ap->oldmask = getmask(irq);
|
|
maskon(irq);
|
|
ap->out_buffer = malloc(ap->in_buffersize = insize);
|
|
ap->in_buffer = malloc(ap->out_buffersize = outsize);
|
|
enable(); /* Interrupt frei */
|
|
|
|
return(dev);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Schnittstelle sperren */
|
|
/*----------------------------------------------------------------------*/
|
|
void close_rs232(int dev) {
|
|
__com_struct *ap = &asydev[dev];
|
|
register int base;
|
|
|
|
if (!ap->active) return;
|
|
|
|
base = ap->base;
|
|
clear_sio(dev);
|
|
|
|
disable();
|
|
if (!ap->oldmask) maskoff(ap->irq);
|
|
outportb(IER, 0x00); /* keine Interrupt Freigabe */
|
|
outportb(MCR, 0x08); /* Out2 sperrt Interrupt */
|
|
if (ap->is_16550)
|
|
outportb(FCR,0x00); /* FIFO OFF */
|
|
freeirq(ap->irq);
|
|
enable();
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Neuer Syntax nach DL1XAO, Implementierung DB7KG: */
|
|
/* Setzt die naechstbeste Baudrate und liefert die gesetzte zurueck. */
|
|
/*----------------------------------------------------------------------*/
|
|
int setbaud(int dev, int argu) {
|
|
|
|
__com_struct *ap = &asydev[dev];
|
|
register int base;
|
|
int baudtab[] = {12,24,48,96,192,384,576,1152,0}, *i, lsb;
|
|
|
|
if (!ap->active) return(0);
|
|
|
|
base = ap->base;
|
|
|
|
clear_sio(dev);
|
|
|
|
for (i = baudtab; *i; i++)
|
|
if (argu <= *i) break;
|
|
|
|
if (*i) argu = *i; /* wenn mehr als Maximalwert gewuenscht,*/
|
|
else argu = 1152; /* dann begrenzen */
|
|
|
|
lsb = 1152 / argu; /* Teiler berechnen */
|
|
|
|
outportb(LCR, 0x87); /* auf Baudraten Register 2 stop bit */
|
|
outportb(TRX, lsb ); /* LSB Teilerfaktor */
|
|
outportb(IER, 0x00); /* MSB */
|
|
outportb(LCR, 0x07); /* 8 Bit, keine Paritaet, 2 Stop */
|
|
|
|
return(argu);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Anzahl der Stopbits setzen */
|
|
/*----------------------------------------------------------------------*/
|
|
void setstopbits(int dev, int stops)
|
|
{
|
|
__com_struct *ap = &asydev[dev];
|
|
register int base;
|
|
|
|
base = ap->base;
|
|
|
|
if (!ap->active) return;
|
|
|
|
clear_sio(dev);
|
|
if (stops == 2) outportb(LCR, 0x07); /* 8 Bit, keine Paritaet, 2 Stop */
|
|
else outportb(LCR, 0x03); /* 8 Bit, k. Paritaet, 1 Stop */
|
|
clear_sio(dev);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Zeichen aus Buffer an Programm liefern */
|
|
/*----------------------------------------------------------------------*/
|
|
int rs232_in(int dev) {
|
|
char c;
|
|
register __com_struct *ap = &asydev[dev];
|
|
|
|
if (!ap->active) return(-1);
|
|
|
|
if (ap->in_buffer_in != ap->in_buffer_out) {
|
|
c = ap->in_buffer[ap->in_buffer_out++];
|
|
if (ap->in_buffer_out == ap->in_buffersize)
|
|
ap->in_buffer_out = 0;
|
|
return(c);
|
|
}
|
|
return(-1);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Zeichen an Schnittstelle geben, ggfs im Buffer speichern */
|
|
/*----------------------------------------------------------------------*/
|
|
void rs232_out(int dev, int c) {
|
|
register __com_struct *ap = &asydev[dev];
|
|
|
|
if (!ap->active) return;
|
|
|
|
disable();
|
|
if (ap->out_aktiv) {
|
|
ap->out_buffer[ap->out_buffer_in++] = c;
|
|
if (ap->out_buffer_in == ap->out_buffersize)
|
|
ap->out_buffer_in = 0;
|
|
}
|
|
else {
|
|
ap->out_aktiv = TRUE;
|
|
outportb(ap->base,c);
|
|
}
|
|
enable();
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Blockweise von der Schnittstelle lesen */
|
|
/*----------------------------------------------------------------------*/
|
|
int rs232_read(int dev, UBYTE *buf, int max) {
|
|
register __com_struct *ap = &asydev[dev];
|
|
int n;
|
|
|
|
if (!ap->active) return(-1);
|
|
|
|
disable();
|
|
for (n = 0; n < max; n++) {
|
|
if (ap->in_buffer_in != ap->in_buffer_out) {
|
|
*buf++ = ap->in_buffer[ap->in_buffer_out++];
|
|
if (ap->in_buffer_out == ap->in_buffersize)
|
|
ap->in_buffer_out = 0;
|
|
} else {
|
|
enable();
|
|
return(n);
|
|
}
|
|
}
|
|
enable();
|
|
return(n);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Speicherblock an Schnittstelle geben, ggfs im Buffer speichern */
|
|
/*----------------------------------------------------------------------*/
|
|
void rs232_write(int dev, UBYTE *start, UBYTE *end) {
|
|
register __com_struct *ap = &asydev[dev];
|
|
|
|
if (!ap->active) return;
|
|
|
|
disable();
|
|
if (!ap->out_aktiv) {
|
|
ap->out_aktiv = TRUE;
|
|
outportb(ap->base, *start++);
|
|
}
|
|
while((end - start) > 0L) {
|
|
ap->out_buffer[ap->out_buffer_in++] = *start++;
|
|
if (ap->out_buffer_in == ap->out_buffersize)
|
|
ap->out_buffer_in = 0;
|
|
}
|
|
enable();
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Status Eingabe */
|
|
/*----------------------------------------------------------------------*/
|
|
int rs232_in_status(int dev)
|
|
{
|
|
__com_struct *ap = &asydev[dev];
|
|
|
|
if (!ap->active) return(0);
|
|
|
|
return(!(ap->in_buffer_in == ap->in_buffer_out));
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Status Eingabe */
|
|
/*----------------------------------------------------------------------*/
|
|
int rs232_out_status(int dev)
|
|
{
|
|
__com_struct *ap = &asydev[dev];
|
|
|
|
if (!ap->active) return(0);
|
|
|
|
/*#ifndef no_db7kg_fast_and_dirty_hack_for_db0fd
|
|
if (ap->out_buffer_in == ap->out_buffer_out)
|
|
ap->out_aktiv = FALSE;
|
|
#endif*/
|
|
return(ap->out_aktiv);
|
|
}
|
|
|
|
void init_rs232(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAXCOMS; i++)
|
|
asydev[i].active = FALSE;
|
|
}
|
|
|
|
void exit_rs232(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAXCOMS; i++)
|
|
if (asydev[i].active)
|
|
close_rs232(i);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
/* End of os/go32/16550.c */
|