home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
telecomm
/
nhclb120
/
8250.c
next >
Wrap
C/C++ Source or Header
|
1993-09-26
|
10KB
|
487 lines
/* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC */
/* hacked to support BIOS interaction for NET PC9801 ala JK1NNT by N3EUA */
#include "config.h"
#if !defined(PLUS)
#include <stdio.h>
#include "global.h"
#include "asy.h"
#include "8250.h"
#include "iface.h"
struct asy asy[ASY_MAX];
unsigned nasy;
unsigned h2ivec[ASYHANDLE_MAX] ;
unsigned nhandlers ;
struct ivec ivec[NIVECS] ;
#ifdef PC9801
int work, f_handle, jkintdos();
#define mask 0x00ff
#endif
/* ASY interrupt handlers */
#ifndef PC9801
extern void asy0vec(),asy1vec(),asy2vec(),asy3vec(),asy4vec();
void (*handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
#endif
/* Initialize asynch port "dev" */
int
asy_init(dev,arg1,arg2,bufsize)
int16 dev;
char *arg1,*arg2; /* Attach args for address and vector */
unsigned bufsize;
{
register unsigned base;
register struct fifo *fp;
register struct asy *ap;
unsigned vec ;
void (*getirq())();
char i_state;
ap = &asy[dev];
ap->addr = htoi(arg1);
ap->vec = htoi(arg2);
/* Set up receiver FIFO */
fp = &ap->fifo;
if((fp->buf = malloc(bufsize)) == NULLCHAR){
printf("asy%d: No space for rx buffer\r\n",dev);
fflush(stdout);
return -1;
}
fp->bufsize = bufsize;
fp->wp = fp->rp = fp->buf;
fp->cnt = 0;
base = ap->addr;
ap->urgent = NULLCHAR; /* For SLFP urgent data */
#ifdef PC9801
f_handle = auxopen();
#else
/* Purge the receive data buffer */
(void)inportb(base+RBR);
i_state = disable();
/* Set up interrupt vector structure if necessary */
vec = ap->vec ;
if (ivec[vec].ichain == NULLASY) {
if (nhandlers == ASYHANDLE_MAX) {
printf("asy%d: No more interrupt handlers\n",dev) ;
return ;
}
/* Save original interrupt vector and mask */
ivec[vec].oldvec = getirq(ap->vec) ;
ivec[vec].oldmask = getmask(ap->vec) ;
/* Set interrupt vector to SIO handler */
setirq(ap->vec,handle[nhandlers]);
/* Set up correspondence between handler and interrupt */
h2ivec[nhandlers] = vec ;
nhandlers++ ; /* step to next handler */
/* Start chain */
ivec[vec].ichain = &asy[dev] ;
ap->ichain = NULLASY ; /* terminate the chain */
}
else { /* Already a handler for this; just put in chain */
ap->ichain = ivec[vec].ichain ; /* Put at head */
ivec[vec].ichain = &asy[dev] ;
}
/* Update vector reference count */
ivec[vec].refcnt++ ;
/* Save original control bits */
ap->save.lcr = inportb(base+LCR);
ap->save.ier = inportb(base+IER);
ap->save.mcr = inportb(base+MCR);
/* save speed bytes */
setbit(base+LCR,LCR_DLAB);
ap->save.divl = inportb(base+DLL);
ap->save.divh = inportb(base+DLM);
clrbit(base+LCR,LCR_DLAB);
/* Set line control register: 8 bits, no parity */
outportb(base+LCR,(char)LCR_8BITS);
/* Turn on receive interrupt enable in 8250, leave transmit
* and modem status interrupts turned off for now
*/
outportb(base+IER,(char)IER_DAV);
/* Set modem control register: assert DTR, RTS, turn on 8250
* master interrupt enable (connected to OUT2)
*/
outportb(base+MCR,(char)(MCR_DTR|MCR_RTS|MCR_OUT2));
/* Enable interrupt */
maskon(ap->vec);
restore(i_state);
#endif /* PC9801 */
}
int
asy_stop(iface)
struct interface *iface;
{
register unsigned base;
register struct asy *ap;
unsigned vec ;
char i_state;
ap = &asy[iface->dev];
base = ap->addr;
#ifndef PC9801
/* Purge the receive data buffer */
(void)inportb(base+RBR);
/* See if this is the last asy using this interrupt, */
/* and restore the vector and interrupt mask if it is */
vec = ap->vec ;
if (--ivec[vec].refcnt == 0) {
i_state = disable();
setirq(ap->vec,ivec[vec].oldvec);
if(ivec[vec].oldmask)
maskon(ap->vec);
else
maskoff(ap->vec);
restore(i_state);
}
/* Restore original interrupt vector and 8259 mask state */
/* Restore speed regs */
setbit(base+LCR,LCR_DLAB);
outportb(base+DLL,ap->save.divl); /* Low byte */
outportb(base+DLM,ap->save.divh); /* Hi byte */
clrbit(base+LCR,LCR_DLAB);
/* Restore control regs */
outportb(base+LCR,ap->save.lcr);
outportb(base+IER,ap->save.ier);
outportb(base+MCR,ap->save.mcr);
#endif /* PC9801 */
}
/* Asynchronous line I/O control */
asy_ioctl(interface,argc,argv)
struct interface *interface;
int argc;
char *argv[];
{
if(argc < 1){
printf("%d\r\n",asy[interface->dev].speed);
return 0;
}
return asy_speed(interface->dev,atoi(argv[0]));
}
/* Set asynch line speed */
int
asy_speed(dev,speed)
int16 dev;
int speed;
{
#ifdef PC9801
register int s_code, speed1;
if (speed ==0 || speed >= 10000 || dev >= nasy)
return -1;
speed1 = 75;
for (s_code = 0; speed >= speed1; s_code++) speed1 *= 2;
speed1 /= 2;
asy[dev].speed = speed1;
set_speed(s_code);
#else
register unsigned base;
register int divisor;
char i_state;
if(speed == 0 || dev >= nasy)
return -1;
base = asy[dev].addr;
asy[dev].speed = speed;
divisor = BAUDCLK / (long)speed;
i_state = disable();
/* Purge the receive data buffer */
(void)inportb(base+RBR);
/* Turn on divisor latch access bit */
setbit(base+LCR,LCR_DLAB);
/* Load the two bytes of the register */
outportb(base+DLL,(char)(divisor & 0xff)); /* Low byte */
outportb(base+DLM,(char)((divisor >> 8) & 0xff)); /* Hi byte */
/* Turn off divisor latch access bit */
clrbit(base+LCR,LCR_DLAB);
restore(i_state);
return 0;
#endif /* PC9801 */
}
/* Send a buffer to serial transmitter */
asy_output(dev,buf,cnt)
unsigned dev;
char *buf;
unsigned short cnt;
{
#ifdef PC9801
int b_cnt, b_buf;
if (dev >= nasy) return;
for (b_cnt = 0; b_cnt < cnt; b_cnt++) {
b_buf = buf[b_cnt];
aput(b_buf);
}
#else
register struct dma *dp;
unsigned base;
char i_state;
if(dev >= nasy)
return;
base = asy[dev].addr;
dp = &asy[dev].dma;
i_state = disable();
if(dp->flags){
restore(i_state);
return; /* Already busy */
}
dp->data = buf;
dp->cnt = cnt;
dp->flags = 1;
/* Enable transmitter buffer empty interrupt and simulate
* an interrupt; this will get things rolling.
*/
setbit(base+IER,IER_TxE);
asytxint(dev);
restore(i_state);
#endif /* PC9801 */
}
/* Receive characters from asynch line
* Returns count of characters read
*/
int16
asy_recv(dev,buf,cnt)
int16 dev;
char *buf;
unsigned cnt;
{
unsigned tot,n;
#ifdef PC9801
int ch;
for (tot = 0; tot < cnt; tot++) {
if (auxstat()) ch = aget();
else break;
buf[tot] = ch;
}
return tot;
}
#else
int kbread();
char i_state;
struct fifo *fp;
fp = &asy[dev].fifo;
tot = 0;
/* Read from serial I/O input buffer */
i_state = disable();
for(;;){
n = min(cnt,fp->cnt);
if(n == 0)
break;
n = min(n,&fp->buf[fp->bufsize] - fp->rp);
memcpy(buf,fp->rp,n);
fp->rp += n;
if(fp->rp >= &fp->buf[fp->bufsize])
fp->rp = fp->buf;
fp->cnt -= n;
buf += n;
tot += n;
cnt -= n;
}
restore(i_state);
return tot;
}
/* Interrupt handler for 8250 asynch chip */
void
asyint(handler)
unsigned handler;
{
register unsigned base;
register char iir;
register unsigned dev ;
struct asy *ap ;
int someint ;
/* The following bears some explaining. Because the PC and AT
* (but not the PS/2) uses edge triggered interrupts, we need
* to assure that the shared interrupt line makes a low-going
* transition before we issue an EOI. If we don't, one of the
* UARTs could raise its interrupt line again after we serviced
* it, but before we cleared all the other UARTs' interrupts,
* and this would be missed by the interrupt controller in
* edge triggered mode. This should still work fine on a
* PS/2.
* Many thanks to Dan Dodge of Quantum Software, Ltd., for
* suggesting this trick. dmf
*/
do {
someint = 0 ; /* No interrupt detected yet on this pass */
ap = ivec[h2ivec[handler]].ichain ; /* Start at head of chain */
while (ap != NULLASY) {
dev = ap - asy ;
base = asy[dev].addr;
while(((iir = inportb(base+IIR)) & IIR_IP) == 0) {
someint = 1 ; /* Detected an interrupt */
switch(iir & IIR_ID){
case IIR_RDA: /* Receiver interrupt */
asyrxint(dev);
break;
case IIR_THRE: /* Transmit interrupt */
asytxint(dev);
break;
}
}
ap = ap->ichain ; /* step to next in chain */
}
} while (someint != 0) ;
}
/* Process 8250 receiver interrupts */
static
asyrxint(dev)
unsigned dev;
{
unsigned base;
register struct fifo *fp;
char c;
base = asy[dev].addr;
fp = &asy[dev].fifo;
while(inportb(base+LSR) & LSR_DR){
c = inportb(base+RBR);
/* Process incoming data;
* If buffer is full, we have no choice but
* to drop the character
*/
if(fp->cnt != fp->bufsize){
*fp->wp++ = c;
if(fp->wp == &fp->buf[fp->bufsize])
/* Wrap around */
fp->wp = fp->buf;
fp->cnt++;
}
}
}
/* Handle 8250 transmitter interrupts */
static
asytxint(dev)
unsigned dev;
{
register struct dma *dp;
register unsigned base;
unsigned urg; /* urgent SLFP data, or == 256 */
base = asy[dev].addr;
dp = &asy[dev].dma;
if(!dp->flags){
/* "Shouldn't happen", but disable transmit
* interrupts anyway
*/
clrbit(base+IER,IER_TxE);
return; /* Nothing to send */
}
while(inportb(base+LSR) & LSR_THRE){
/* Send any pending urgent data */
if (asy[dev].urgent != NULLCHAR)
if ((urg = (*asy[dev].urgent)(dev)) < 256) {
outportb(base+THR,urg);
continue;
}
dp->last_octet = *dp->data;
outportb(base+THR,*dp->data++);
if(--dp->cnt == 0){
dp->flags = 0;
/* Disable transmit interrupts */
clrbit(base+IER,IER_TxE);
/* Call completion interrupt here */
break;
}
}
}
/* Set bit(s) in I/O port */
setbit(port,bits)
unsigned port;
char bits;
{
outportb(port,(char)inportb(port)|bits);
}
/* Clear bit(s) in I/O port */
clrbit(port,bits)
unsigned port;
char bits;
{
outportb(port,(char)(inportb(port) & ~bits));
}
#endif /* PC9801 */
int
stxrdy(dev)
int16 dev;
{
return(!asy[dev].dma.flags);
}
#ifdef PC9801
auxopen()
{
char *device;
device = "AUX";
work = jkintdos(0x3d02,0,device);
return (work);
}
auxstat()
{
work = jkintdos(0x4406,f_handle,0) & mask;
return (work);
}
aget()
{
work = jkintdos(0x0300,0,0) & mask;
return (work);
}
aput(ch)
int ch;
{
jkintdos(0x0400,0,ch);
}
#endif /* PC9801 */
#endif /* !defined(PLUS) */