TheNetNode-CB/os/go32/16550.c

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