Listing 1: Parallel I/O functions
#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <mem.h>
#include "local.h"
#include "ppio.h"
#include "irqhelpr.h"
#define IN_SYNC 0x40
#define OUT_SYNC 0x08
/*not shown: nibble translation tables*/
/*
recieve a block from the parallel port.
this can be called either via interrupt or directly
*/
LOCAL void PP_rcv(PP *info)
{
int dPort = info->basePort; /* data port */
int sPort = dPort+1; /* status port */
int cPort = dPort+2; /* control port */
PPMODE mode = info->mode; /* mode */
RCVSTATE state = RCV_WAKEUP; /* current state */
BYTE sync = 0; /* sync toggle */
BOOL ready; /* FALSE if timeout */
BYTE byte; /* byte accumulator */
LENGTHBLOCK hdr; /* length header */
int ack; /* ack, 0 = NAK */
unsigned ii; /* misc. index */
++info->lock; /* block re-entrancy */
outportb(cPort, 0x04); /* disable hardware interrupts */
do {
unsigned now = WCLOCK; /* setup timeout variables */
int tickCt = RTICKS;
BYTE dNybble;
do {
if (now != WCLOCK) { /* check timeout */
tickCt--;
now = WCLOCK;
}
dNybble = inportb(sPort); /* get nybble */
ready = (dNybble & IN_SYNC) ^ sync; /* done when sync inverts */
} while (!ready && tickCt);
if (ready) {
/*
1st --- translate the port result into a usable nybble
note --- the port is re-read because occasionally all of the
bits in [dNybble] are not correct.
*/
dNybble = in_nybble[(inportb(sPort) >> 3) & 0x1f];
if (mode == PP_BYTE) /* byte mode, grab high
nybble from control port */
byte = dNybble | (inportb(cPort) << 4);
else if (sync) /* low sync = low nybble */
byte = dNybble;
else /* high sync = high nybble */
byte |= dNybble << 4;
if (state == RCV_WAKEUP) {
/*
wakeup only -- <<always>> advance state
*/
state++; /* bump to recieve header */
ack = mode; /* ack with mode */
ii = 0; /* set header index (0) */
} else if ((mode == PP_BYTE) || !sync) {
/*
otherwise process every other nybble, or every byte
*/
switch (state) {
case RCV_HEADER: /* recieving length block */
((char *) &hdr)[ii++] = byte;
if (ii == sizeof(hdr)) {
ack = (hdr.length == ~hdr.notLen);
state++;
ii=0;
} else
ack = 1;
break;
case RCV_DATA: /* recieving data */
/*
note:
NAK on queue full
*/
ack = BYTEQUEUE_enqueue(&info->rcv, byte) != FALSE;
if (++ii == hdr.length)
ack = FALSE;
break;
}
}
/*
send ack
*/
sync ^= IN_SYNC;
outportb(dPort, out_nybble[ack] | (sync ? OUT_SYNC : 0));
}
} while (ready && ack);
/*
wait for sender to set the line low before proceeding
*/
{
unsigned now = WCLOCK; /* setup timeout variables */
int tickCt = WTICKS;
BYTE dNybble;
do {
if (now != WCLOCK) { /* check timeout */
tickCt--;
now = WCLOCK;
}
dNybble = inportb(sPort); /* get nybble */
ready = !(dNybble & IN_SYNC);
} while (!ready && tickCt);
}
/*
at this point, either normal completion <<or>> timeout
either way, it's OK to exit
*/
outportb(dPort, 0x00); /* set dataline low */
outportb(cPort, (info->old) ? 0x14 : 0x04);
--info->lock;
}
/*not shown: PPIO(), infoArray, and handler defintions*/
PP *PP_Open(int lpt, int irq, int basePort, int rcvBufSize, PPMODE mode)
{
BYTE *ptr;
PP *info;
int handlerNo = -1;
if (rcvBufSize >= 0) {
for (handlerNo=0;
(handlerNo < STRUCTCOUNT(infoArray)) && infoArray[handlerNo];
handlerNo++)
{
}
if (handlerNo == STRUCTCOUNT(infoArray))
return 0;
}
ptr = calloc(1, sizeof(PP) + abs(rcvBufSize));
if (!ptr)
return NULL;
info = (void *) ptr;
infoArray[handlerNo] = info;
info->handlerNo = handlerNo;
info->rcv.len = abs(rcvBufSize);
info->rcv.buf = ptr + sizeof(*info);
info->mode = mode;
if (lpt > 0) {
info->basePort = *(WORD *) MK_FP(0x40, 0x08+2*(lpt-1));
info->irq = 0x07;
}
if (basePort >= 0)
info->basePort = basePort;
if (irq >= 0)
info->irq = irq;
basePort = info->basePort;
if (rcvBufSize >= 0)
info->old = initIRQ(info->irq, handlerFn[handlerNo]);
outportb(basePort+2, (info->old) ? 0x14 : 0x04);
outportb(basePort, 0x00); /* set data lines low */
return info;
}
void PP_Close(PP *info)
{
if (info->old) {
infoArray[info->handlerNo] = 0;
outportb(info->basePort+2, 0x04);
cleanupIRQ(info->irq, info->old);
free(info);
}
}
typedef enum {
SND_WAKEUP = 0,
SND_HEADER,
SND_DATA,
SND_DATAEND,
SND_FINISHED
} SNDSTATE;
int PP_Write(PP *info, void *buf, int len)
{
int dPort = info->basePort; /* data port */
int sPort = dPort+1; /* status */
int cPort = dPort+2; /* control */
LENGTHBLOCK hdr; /* length header */
unsigned ii; /* misc. index */
BYTE sync = IN_SYNC; /* sync state */
PPMODE mode = PP_UNKNOWN; /* output mode */
BOOL ready; /* FALSE = timeout or NAK */
SNDSTATE state = SND_WAKEUP; /* initial state */
BYTE byte; /* output byte */
WORD now; /* timeout var.s */
int tickCt;
BYTE dNybble;
unsigned sent = 0;
++info->lock; /* lock reads */
outportb(cPort, 0x04); /* disable hardware interrupts */
hdr.length = len; /* setup header */
hdr.notLen = ~len;
do {
if (state == SND_WAKEUP) {
byte = 0; /* send wakeup immediatly! */
ii = 0; /* start with 1st header byte */
state++;
} else if ((mode == PP_BYTE) || !sync) {
switch (state) {
case SND_HEADER:
byte = ((BYTE *) (&hdr))[ii];
if (++ii == sizeof(hdr)) {
/*
header finished, move on to data
*/
state++;
ii = 0;
}
break;
case SND_DATA:
byte = ((BYTE *) buf)[ii];
sent++;
if (++ii == len) {
/*
finished!
*/
state++;
ii = 0;
}
break;
case SND_DATAEND: /* the last byte has been acknowledged! */
state++;
/*sync = IN_SYNC; /* force wait for data line to drop */
break;
}
} else
byte >>= 4; /* send high nybble */
/*
the finished state outputs <<no>> data
*/
if (state != SND_FINISHED) {
/*
send low nybble of (byte)
*/
if (mode == PP_BYTE)
outportb(cPort, byte >> 4);
sync ^= IN_SYNC;
outportb(dPort, out_nybble[(byte & 0x0f)] | (sync ? 0 : OUT_SYNC));
}
/*
wait for ACK
*/
now = WCLOCK;
tickCt = WTICKS;
do {
if (now != WCLOCK) {
tickCt--;
now = WCLOCK;
}
dNybble = inportb(sPort);
ready = (dNybble & IN_SYNC) ^ sync;
} while (!ready && tickCt);
if (mode == PP_UNKNOWN)
mode = in_nybble[(dNybble >> 3) & 0x1f];
} while (ready && (state != SND_FINISHED));
outportb(dPort, 0x00); /* clear data port */
outportb(cPort, (info->old) ? 0x14 : 0x04);
--info->lock;
return sent;
}
int PP_WaitByte(PP *info, int timeout)
{
WORD now = WCLOCK;
while (timeout && !PP_RecievePending(info)) {
if (!info->old)
PP_rcv(info);
if (WCLOCK != now) {
now = WCLOCK;
timeout--;
}
}
return BYTEQUEUE_dequeue(&info->rcv);
}
int PP_Read(PP *info, void *buf, int len)
{
int ii;
char *ptr = (char *) buf;
int timeout = 36;
for (ii=0; (ii < len); ii++) {
int ch = PP_WaitByte(info, timeout);
timeout=0;
if (ch < 0)
return ii;
ptr[ii] = ch;
}
return len;
}
/* End of File */