?? n8250.c
字號:
/* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC
*/
#include <stdio.h>
#include <dos.h>
#include <errno.h>
#include "global.h"
#include "mbuf.h"
#include "proc.h"
#include "iface.h"
#include "n8250.h"
#include "asy.h"
#include "devparam.h"
#include "nospc.h"
#include "dialer.h"
static int asyrxint(struct asy *asyp);
static void asytxint(struct asy *asyp);
static void asymsint(struct asy *asyp);
static void pasy(struct asy *asyp);
static INTERRUPT (far *(asycom)(struct asy *))(void);
struct asy Asy[ASY_MAX];
struct fport Fport[FPORT_MAX];
static INTERRUPT (*Fphand[FPORT_MAX])() = {
fp0vec,
};
/* ASY interrupt handlers */
static INTERRUPT (*Handle[ASY_MAX])() = {
asy0vec,asy1vec,asy2vec,asy3vec,asy4vec,asy5vec
};
/* Initialize asynch port "dev" */
int
asy_init(dev,ifp,base,irq,bufsize,trigchar,speed,cts,rlsd,chain)
int dev;
struct iface *ifp;
int base;
int irq;
uint16 bufsize;
int trigchar;
long speed;
int cts; /* Use CTS flow control */
int rlsd; /* Use Received Line Signal Detect (aka CD) */
int chain; /* Chain interrupts */
{
register struct fifo *fp;
register struct asy *ap;
int i_state;
ap = &Asy[dev];
ap->iface = ifp;
ap->addr = base;
ap->vec = irq;
ap->chain = chain;
/* Set up receiver FIFO */
fp = &ap->fifo;
fp->buf = mallocw(bufsize);
fp->bufsize = bufsize;
fp->wp = fp->rp = fp->buf;
fp->cnt = 0;
fp->hiwat = 0;
fp->overrun = 0;
base = ap->addr;
ap->trigchar = trigchar;
/* Purge the receive data buffer */
(void)inportb(base+RBR);
i_state = dirps();
/* Save original interrupt vector, mask state, control bits */
if(ap->vec != -1){
ap->save.vec = getirq(ap->vec);
ap->save.mask = getmask(ap->vec);
}
ap->save.lcr = inportb(base+LCR);
ap->save.ier = inportb(base+IER);
ap->save.mcr = inportb(base+MCR);
ap->msr = ap->save.msr = inportb(base+MSR);
ap->save.iir = inportb(base+IIR);
/* 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);
/* save modem control flags */
ap->cts = cts;
ap->rlsd = rlsd;
/* Set interrupt vector to SIO handler */
if(ap->vec != -1)
setirq(ap->vec,Handle[dev]);
/* Set line control register: 8 bits, no parity */
outportb(base+LCR,LCR_8BITS);
/* determine if 16550A, turn on FIFO mode and clear RX and TX FIFOs */
outportb(base+FCR,FIFO_ENABLE);
/* According to National ap note AN-493, the FIFO in the 16550 chip
* is broken and must not be used. To determine if this is a 16550A
* (which has a good FIFO implementation) check that both bits 7
* and 6 of the IIR are 1 after setting the fifo enable bit. If
* not, don't try to use the FIFO.
*/
if ((inportb(base+IIR) & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED) {
ap->is_16550a = TRUE;
outportb(base+FCR,FIFO_SETUP);
} else {
/* Chip is not a 16550A. In case it's a 16550 (which has a
* broken FIFO), turn off the FIFO bit.
*/
outportb(base+FCR,0);
ap->is_16550a = FALSE;
}
/* Turn on receive interrupts and optionally modem interrupts;
* leave transmit interrupts off until we actually send data.
*/
if(ap->rlsd || ap->cts)
outportb(base+IER,IER_MS|IER_DAV);
else
outportb(base+IER,IER_DAV);
/* Turn on 8250 master interrupt enable (connected to OUT2) */
setbit(base+MCR,MCR_OUT2);
/* Enable interrupt */
if(ap->vec != -1)
maskon(ap->vec);
restore(i_state);
asy_speed(dev,speed);
return 0;
}
int
asy_stop(ifp)
struct iface *ifp;
{
register unsigned base;
register struct asy *ap;
struct asydialer *dialer;
int i_state;
ap = &Asy[ifp->dev];
if(ap->iface == NULL)
return -1; /* Not allocated */
ap->iface = NULL;
base = ap->addr;
if(ifp->dstate != NULL){
dialer = (struct asydialer *)ifp->dstate;
stop_timer(&dialer->idle); /* Stop the idle timer, if running */
free(dialer);
ifp->dstate = NULL;
}
(void)inportb(base+RBR); /* Purge the receive data buffer */
if(ap->is_16550a){
/* Purge hardware FIFOs and disable if we weren't already
* in FIFO mode when we entered. Apparently some
* other comm programs can't handle 16550s in
* FIFO mode; they expect 16450 compatibility mode.
*/
outportb(base+FCR,FIFO_SETUP);
if((ap->save.iir & IIR_FIFO_ENABLED) != IIR_FIFO_ENABLED)
outportb(base+FCR,0);
}
/* Restore original interrupt vector and 8259 mask state */
i_state = dirps();
if(ap->vec != -1){
setirq(ap->vec,ap->save.vec);
if(ap->save.mask)
maskon(ap->vec);
else
maskoff(ap->vec);
}
/* 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);
restore(i_state);
free(ap->fifo.buf);
return 0;
}
/* Set asynch line speed */
int
asy_speed(dev,bps)
int dev;
long bps;
{
register unsigned base;
register long divisor;
struct asy *asyp;
int i_state;
if(bps <= 0 || dev >= ASY_MAX)
return -1;
asyp = &Asy[dev];
if(asyp->iface == NULL)
return -1;
if(bps == 0)
return -1;
asyp->speed = bps;
base = asyp->addr;
divisor = BAUDCLK / bps;
i_state = dirps();
/* Purge the receive data buffer */
(void)inportb(base+RBR);
if (asyp->is_16550a) /* clear tx+rx fifos */
outportb(base+FCR,FIFO_SETUP);
/* Turn on divisor latch access bit */
setbit(base+LCR,LCR_DLAB);
/* Load the two bytes of the register */
outportb(base+DLL,divisor); /* Low byte */
outportb(base+DLM,divisor >> 8); /* Hi byte */
/* Turn off divisor latch access bit */
clrbit(base+LCR,LCR_DLAB);
restore(i_state);
return 0;
}
/* Asynchronous line I/O control */
int32
asy_ioctl(ifp,cmd,set,val)
struct iface *ifp;
int cmd;
int set;
int32 val;
{
struct asy *ap = &Asy[ifp->dev];
uint16 base = ap->addr;
switch(cmd){
case PARAM_SPEED:
if(set)
asy_speed(ifp->dev,val);
return ap->speed;
case PARAM_DTR:
if(set) {
writebit(base+MCR,MCR_DTR,(int)val);
}
return (inportb(base+MCR) & MCR_DTR) ? TRUE : FALSE;
case PARAM_RTS:
if(set) {
writebit(base+MCR,MCR_RTS,(int)val);
}
return (inportb(base+MCR) & MCR_RTS) ? TRUE : FALSE;
case PARAM_DOWN:
clrbit(base+MCR,MCR_RTS);
clrbit(base+MCR,MCR_DTR);
return FALSE;
case PARAM_UP:
setbit(base+MCR,MCR_RTS);
setbit(base+MCR,MCR_DTR);
return TRUE;
}
return -1;
}
/* Open an asynch port for direct I/O, temporarily suspending any
* packet-mode operations. Returns device number for asy_write and get_asy
*/
int
asy_open(name)
char *name;
{
struct iface *ifp;
int dev;
if((ifp = if_lookup(name)) == NULL){
errno = ENODEV;
return -1;
}
if((dev = ifp->dev) >= ASY_MAX || Asy[dev].iface != ifp){
errno = EINVAL;
return -1;
}
/* Suspend the packet drivers */
suspend(ifp->rxproc);
suspend(ifp->txproc);
/* bring the line up (just in case) */
if(ifp->ioctl != NULL)
(*ifp->ioctl)(ifp,PARAM_UP,TRUE,0L);
return dev;
}
int
asy_close(dev)
int dev;
{
struct iface *ifp;
if(dev < 0 || dev >= ASY_MAX){
errno = EINVAL;
return -1;
}
/* Resume the packet drivers */
if((ifp = Asy[dev].iface) == NULL){
errno = EINVAL;
return -1;
}
resume(ifp->rxproc);
resume(ifp->txproc);
return 0;
}
/* Send a buffer on the serial transmitter and wait for completion */
int
asy_write(dev,buf,cnt)
int dev;
void *buf;
unsigned short cnt;
{
register struct dma *dp;
unsigned base;
struct asy *asyp;
int tmp;
int i_state;
struct iface *ifp;
if(dev < 0 || dev >= ASY_MAX)
return -1;
asyp = &Asy[dev];
if((ifp = asyp->iface) == NULL)
return -1;
base = asyp->addr;
dp = &asyp->dma;
if(dp->busy)
return -1; /* Already busy */
dp->data = buf;
dp->cnt = cnt;
dp->busy = 1;
/* If CTS flow control is disabled or CTS is true,
* enable transmit interrupts here so we'll take an immediate
* interrupt to get things going. Otherwise let the
* modem control interrupt enable transmit interrupts
* when CTS comes up. If we do turn on TxE,
* "kick start" the transmitter interrupt routine, in case just
* setting the interrupt enable bit doesn't cause an interrupt
*/
if(!asyp->cts || (asyp->msr & MSR_CTS)){
setbit(base+IER,IER_TxE);
asytxint(asyp);
}
/* Wait for completion */
for(;;){
i_state = dirps();
tmp = dp->busy;
restore(i_state);
if(tmp == 0)
break;
kwait(&asyp->dma);
}
ifp->lastsent = secclock();
return cnt;
}
/* Read data from asynch line
* Blocks until at least 1 byte of data is available.
* returns number of bytes read, up to 'cnt' max
*/
int
asy_read(dev,buf,cnt)
int dev;
void *buf;
unsigned short cnt;
{
struct fifo *fp;
int i_state,tmp;
uint8 c,*obp;
if(cnt == 0)
return 0;
if(dev < 0 || dev >= ASY_MAX){
errno = EINVAL;
return -1;
}
fp = &Asy[dev].fifo;
obp = (uint8 *)buf;
for(;;){
/* Atomic read of and subtract from fp->cnt */
i_state = dirps();
tmp = fp->cnt;
if(tmp != 0){
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -