1
0
mirror of https://github.com/prirun/p50em.git synced 2026-04-03 11:52:47 +00:00

devamlc: major revision to interrupt only on the clock line board,

and in general, optimize operation of the AMLC subsystem.
This commit is contained in:
Jim
2011-09-15 18:27:33 -04:00
parent efada91773
commit f912f5dcc5

539
devamlc.h
View File

@@ -1,6 +1,54 @@
#ifndef HOBBY
/*
Implements the AMLC subsystem for Primos. In earlier versions
(r186), a more hardware-centric implementation was used that closely
followed the design of Prime's real AMLC board. For example, every
board generated its own interrupts. This version uses knowledge of
how the Primos AMLDIM software operates for a more efficient
implementation. There are several key points:
- up to 8 AMLC boards are supported, with 16 lines on each
- an internal terminal server allocates lines as telnet connections
are received. Connections from specific IP addresses can be
mapped to specific AMLC lines.
- hardware serial ports are supported via USB serial devices and
an amlc.cfg config file that ties specific AMLC lines to Unix
devices. Unix flow control can be enabled.
- only QAMLC is implemented, not the older DMT-based boards
- the last line of the last board is the only line with per-character
interrupts enabled; this is the "clock line", and the baud rate
set on this line determined the general polling frequency for
AMLDIM. This is simulated here with AMLCPOLL, and set to 10 polls
per second - typical for a real Prime.
- AMLDIM processes *every* AMLC board's input whenever *any* board
interrupts with an end-of-range (EOR). So to optimize, only the
clock-line board generates EOR interrupts in this implementation.
AMLDIM does not fill output queues on just an EOR interrupt.
- AMLDIM fills *every* AMLC board's output queue only when the
clock line board interrupts with the "character time interrupt"
(CTI) bit set; it also reads *every* AMLC board's input. This
is why normal interactive typing works: no EOR occurs for single
characters; they are read periodically when CTI occurs
- the AMLDIM processes needs the periodic clock line CTI interrupts
to cause it to run periodically, but does not rely on the frequency
of the interrupts for timing; it uses the Primos clock process
timers for real-time activities. This allows the emulator the
flexibility of increasing the poll rate when there is demand.
NOTE: varying the AMLC poll rates dynamically is the default, but
this can lead to unpredictable performance. If consistent,
predictable performance is more important than absolute
performance, MAXAMLCSPEEDUP can be set to 1. Then this
implementation will function more or less like a real Prime.
AMLC I/O operations:
OCP '0054 - stop clock
@@ -108,27 +156,25 @@ AMLC status word (from AMLCT5):
/* macro to setup the next AMLC poll */
#define AMLC_SET_POLL \
if ((dc[dx].ctinterrupt || dc[dx].xmitenabled || (dc[dx].recvenabled & dc[dx].connected))) \
if (devpoll[device] == 0 || devpoll[device] > AMLCPOLL*gvp->instpermsec/pollspeedup) \
devpoll[device] = AMLCPOLL*gvp->instpermsec/pollspeedup; /* setup another poll */
if (devpoll[device] == 0 || devpoll[device] > AMLCPOLL*gvp->instpermsec/pollspeedup) \
devpoll[device] = AMLCPOLL*gvp->instpermsec/pollspeedup; /* setup another poll */
int countbits (int v) {
v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
}
int devamlc (int class, int func, int device) {
#define MAXLINES 128
#define MAXBOARDS 8
#define MAXAMLCSPEEDUP 16
/* check for new connections every .1 seconds */
#define AMLCACCEPTSECS .10
/* AMLC poll rate (ms). Max data rate = queue size*1000/AMLCPOLL
The max AMLC output queue size is 1023 (octal 2000), so a poll
rate of 33 (1000/33 = 30 times per second) will generate about
31,000 chars per second. This rate may be further boosted if
there are DMQ buffers with 255 or more characters. */
there are DMQ buffers with 1023 or more characters. */
#define AMLCPOLL 100
@@ -168,17 +214,20 @@ int devamlc (int class, int func, int device) {
/* telnet options */
#define TN_BINARY 0
#define TN_ECHO 1
#define TN_BINARY 0 /* put telnet client in binary mode */
#define TN_ECHO 1 /* we echo, not telnet client */
#define TN_SGA 3 /* means this is a full-duplex connection */
#define TN_KERMIT 47
#define TN_SUBOPT 250
static short inited = 0;
static int pollspeedup = 1;
static int wascti = 0;
static int anyeor = 0;
static float pollspeedup = 1;
static int baudtable[16] = {1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200};
static int tsfd; /* socket fd for terminal server */
static int haveob = 0; /* true if there are outbound socket lines */
static struct timeval timeout = {0, 0};
static double acceptts = 0; /* timestamp for next accept */
static struct {
unsigned short deviceid; /* this board's device ID */
unsigned short dmcchan; /* DMC channel (for input) */
@@ -206,7 +255,11 @@ int devamlc (int class, int func, int device) {
char eor; /* 1=End of Range on input */
} dc[MAXBOARDS];
int dx, dx2, lx, lcount, activelines;
struct timeval tv;
double ts;
fd_set readfds;
int neweor;
int dx, dxsave, lx, lcount, activelines;
unsigned short utempa;
unsigned int utempl;
ea_t qcbea, dmcea, dmcbufbegea, dmcbufendea;
@@ -219,7 +272,6 @@ int devamlc (int class, int func, int device) {
unsigned int addrlen;
char buf[1024]; /* max size of DMQ buffer */
int i, j, n, maxn, n2, nw;
struct timeval timeout;
unsigned char ch;
int tstate, toper;
int allbusy;
@@ -261,6 +313,7 @@ int devamlc (int class, int func, int device) {
fprintf(stderr, "devamlc: non-AMLC device id '%o ignored\n", device);
return -1;
}
dxsave = dx;
switch (class) {
@@ -277,18 +330,18 @@ int devamlc (int class, int func, int device) {
/* initially, we don't know about any AMLC boards */
for (dx2=0; dx2<MAXBOARDS; dx2++) {
dc[dx2].deviceid = 0;
dc[dx2].connected = 0;
dc[dx2].serial = 0;
for (dx=0; dx<MAXBOARDS; dx++) {
dc[dx].deviceid = 0;
dc[dx].connected = 0;
dc[dx].serial = 0;
for (lx = 0; lx < 16; lx++) {
dc[dx2].fd[lx] = -1;
dc[dx2].tstate[lx] = TS_DATA;
dc[dx2].lconf[lx] = 0;
dc[dx2].ctype[lx] = CT_SOCKET;
dc[dx2].modemstate[lx] = 0;
dc[dx].fd[lx] = -1;
dc[dx].tstate[lx] = TS_DATA;
dc[dx].lconf[lx] = 0;
dc[dx].ctype[lx] = CT_SOCKET;
dc[dx].modemstate[lx] = 0;
}
dc[dx2].recvlx = 0;
dc[dx].recvlx = 0;
}
/* read the amlc.cfg file. This file has 3 uses:
@@ -344,7 +397,7 @@ int devamlc (int class, int func, int device) {
continue;
}
//printf("devamlc: lc=%d, line '%o (%d) set to device %s\n", lc, i, i, devname);
dx2 = i/16;
dx = i/16;
lx = i & 0xF;
if (devname[0] == '/') { /* USB serial port */
if ((fd = open(devname, O_RDWR | O_NONBLOCK | O_EXLOCK)) == -1) {
@@ -352,10 +405,10 @@ int devamlc (int class, int func, int device) {
continue;
}
printf("em: connected AMLC line '%o (%d) to device %s\n", i, i, devname);
dc[dx2].fd[lx] = fd;
dc[dx2].connected |= BITMASK16(lx+1);
dc[dx2].serial = 1;
dc[dx2].ctype[lx] = CT_SERIAL;
dc[dx].fd[lx] = fd;
dc[dx].connected |= BITMASK16(lx+1);
dc[dx].serial = 1;
dc[dx].ctype[lx] = CT_SERIAL;
} else {
/* might be IP address:port for outbound telnet (printers) */
@@ -383,11 +436,11 @@ int devamlc (int class, int func, int device) {
}
} else
tempport = 0;
dc[dx2].obhost[lx] = *(unsigned int *)host->h_addr;
dc[dx2].obport[lx] = tempport;
dc[dx2].ctype[lx] = CT_DEDIP;
dc[dx].obhost[lx] = *(unsigned int *)host->h_addr;
dc[dx].obport[lx] = tempport;
dc[dx].ctype[lx] = CT_DEDIP;
haveob = 1;
//printf("Dedicated socket, host=%x, port=%d, cont=%d, line=%d\n", dc[dx2].obhost[lx], tempport, dx2, lx);
//printf("Dedicated socket, host=%x, port=%d, cont=%d, line=%d\n", dc[dx].obhost[lx], tempport, dx, lx);
}
}
}
@@ -434,6 +487,7 @@ int devamlc (int class, int func, int device) {
/* this part of initialization occurs for every AMLC board */
dx = dxsave;
if (!inited || tport == 0)
return -1;
@@ -524,21 +578,24 @@ int devamlc (int class, int func, int device) {
IOSKIP;
} else if (func == 07) { /* input AMLC status */
crs[A] = 040000 | (dc[dx].bufnum<<8) | (dc[dx].intenable<<5) | (1<<4);
if (dc[dx].eor) {
crs[A] |= 0100000;
dc[dx].eor = 0;
}
if (dc[dx].ctinterrupt)
if (dc[dx].ctinterrupt & 0xfffe)
crs[A] |= 0xcf; /* multiple char time interrupt */
else
crs[A] |= 0x8f; /* last line cti */
dc[dx].interrupting = 0;
crs[A] = 040000 | (dc[dx].bufnum<<8) | (dc[dx].intenable<<5) | (1<<4);
if (anyeor) {
crs[A] |= 0100000;
for (dx=0; dx<MAXBOARDS; dx++)
dc[dx].eor = 0;
anyeor = 0;
}
if (wascti) {
crs[A] |= 0x8f; /* last line cti */
wascti = 0;
}
//printf("INA '07%02o returns 0x%x\n", device, crs[A]);
IOSKIP;
if (crs[A] & 0100000)
if (crs[A] & 0100000) {
maxxmit = 0; /* ugh! sloppy... */
goto dorecv;
}
} else if (func == 011) { /* input ID */
crs[A] = 020000 | 054; /* 020000 = QAMLC */
@@ -715,7 +772,12 @@ int devamlc (int class, int func, int device) {
dc[dx].recvenabled |= BITMASK16(lx+1);
else
dc[dx].recvenabled &= ~BITMASK16(lx+1);
AMLC_SET_POLL;
if (dc[dx].ctinterrupt)
AMLC_SET_POLL;
IOSKIP;
/* this is a new "experimental" OTA */
} else if (func == 03) { /* set room in input buffer */
IOSKIP;
} else if (func == 014) { /* set DMA/C channel (for input) */
@@ -744,14 +806,15 @@ int devamlc (int class, int func, int device) {
break;
case 4:
maxxmit = 0;
//printf("poll device '%o, speedup=%d, cti=%x, xmit=%x, recv=%x, dss=%x\n", device, pollspeedup, dc[dx].ctinterrupt, dc[dx].xmitenabled, dc[dx].recvenabled, dc[dx].dss);
//printf("poll device '%o, speedup=%5.2f, cti=%x, xmit=%x, recv=%x, dss=%x\n", device, pollspeedup, dc[dx].ctinterrupt, dc[dx].xmitenabled, dc[dx].recvenabled, dc[dx].dss);
/* check for 1 new telnet connection on each AMLC poll of the clock line */
/* check for 1 new telnet connection 10 times per second */
if (dc[dx].ctinterrupt) {
if (gettimeofday(&tv, NULL) != 0)
fatal("amlc gettimeofday failed");
ts = tv.tv_sec + tv.tv_usec/1000000.0;
if (ts > acceptts) {
acceptts += AMLCACCEPTSECS;
addrlen = sizeof(addr);
fd = accept(tsfd, (struct sockaddr *)&addr, &addrlen);
if (fd == -1) {
@@ -786,7 +849,6 @@ int devamlc (int class, int func, int device) {
dc[i].connected |= BITMASK16(lx+1);
dc[i].fd[lx] = fd;
dc[i].tstate[lx] = TS_DATA;
devpoll[dc[i].deviceid] = AMLCPOLL*gvp->instpermsec;
//printf("em: AMLC connection, fd=%d, device='%o, line=%d\n", fd, dc[i].deviceid, lx);
goto endconnect;
}
@@ -850,9 +912,11 @@ int devamlc (int class, int func, int device) {
output buffer from the previous terminal session will be
displayed to the new user. */
if (dc[dx].xmitenabled) {
maxxmit = 0;
for (dx=0; dx<MAXBOARDS; dx++) {
if (!dc[dx].deviceid || !dc[dx].xmitenabled)
continue;
for (lx = 0; lx < 16; lx++) {
if (dc[dx].xmitenabled & BITMASK16(lx+1)) {
n = 0;
qcbea = dc[dx].baseaddr + lx*4;
@@ -904,9 +968,10 @@ int devamlc (int class, int func, int device) {
XXX: Might be good to keep write stats here, to
decide how many chars to dequeue above and/or how
often to do writes. There's overhead if large DMQ
buffers are used and Unix buffers get full so writes
can't complete */
often to do writes, or do a write select on each line
first. There's overhead if large DMQ buffers are
used and Unix buffers get full so writes can't
complete */
qtop = (qtop & ~qmask) | ((qtop+nw) & qmask);
put16io(qtop, qcbea);
@@ -938,28 +1003,6 @@ int devamlc (int class, int func, int device) {
}
}
/* the largest DMQ buffer size is 1023 chars. If any line's DMQ
buffer is getting filled completely, then we need to poll
faster to increase throughput. If the max queue size falls
below 256, then decrease the interrupt rate. Anywhere between
256-1022, leave the poll rate alone.
*/
#if 1
if (maxxmit >= 1023) {
if (pollspeedup < 8) {
pollspeedup++;
//printf("%d ", pollspeedup);
//fflush(stdout);
}
} else if (pollspeedup > 1 && maxxmit < 256) {
pollspeedup--;
//printf("%d ", pollspeedup);
//fflush(stdout);
}
#endif
/* process input, but only as much as will fit into the DMC
buffer.
@@ -975,162 +1018,181 @@ int devamlc (int class, int func, int device) {
causing data from the terminal to be dropped. */
dorecv:
activelines = countbits(dc[dx].connected & dc[dx].recvenabled);
if (activelines && !dc[dx].eor) {
if (dc[dx].bufnum)
dmcea = dc[dx].dmcchan + 2;
else
dmcea = dc[dx].dmcchan;
dmcpair = get32io(dmcea);
dmcbufbegea = dmcpair>>16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = dmcbufendea - dmcbufbegea + 1;
//printf("AMLC: dmcnw=%d\n", dmcnw);
lx = dc[dx].recvlx;
for (lcount = 0; lcount < 16 && dmcnw > 0; lcount++) {
neweor = 0;
for (dx=0; dx<MAXBOARDS; dx++) {
if (dc[dx].deviceid == 0 || dc[dx].connected == 0 || dc[dx].eor)
continue;
/* select to see which lines have data to be read */
FD_ZERO(&readfds);
n = -1;
for (lx = 0; lx < 16; lx++) {
if ((dc[dx].connected & dc[dx].recvenabled & BITMASK16(lx+1))) {
/* dmcnw is the # of characters left in the dmc buffer (each
character occupies 2 bytes or 1 16-bit word) */
n2 = dmcnw / activelines;
if (n2 > sizeof(buf))
n2 = sizeof(buf);
activelines -= 1;
fd = dc[dx].fd[lx];
n = read(fd, buf, n2);
FD_SET(fd, &readfds);
if (fd > n)
n = fd;
}
}
activelines = select(n+1, &readfds, NULL, NULL, &timeout);
if (activelines == -1) {
if (errno == EINTR || errno == EAGAIN)
activelines = 0;
else {
perror("devamlc: unable to do read select");
fatal(NULL);
}
}
if (activelines) {
if (dc[dx].bufnum)
dmcea = dc[dx].dmcchan + 2;
else
dmcea = dc[dx].dmcchan;
dmcpair = get32io(dmcea);
dmcbufbegea = dmcpair>>16;
dmcbufendea = dmcpair & 0xffff;
dmcnw = dmcbufendea - dmcbufbegea + 1;
//printf("AMLC: dmcnw=%d for %o, activelines=%d\n", dmcnw, dc[dx].deviceid, activelines);
lx = dc[dx].recvlx;
for (lcount = 0; lcount < 16 && dmcnw > 0; lcount++) {
fd = dc[dx].fd[lx];
if (fd >= 0 && FD_ISSET(fd, &readfds)) {
//printf("processing recv on device %o, line %d, b#=%d, n2=%d, n=%d\n", device, lx, dc[dx].bufnum, n2, n);
/* dmcnw is the # of characters left in the dmc buffer (each
character occupies 2 bytes or 1 16-bit word) */
/* zero length read means the fd has been closed */
n2 = dmcnw / activelines;
if (n2 > sizeof(buf))
n2 = sizeof(buf);
activelines -= 1;
n = read(fd, buf, n2);
if (n == 0) {
n = -1;
errno = EPIPE;
}
if (n == -1) {
n = 0;
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
;
else if (errno == EPIPE || errno == ECONNRESET || errno == ENXIO) {
AMLC_CLOSE_LINE;
} else {
perror("Reading AMLC");
//printf("processing recv on device %o, line %d, b#=%d, n2=%d, n=%d\n", device, lx, dc[dx].bufnum, n2, n);
/* zero length read means the fd has been closed */
if (n == 0) {
n = -1;
errno = EPIPE;
}
}
/* very primitive support here for telnet - only enough to
ignore commands sent by the telnet client. Telnet
commands could be split across reads and AMLC interrupts,
so a small state machine is used for each line.
XXX: need to respond to remote telnet server commands...
For direct serial connections, the line stays in TS_DATA
state so no telnet processing occurs. */
if (n > 0) {
//printf("devamlc: RECV dx=%d, lx=%d, b=%d, tried=%d, read=%d\n", dx, lx, dc[dx].bufnum, n2, n);
tstate = dc[dx].tstate[lx];
for (i=0; i<n; i++) {
ch = buf[i];
switch (tstate) {
case TS_DATA:
if (ch == TN_IAC && (dc[dx].ctype[lx] == CT_SOCKET || dc[dx].ctype[lx] == CT_DEDIP))
tstate = TS_IAC;
else {
storech:
utempa = lx<<12 | 0x0200 | ch;
put16io(utempa, dmcbufbegea);
//printf("******* stored character %o (%c) at %o\n", utempa, utempa&0x7f, dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1);
dmcnw--;
}
break;
case TS_IAC:
switch (ch) {
case TN_IAC:
tstate = TS_DATA;
goto storech;
case TN_WILL:
case TN_WONT:
case TN_DO:
case TN_DONT:
//printf("\nReceived command %d\n", ch);
tstate = TS_OPTION;
toper = ch;
break;
case TN_SUBOPT:
//printf("\nReceived SUBOPT command\n");
tstate = TS_SUBOPT;
break;
default: /* ignore other chars after IAC */
//printf("\nReceived unknown IAC command %d\n", ch);
tstate = TS_DATA;
}
break;
case TS_SUBOPT:
//printf("\nsubopt received %d\n", ch);
if (ch == TN_IAC)
tstate = TS_IAC;
break;
case TS_OPTION:
//printf("\nReceived option %d\n", ch);
if (toper == TN_WILL) {
if (ch == TN_BINARY)
;
else {
buf[0] = TN_IAC;
buf[1] = TN_DONT;
buf[2] = ch;
//printf("Sending DONT %d\n", ch);
write(fd, buf, 3);
}
} else if (toper == TN_DO) {
if (ch == TN_ECHO || ch == TN_SGA)
;
else {
buf[0] = TN_IAC;
buf[1] = TN_WONT;
buf[2] = ch;
//printf("Sending WONT %d\n", ch);
write(fd, buf, 3);
}
}
tstate = TS_DATA;
break;
default:
tstate = TS_DATA;
if (n == -1) {
n = 0;
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
;
else if (errno == EPIPE || errno == ECONNRESET || errno == ENXIO) {
AMLC_CLOSE_LINE;
} else {
perror("Reading AMLC");
}
}
dc[dx].tstate[lx] = tstate;
/* very primitive support here for telnet - only enough to
ignore commands sent by the telnet client. Telnet
commands could be split across reads and AMLC interrupts,
so a small state machine is used for each line.
For direct serial connections, the line stays in TS_DATA
state so no telnet processing occurs. */
if (n > 0) {
//printf("devamlc: RECV dx=%d, lx=%d, b=%d, tried=%d, read=%d\n", dx, lx, dc[dx].bufnum, n2, n);
tstate = dc[dx].tstate[lx];
for (i=0; i<n; i++) {
ch = buf[i];
switch (tstate) {
case TS_DATA:
if (ch == TN_IAC && (dc[dx].ctype[lx] == CT_SOCKET || dc[dx].ctype[lx] == CT_DEDIP))
tstate = TS_IAC;
else {
storech:
utempa = lx<<12 | 0x0200 | ch;
put16io(utempa, dmcbufbegea);
//printf("******* stored character %o (%c) at %o\n", utempa, utempa&0x7f, dmcbufbegea);
dmcbufbegea = INCVA(dmcbufbegea, 1);
dmcnw--;
}
break;
case TS_IAC:
switch (ch) {
case TN_IAC:
tstate = TS_DATA;
goto storech;
case TN_WILL:
case TN_WONT:
case TN_DO:
case TN_DONT:
//printf("\nReceived command %d\n", ch);
tstate = TS_OPTION;
toper = ch;
break;
case TN_SUBOPT:
//printf("\nReceived SUBOPT command\n");
tstate = TS_SUBOPT;
break;
default: /* ignore other chars after IAC */
//printf("\nReceived unknown IAC command %d\n", ch);
tstate = TS_DATA;
}
break;
case TS_SUBOPT:
//printf("\nsubopt received %d\n", ch);
if (ch == TN_IAC)
tstate = TS_IAC;
break;
case TS_OPTION:
//printf("\nReceived option %d\n", ch);
if (toper == TN_WILL) {
if (ch == TN_BINARY)
;
else {
buf[0] = TN_IAC;
buf[1] = TN_DONT;
buf[2] = ch;
//printf("Sending DONT %d\n", ch);
write(fd, buf, 3);
}
} else if (toper == TN_DO) {
if (ch == TN_ECHO || ch == TN_SGA)
;
else {
buf[0] = TN_IAC;
buf[1] = TN_WONT;
buf[2] = ch;
//printf("Sending WONT %d\n", ch);
write(fd, buf, 3);
}
}
tstate = TS_DATA;
break;
default:
tstate = TS_DATA;
}
}
dc[dx].tstate[lx] = tstate;
}
}
lx = (lx+1) & 0xF;
}
dc[dx].recvlx = lx;
if (dmcbufbegea-1 > dmcbufendea)
fatal("AMLC tumble table overflowed?");
put16io(dmcbufbegea, dmcea);
if (dmcbufbegea > dmcbufendea) { /* end of range has occurred */
dc[dx].bufnum = 1-dc[dx].bufnum;
dc[dx].eor = 1;
neweor = 1;
anyeor = 1;
}
lx = (lx+1) & 0xF;
}
dc[dx].recvlx = lx;
if (dmcbufbegea-1 > dmcbufendea)
fatal("AMLC tumble table overflowed?");
put16io(dmcbufbegea, dmcea);
if (dmcbufbegea > dmcbufendea) { /* end of range has occurred */
dc[dx].bufnum = 1-dc[dx].bufnum;
dc[dx].eor = 1;
}
}
/* time to interrupt?
/* time to interrupt? */
XXX: might be a bug here: with multiple controllers, maybe only
the last controller (with ctinterrupt set) will get high
performance (higher poll rates) while others will get the
standard poll rate. If a non-clock-line controller wants
faster polling, we probably need to generate an interrupt on
the clock line controller to cause AMLDIM to refill the
buffers, or force the ctinterrupt status bit to be returned
on the next status request.
*/
if (dc[dx].intenable && (dc[dx].ctinterrupt || dc[dx].eor)) {
dx = dxsave;
if (class == 4) /* this was a poll */
wascti = 1;
if (dc[dx].intenable && (wascti || neweor)) {
if (gvp->intvec == -1) {
gvp->intvec = dc[dx].intvector;
dc[dx].interrupting = 1;
@@ -1138,15 +1200,28 @@ dorecv:
devpoll[device] = 100; /* come back soon! */
}
/* conditions to setup another poll:
- any board with ctinterrupt set on any line is polled
- any board with xmitenabled on any line is polled (to drain output)
- any board with recvenabled on a connected line is polled
- the device must not already be set for a poll
/* the largest DMQ buffer size is 1023 chars. If any line's DMQ
buffer is getting filled completely, then we need to poll
faster to increase throughput. Otherwise, poll slower to
decrease the interrupt rate. We test against 1023 instead of
the actual queue size, because if they want high performance,
using a smaller queue size is not optimal and they probably
don't care about performance.
NOTE: there is always at least one board with ctinterrupt set
(the last board), so it will always be polling and checking
for new incoming connections */
If any line had an eor this time, poll faster next time.
*/
if (maxxmit >= 1023 || neweor) {
if (pollspeedup < MAXAMLCSPEEDUP) {
pollspeedup += .5;
//printf("%d ", pollspeedup); fflush(stdout);
}
} else if (pollspeedup > 1) {
pollspeedup -= .25;
//printf("%d ", pollspeedup); fflush(stdout);
}
/* setup another poll */
AMLC_SET_POLL;
break;