[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: MiNTlib (I think I'm on pl43): Bug in sprintf().
Kay Roemer writes:
> > Probably normal, given that it's really polling at 60 times per second.
> > So up to 30-some characters can arrive (at 19200) before polling, and then
> > checkbttys is only called on a context switch, so yes, there could be a
> > large number of characters waiting before select returns...
>
> Well, I'm not taking of 30 characters, but about a screen full of them, ie
> about 80x25=2000! When I use modm0dev which polls at 5 times per second
> using a separate porcess I get much smaller hunks, about 500.
thats on a 68000 right? i guess you just found the `feature' that a
RAW tty read is slower copying stuff out of the receiver buffer than
data comes in at 19200 bps... sounds unbelievable hmm? ;) well, try this!
(relative to 10h6)
1. fix range checks, eliminate some dead code...
Index: bios.c
@@ -80,24 +80,24 @@
/* and then do BCOSTAT ourselves, the BIOS SCC ones are often broken */
#define BCOSTAT(dev) \
- (((unsigned)dev <= 4 && tosvers > 0x0102) ? \
- (int)callout1(xcostat[dev], dev) : \
- ((has_bconmap && (unsigned)((dev-SERDEV) <= btty_max)) ? \
+ (((unsigned)dev <= 4) ? ((tosvers > 0x0102) ? \
+ (int)callout1(xcostat[dev], dev) : Bcostat(dev)) : \
+ ((has_bconmap && (unsigned)dev-SERDEV < btty_max) ? \
bcxstat(MAPTAB[dev-SERDEV].iorec+1) : Bcostat(dev)))
#define BCONOUT(dev, c) \
- (((unsigned)dev <= 4 && tosvers > 0x0102) ? \
- callout2(xconout[dev], dev, c) : \
- ((has_bconmap && (unsigned)((dev-SERDEV) <= btty_max)) ? \
+ (((unsigned)dev <= 4) ? ((tosvers > 0x0102) ? \
+ callout2(xconout[dev], dev, c) : Bconout(dev, c)) : \
+ ((has_bconmap && (unsigned)dev-SERDEV < btty_max) ? \
callout2(MAPTAB[dev-SERDEV].bconout, dev, c) : Bconout(dev, c)))
#define BCONSTAT(dev) \
- (((unsigned)dev <= 4 && tosvers > 0x0102) ? \
- (int)callout1(xconstat[dev], dev) : \
- ((has_bconmap && (unsigned)((dev-SERDEV) <= btty_max)) ? \
+ (((unsigned)dev <= 4) ? ((tosvers > 0x0102) ? \
+ (int)callout1(xconstat[dev], dev) : Bconstat(dev)) : \
+ ((has_bconmap && (unsigned)dev-SERDEV < btty_max) ? \
(int)callout1(MAPTAB[dev-SERDEV].bconstat, dev) : Bconstat(dev)))
#define BCONIN(dev) \
- (((unsigned)dev <= 4 && tosvers > 0x0102) ? \
- callout1(xconin[dev], dev) : \
- ((has_bconmap && (unsigned)((dev-SERDEV) <= btty_max)) ? \
+ (((unsigned)dev <= 4) ? ((tosvers > 0x0102) ? \
+ callout1(xconin[dev], dev) : Bconin(dev)) : \
+ ((has_bconmap && (unsigned)dev-SERDEV < btty_max) ? \
callout1(MAPTAB[dev-SERDEV].bconin, dev) : Bconin(dev)))
#else
#define BCOSTAT(dev) \
@@ -264,7 +264,14 @@
} else
h = dev-SERDEV;
- if (h >= 0 && h < btty_max) {
+ if ((unsigned)h < btty_max) {
+ if (has_bconmap) { /* help the compiler... :) */
+ long *statc;
+
+ while (!callout1(*(statc=&MAPTAB[dev-SERDEV].bconstat), dev))
+ sleep(IO_Q, (long)&bttys[h]);
+ return callout1(statc[1], dev);
+ }
while (!BCONSTAT(dev))
sleep(IO_Q, (long)&bttys[h]);
} else if (dev > 0) {
Index: xbios.c
@@ -145,10 +145,12 @@
uiorec(dev)
int dev;
{
+ extern short btty_max;
+
TRACE(("Iorec(%d)", dev));
if (dev == 0 && has_bconmap) {
/* get around another BIOS Bug: in (at least) TOS 2.05 Iorec(0) is broken */
- if (curproc->bconmap >= 6)
+ if ((unsigned)curproc->bconmap-6 < btty_max)
return (long)MAPTAB[curproc->bconmap-6].iorec;
mapin(curproc->bconmap);
}
@@ -174,7 +176,7 @@
/* more bugs... serial1 is three-wire, requesting hardware flowcontrol
* on it can confuse BIOS
*/
- if ((flow & 2) && (unsigned)curproc->bconmap-6 <= btty_max &&
+ if ((flow & 2) && (unsigned)curproc->bconmap-6 < btty_max &&
bttys[curproc->bconmap-6].rsel == &(ttmfp_tty.rsel))
flow &= ~2;
2. serial lines, act 2
anyone remember my idea to add shortcuts for fast RAW tty IO in the DEVDRV
struct? well, looks like it works. i can receive and display(!) nearly
full 57600 bps now using my hack of howards little `tip' program...
a while ago the same program had problems at 19200 and this 68000 runs
at 16 MHz. so its a factor >= 3 including output!
the iread() and iwrite() are mostly the ones i patched into taylor uucp 1.03
so they have been tested for some time. i hope 1.05 will no longer need
any such hacks now... (imagine uucp-i on a ST :)
oh and memcpy is an extended quickmove really (quickmovb), and the XDEF'd
_bcopy is just a hint for gcc that it doesn't need to load the libs
bcopy too.
Index: file.h
@@ -141,7 +141,15 @@
long ARGS_ON_STACK (*datime) P_((FILEPTR *f, short *timeptr, int rwflag));
long ARGS_ON_STACK (*close) P_((FILEPTR *f, int pid));
long ARGS_ON_STACK (*select) P_((FILEPTR *f, long proc, int mode));
- void ARGS_ON_STACK (*unselect) P_((FILEPTR *f, long proc, int mode));
+ void ARGS_ON_STACK (*unselect) P_((FILEPTR *f, long proc, int mode));
+/* extensions, check dev_descr.drvsize (size of DEVDRV struct) before calling:
+ * fast RAW tty byte io */
+ long ARGS_ON_STACK (*writeb) P_((FILEPTR *f, const char *buf, long bytes));
+ long ARGS_ON_STACK (*readb) P_((FILEPTR *f, char *buf, long bytes));
+/* what about: scatter/gather io for DMA devices...
+ * long ARGS_ON_STACK (*writev) P_((FILEPTR *f, const struct iovec *iov, long cnt));
+ * long ARGS_ON_STACK (*readv) P_((FILEPTR *f, const struct iovec *iov, long cnt));
+ */
} DEVDRV;
typedef struct filesys {
@@ -509,6 +517,8 @@
long *rsel; /* pointer to field in tty struct */
IOREC_T *orec; /* Same, for output... */
long *wsel;
+ long ispeed, ospeed;
+ long *baudmap, maxbaud;
};
/* Dcntl constants and types */
@@ -521,7 +531,8 @@
short dinfo;
short flags;
struct tty *tty;
- long reserved[4];
+ long drvsize; /* size of DEVDRV struct */
+ long reserved[3];
};
@@ -582,6 +593,7 @@
struct bios_file *next;
short lockpid; /* owner of the lock */
XATTR xattr; /* guess what... */
+ long drvsize; /* size of DEVDRV struct */
};
#endif /* _filesys_h */
Index: mint.h
@@ -139,6 +139,7 @@
#define strlwr MS_lwr
#define strupr MS_upr
#define sleep M_sleep
+#define memcpy quickmovb
#endif
#ifdef SHORT_NAMES
@@ -170,6 +171,10 @@
#include "proto.h"
#include "sproto.h"
#endif
+
+#ifndef offsetof
+#include <stddef.h>
+#endif
#ifndef NULL
#define NULL ((void *)0)
Index: proto.h
@@ -298,6 +298,7 @@
/* tty.c */
long tty_read P_((FILEPTR *f, void *buf, long nbytes));
+INLINE void tty_checkttou P_((FILEPTR *f, struct tty *tty));
long tty_write P_((FILEPTR *f, const void *buf, long nbytes));
long tty_ioctl P_((FILEPTR *f, int mode, void *arg));
long tty_getchar P_((FILEPTR *f, int mode));
@@ -336,6 +337,8 @@
/* biosfs.c */
void biosfs_init P_((void));
+long iwrite P_((int bdev, const char *buf, long bytes, int ndelay, struct bios_file *b));
+long iread P_((int bdev, char *buf, long bytes, int ndelay, struct bios_file *b));
void ARGS_ON_STACK mouse_handler P_((const char *buf));
long ARGS_ON_STACK nocreat P_((fcookie *dir, const char *name, unsigned mode, int attrib,
fcookie *fc));
Index: bios.c
@@ -694,7 +694,11 @@
return 0;
}
if (is_terminal(f)) {
+ int oldflags = f->flags;
+
s = bconbuf;
+/* turn off NDELAY for this write... */
+ f->flags &= ~O_NDELAY;
if (dev == 5) {
while (bsiz-- > 0) {
if (*s < ' ') {
@@ -710,6 +714,24 @@
#if 1
long *where, nbytes;
+#if 1
+ extern FILESYS bios_filesys;
+
+ /* see if we can do fast RAW byte IO thru the device driver... */
+ if ((f->fc.fs != &bios_filesys ||
+ (bsiz > 1 &&
+ ((struct bios_file *)f->fc.index)->drvsize >
+ offsetof (DEVDRV, writeb))) && *f->dev->writeb) {
+ struct tty *tty = (struct tty *)f->devinfo;
+
+ tty_checkttou (f, tty);
+ if ((ret = (*f->dev->writeb)(f, s, bsiz)) != EUNDEV) {
+ f->flags = oldflags;
+ bconbdev = 0;
+ return ret;
+ }
+ }
+#endif
/* the tty_putchar should set up terminal modes correctly */
(void) tty_putchar(f, (long)*s++, RAW);
where = lbconbuf;
@@ -726,6 +748,7 @@
#endif
}
ret = -1;
+ f->flags = oldflags;
} else {
ret = (*f->dev->write)(f, (char *)bconbuf, bsiz);
}
@@ -739,6 +762,10 @@
dev = curproc->bconmap;
statdev = dev;
}
+ if ((ret = iwrite (dev, bconbuf, bsiz, 0, 0)) != EUNDEV) {
+ bconbdev = 0;
+ return ret;
+ }
/* compensate for a known BIOS bug; MIDI and IKBD are switched */
else if (dev == 3) { /* MIDI */
statdev = 4;
Index: biosfs.c
@@ -35,6 +35,8 @@
static long ARGS_ON_STACK bios_topen P_((FILEPTR *f));
static long ARGS_ON_STACK bios_twrite P_((FILEPTR *f, const char *buf, long bytes));
static long ARGS_ON_STACK bios_tread P_((FILEPTR *f, char *buf, long bytes));
+static long ARGS_ON_STACK bios_writeb P_((FILEPTR *f, const char *buf, long bytes));
+static long ARGS_ON_STACK bios_readb P_((FILEPTR *f, char *buf, long bytes));
static long ARGS_ON_STACK bios_nwrite P_((FILEPTR *f, const char *buf, long bytes));
static long ARGS_ON_STACK bios_nread P_((FILEPTR *f, char *buf, long bytes));
static long ARGS_ON_STACK bios_ioctl P_((FILEPTR *f, int mode, void *buf));
@@ -64,7 +66,8 @@
DEVDRV bios_tdevice = {
bios_topen, bios_twrite, bios_tread, bios_tseek, bios_ioctl,
- null_datime, bios_close, bios_select, bios_unselect
+ null_datime, bios_close, bios_select, bios_unselect,
+ bios_writeb, bios_readb
};
/* device driver for BIOS devices that are not terminals */
@@ -146,9 +149,22 @@
{"", 0, 0, 0, 0, 0}
};
+#define xconstat ((long *)0x51eL)
+#define xconin ((long *)0x53eL)
+#define xcostat ((long *)0x55eL)
+#define xconout ((long *)0x57eL)
+
extern BCONMAP2_T *bconmap2; /* bconmap struct */
#define MAPTAB (bconmap2->maptab)
+#define MAXBAUD 16
+
+/* keep these sorted in descending order */
+static long baudmap[MAXBAUD] = {
+19200L, 9600L, 4800L, 3600L, 2400L, 2000L, 1800L, 1200L,
+600L, 300L, 200L, 150L, 134L, 110L, 75L, 50L
+};
+
/* set/reset bits in SCC w5 */
INLINE static void scc_set5 (control, setp, bits, iorec)
volatile char *control;
@@ -183,6 +199,8 @@
struct bios_tty bttys[MAX_BTTY];
short btty_max;
+extern int tosvers; /* from main.c */
+
/* Does the fcookie fc refer to the \dev\fd directory? */
#define IS_FD_DIR(fc) ((fc)->aux == S_IFDIR)
/* Does the fcookie fc refer to a file in the \dev\fd directory? */
@@ -254,6 +272,10 @@
/* Save a pointer to the first serial port */
if (b->private == 6)
c = b;
+ if (b->device->readb && b->tty != &con_tty)
+ /* device has DEVDRV calls beyond unselect */
+ b->drvsize = offsetof (DEVDRV, readb) + sizeof (long);
+
/* if not a TT or Mega STE, adjust the MODEM1 device to be BIOS
* device 1
* and ignore the remaining devices, since they're not present
@@ -281,12 +303,18 @@
}
/* Initialize bios_tty structures */
for (i=0;c && i<MAX_BTTY;c=c->next, i++) {
+ unsigned r;
if (has_bconmap)
Bconmap(c->private);
bttys[i].irec = Iorec(0);
bttys[i].orec = bttys[i].irec+1;
bttys[i].rsel = &(c->tty->rsel);
bttys[i].wsel = &(c->tty->wsel);
+ bttys[i].baudmap = baudmap;
+ bttys[i].maxbaud = MAXBAUD;
+ r = (int)rsconf(-2, -1, -1, -1, -1, -1);
+ Rsconf(r, -1, -1, -1, -1, -1);
+ bttys[i].ospeed = bttys[i].ispeed = r<MAXBAUD ? baudmap[r] : -1;
}
btty_max = i;
@@ -788,6 +816,7 @@
* short dinfo info for the device driver
* short flags flags for the file (e.g. O_TTY)
* struct tty *tty tty structure, if appropriate
+ * long drvsize; size of driver struct
*
* Dcntl(0xde00, "U:\DEV\BAR", n): install a new BIOS terminal device, with
* BIOS device number "n".
@@ -835,6 +864,7 @@
b->private = d->dinfo;
b->flags = d->flags;
b->tty = d->tty;
+ b->drvsize = d->drvsize;
set_xattr(&(b->xattr), S_IFCHR|DEFAULT_MODE, UNK_RDEV|devindex);
devindex = (devindex+1) & 0x00ff;
return (long)&kernelinfo;
@@ -865,7 +895,11 @@
return ENSMEM;
b->tty = ttyptr;
}
+ b->drvsize = 0;
b->device = &bios_tdevice;
+ if (b->device->readb)
+ /* device has DEVDRV calls beyond unselect */
+ b->drvsize = offsetof (DEVDRV, readb) + sizeof (long);
b->private = arg;
b->flags = O_TTY;
*b->tty = default_tty;
@@ -887,6 +921,7 @@
* who didn't recognize this change is still using it.
*/
b->tty = 0;
+ b->drvsize = 0;
b->device = &bios_ndevice;
b->private = arg;
b->flags = 0;
@@ -1221,7 +1256,7 @@
* for cooked TTY output they will, which is the only sort that
* control characters affect anyways).
*/
- if (bytes > 0 && (*r & 0x000000ffL) == '\n')
+ if (bdev == 2 && bytes > 0 && (*r & 0x000000ffL) == '\n')
(void) checkkeys();
if (f->flags & O_NDELAY) {
@@ -1277,6 +1312,335 @@
}
/*
+ * fast RAW byte IO for BIOS ttys
+ * without this a RAW tty read goes thru bios_tread for every single
+ * char, calling BIOS 3 times per byte at least... a poor 8 MHz ST
+ * just can't move real 19200 bits per second that way, before a
+ * byte crawled thru all this the next has already arrived.
+ * if the device has xcon* calls and a `normal' iorec these functions
+ * access the buffers directly using as little CPU time as possible,
+ * for other devices they return EUNDEV (== do the slow thing then).
+ * yes it is a hack but better one hack here than hacks in every
+ * user process that wants good RAW IO performance...
+ */
+
+static long ARGS_ON_STACK
+bios_writeb(f, buf, bytes)
+ FILEPTR *f; const char *buf; long bytes;
+{
+ int bdev = f->fc.aux;
+ struct bios_file *b = (struct bios_file *)f->fc.index;
+
+ return iwrite (bdev, buf, bytes, (f->flags & O_NDELAY), b);
+}
+
+/* FILEPTR-less entrypoint for bflush etc. */
+
+long
+iwrite(bdev, buf, bytes, ndelay, b)
+ int bdev; const char *buf; long bytes; int ndelay; struct bios_file *b;
+{
+ IOREC_T *ior = 0;
+ long *cout = 0; /* keep compiler happy */
+ long *ospeed;
+ const char *p = buf;
+ int slept = 0;
+
+ if (has_bconmap) {
+ if ((unsigned)bdev-6 < btty_max) {
+ ior = MAPTAB[bdev-6].iorec + 1;
+ cout = &MAPTAB[bdev-6].bconout;
+ ospeed = &bttys[bdev-6].ospeed;
+ }
+ } else if (bdev == 1 && tosvers > 0x0102) {
+ ior = bttys[0].orec;
+ cout = &xconout[1];
+ ospeed = &bttys[0].ospeed;
+ }
+
+/* no iorec, fall back to the slow way... */
+ if (!ior)
+ return EUNDEV;
+
+ if (!buf) {
+ /* flush send buffer... should be safe to just set the
+ tail pointer. */
+ ior->tail = ior->head;
+ return 0;
+ }
+
+ if (!bytes)
+ /* nothing to do... */
+ return 0;
+
+ do {
+ char ch;
+ unsigned short tail, bsize, wrap, newtail;
+ long free;
+
+ tail = ior->tail;
+ bsize = ior->buflen;
+ if ((free = (long)ior->head - tail - 1) < 0)
+ free += bsize;
+
+ /* if buffer is full or we're blocking and can't write enuf */
+ if ((unsigned long)free < 2 ||
+ (!ndelay && free < bytes && free < bsize/2)) {
+ long sleepchars;
+ unsigned long isleep;
+
+ /* if the write should not block thats it. */
+ if (ndelay)
+ return p - buf;
+
+ /* else sleep the (minimum) time it takes until
+ the buffer is either half-empty or has space
+ enough for the write, wichever is smaller. */
+ if ((sleepchars = bsize/2) > bytes)
+ sleepchars = bytes;
+ sleepchars -= free;
+
+ isleep = (unsigned long) ((sleepchars * 10000L) / *ospeed);
+
+ /* except that if we already slept and the buffer
+ still was full we sleep for at least 20
+ milliseconds. (driver must be waiting for
+ some handshake signal and we don't want to
+ hog the processor.) */
+ if (slept && isleep < 20)
+ isleep = 20;
+
+ if (isleep < 5)
+ /* if it still would be less than 5
+ milliseconds then just give up this
+ timeslice */
+ yield();
+ else if (isleep < 20)
+ nap (isleep);
+ else {
+ TIMEOUT *t;
+
+ if (isleep > 200)
+ isleep = 200;
+ t = addtimeout((long)isleep, (void (*)(PROC *))wakeselect);
+ if (t) {
+ sleep(SELECT_Q, (long)wakeselect);
+ canceltimeout(t);
+ }
+ }
+
+ /* loop and try again. */
+ slept = (unsigned long)free < 2;
+ continue;
+ }
+ slept = 0;
+
+ /* save the 1st char, we could need it later. */
+ ch = *p;
+ wrap = bsize - tail;
+ if (--free > bytes)
+ free = bytes;
+ bytes -= free;
+
+ /* now copy to buffer. if its just a few then do it here... */
+ if (free < 5) {
+ char *q = ior->bufaddr + tail;
+
+ while (free--) {
+ if (!--wrap)
+ q -= bsize;
+ *++q = *p++;
+ }
+ newtail = q - ior->bufaddr;
+
+ /* else use memcpy. */
+ } else {
+ /* --wrap and tail+1 because tail is `inc before access' */
+ if (--wrap < free) {
+ memcpy (ior->bufaddr + tail + 1, p, wrap);
+ memcpy (ior->bufaddr, p + wrap, free - wrap);
+ newtail = free - wrap - 1;
+ } else {
+ memcpy (ior->bufaddr + tail + 1, p, free);
+ newtail = tail + free;
+ }
+ p += free;
+ }
+
+ /* the following has to be done with interrupts off to avoid
+ race conditions. */
+ {
+ short sr = spl7();
+
+ /* if the buffer is empty there might be no
+ interrupt that sends the next char, so we
+ send it thru the xcon* vector. */
+ if (ior->head == ior->tail) {
+ (void) callout2(*cout, bdev, (unsigned char) ch);
+
+ /* if the buffer now is still empty set
+ the head pointer to skip the 1st char
+ (that we just sent). */
+ if (ior->head == ior->tail) {
+ if (++tail >= bsize)
+ tail = 0;
+ ior->head = tail;
+ }
+ }
+ ior->tail = newtail;
+
+ spl(sr);
+ if (b) {
+ b->xattr.mtime = b->xattr.atime = timestamp;
+ b->xattr.mdate = b->xattr.adate = datestamp;
+ }
+ }
+ /* if we're blocking loop until everything is written */
+ } while (bytes && !ndelay);
+
+ return p - buf;
+}
+
+/*
+ * fast RAW BIOS tty read
+ * this really works like a RAW tty read i.e. only blocks until _some_
+ * data is ready.
+ * TODO: implement termios VMIN, VTIME...
+ */
+
+static long ARGS_ON_STACK
+bios_readb(f, buf, bytes)
+ FILEPTR *f; char *buf; long bytes;
+{
+ int bdev = f->fc.aux;
+ struct bios_file *b = (struct bios_file *)f->fc.index;
+
+ return iread (bdev, buf, bytes, (f->flags & O_NDELAY), b);
+}
+
+long
+iread(bdev, buf, bytes, ndelay, b)
+ int bdev; char *buf; long bytes; int ndelay; struct bios_file *b;
+{
+ IOREC_T *ior = 0;
+ long *cin = 0; /* keep compiler happy */
+ long *cinstat;
+ char *p;
+ unsigned short head, bsize, wrap;
+ long left;
+
+ if (bdev == 3 && tosvers > 0x0102) {
+ /* midi */
+ ior = (IOREC_T *) uiorec(2);
+ cin = &xconin[3];
+ cinstat = &xconstat[3];
+ } else if (has_bconmap) {
+ if ((unsigned)bdev-6 < btty_max) {
+ ior = MAPTAB[bdev-6].iorec;
+ cin = &MAPTAB[bdev-6].bconin;
+ cinstat = &MAPTAB[bdev-6].bconstat;
+ }
+ } else if (bdev == 1 && tosvers > 0x0102) {
+ ior = bttys[0].irec;
+ cin = &xconin[1];
+ cinstat = &xconstat[1];
+ }
+
+/* no iorec, fall back to the slow way... */
+ if (!ior)
+ return EUNDEV;
+
+ if (buf && !bytes)
+ /* nothing to do... */
+ return 0;
+
+ /* if the read should block sleep until there is something to read */
+ if (buf && !ndelay) {
+ while (!(int)callout1(*cinstat, bdev)) {
+ if (has_bconmap && (unsigned)bdev-6 < btty_max) {
+ sleep(IO_Q, (long)&bttys[bdev-6]);
+ } else if (bdev == 1) {
+ sleep(IO_Q, (long)&bttys[0]);
+ } else
+ nap(60);
+ }
+ }
+
+ head = ior->head;
+ if (!(left = ((unsigned long) ior->tail) - head)) {
+ /* if the buffer is still empty we're finished... */
+ return 0;
+ }
+
+ /* now copy the data out of the buffer */
+ bsize = ior->buflen;
+ if (left < 0)
+ left += bsize;
+ wrap = bsize - head;
+
+ /* if we should flush input pretend we want to read it all */
+ if (!buf && !bytes)
+ bytes = left;
+
+ if (left > bytes)
+ left = bytes;
+
+ /* if its just a few then do it here... */
+ if (buf && left <= 5) {
+ char *q = ior->bufaddr + head;
+
+ /* the --left in the while() makes us get one char less
+ because we want to get the last one thru the driver
+ so that it gets a chance to raise RTS or send XON... */
+ p = buf;
+ while (--left) {
+ if (!--wrap)
+ q -= bsize;
+ *p++ = *++q;
+ }
+ ior->head = q - ior->bufaddr;
+
+ /* else memcpy is faster. */
+ } else {
+ /* --wrap and head+1 because head is `inc before access' */
+ if (--wrap < --left) {
+ if (buf) {
+ memcpy (buf, ior->bufaddr + head + 1, wrap);
+ memcpy (buf + wrap, ior->bufaddr, left - wrap);
+ }
+ ior->head = left - wrap - 1;
+ } else {
+ if (buf)
+ memcpy (buf, ior->bufaddr + head + 1, left);
+ ior->head = head + left;
+ }
+ /* p points to last char */
+ p = buf + left;
+ }
+
+ {
+ short sr = spl7();
+
+ /* xconin[] are always blocking, and we don't want to
+ hang at ipl7 even if something impossible like a
+ `magically' empty buffer happens. so check again. */
+ if (ior->tail != ior->head)
+ if (buf)
+ *p++ = callout1(*cin, bdev);
+ else
+ (void) callout1(*cin, bdev);
+ spl(sr);
+ if (b) {
+ b->xattr.atime = timestamp;
+ b->xattr.adate = datestamp;
+ }
+ }
+ if (!buf)
+ return 0;
+ return p - buf;
+}
+
+/*
* read/write routines for BIOS devices that aren't terminals (like the
* printer & IKBD devices)
*/
@@ -1346,14 +1710,6 @@
return 0;
}
-#define MAXBAUD 16
-
-/* keep these sorted in descending order */
-static long baudmap[MAXBAUD] = {
-19200L, 9600L, 4800L, 3600L, 2400L, 2000L, 1800L, 1200L,
-600L, 300L, 200L, 150L, 134L, 110L, 75L, 50L
-};
-
static long ARGS_ON_STACK
bios_ioctl(f, mode, buf)
FILEPTR *f; int mode; void *buf;
@@ -1395,16 +1751,23 @@
flushtype = (int) *r;
}
if (dev == 1 || dev >= 6) {
+ b = (struct bios_file *)f->fc.index;
if (has_bconmap && dev >= 6)
ior = MAPTAB[dev-6].iorec;
else
ior = (IOREC_T *) uiorec(0);
- if (flushtype & 1) {
+ /* just resetting iorec pointers here can hang a flow controlled port,
+ * iread can do better...
+ */
+ if ((flushtype & 1) &&
+ iread (dev, (char *) NULL, 0, 1, b) == EUNDEV) {
sr = spl7();
ior->head = ior->tail = 0;
spl(sr);
}
- if (flushtype & 2) {
+ /* sender should be ok but iwrite also sets the dev's ctime */
+ if ((flushtype & 2) &&
+ iwrite (dev, (char *) NULL, 0, 1, b) == EUNDEV) {
ior++; /* output record */
sr = spl7();
ior->head = ior->tail = 0;
Index: quickmov.spp
@@ -6,14 +6,49 @@
; quickly copy "nbytes" bytes from src to dst. Assumes that both
; src and dst are word aligned.
;
+; quickmovb(char *dst, char *src, long nbytes):
+; like memcpy, does unaligned too... does not check for overlap (memmove).
+;
TEXT
XDEF _quickmove
+ XDEF _quickmovb
+;%ifdef OWN_LIB
+ XDEF _bcopy
+ XDEF __bcopy
+
+_bcopy:
+__bcopy:
+ move.l 8(sp),a0 ; get dst
+ move.l 4(sp),a1 ; get src
+ bra.s _quickmovb1
+;%endif
+
+_quickmovb:
+ move.l 4(sp),a0 ; get dst
+ move.l 8(sp),a1 ; get src
+_quickmovb1:
+ move.w a0,d0
+ move.w a1,d1
+ eor.w d1,d0 ; bit 0 == unaligned
+ lsr.w #1,d0 ; ...now in x flag
+ move.l 12(sp),d0 ; get nbytes
+ beq.s Ldone
+ roxr.w #1,d1 ; bit 0 == both odd, msb == unaligned
+ bmi.s bytecopy ; unaligned, do the slow thing...
+ bcc.s quickmov1 ; both even, ok
+ subq.l #1,d0 ; both odd, can be fixed
+ move.b (a1)+,(a0)+
+ bra.s quickmov1
+
_quickmove:
move.l 4(sp),a0 ; get dst
move.l 8(sp),a1 ; get src
move.l 12(sp),d0 ; get nbytes
+quickmov1:
+ move.w #$1ff,d1
+ and.w d0,d1 ; d1 = nbytes % 512
lsr.l #8,d0 ;
lsr.l #1,d0 ; d0 = nbytes / 512
subq.l #1,d0 ; prepare for dbra loop
@@ -50,13 +85,34 @@
movem.l (sp)+,d1-d7/a2-a6 ; pop registers
Leftover: ; do the remaining bytes
- move.l 12(sp),d1
- and.w #$01ff,d1 ; d1 = nbytes % 512
+ moveq.l #3,d0
+ and.w d1,d0
+ lsr.w #2,d1
subq.w #1,d1 ; prepare for dbra loop
+ bmi.s L4done
+ lsr.w #1,d1
+ bcc.s L23
+L2l:
+ move.l (a1)+,(a0)+
+L23:
+ move.l (a1)+,(a0)+
+ dbra d1,L2l
+L4done:
+ subq.w #1,d0 ; prepare for dbra loop
bmi.s Ldone
L2:
move.b (a1)+,(a0)+
- dbra d1,L2
+ dbra d0,L2
Ldone:
rts ; return
+
+bytecopy:
+ subq.l #1,d0 ; prepare for dbra loop
+ move.l d0,d1
+ swap d1
+L3:
+ move.b (a1)+,(a0)+
+ dbra d0,L3
+ dbra d1,L3
+ rts ; return
END
Index: sproto.h
@@ -29,6 +29,7 @@
/* quickmov.s */
void ARGS_ON_STACK quickmove P_((void *dst, void *src, long nbytes));
+void ARGS_ON_STACK quickmovb P_((void *dst, const void *src, long nbytes));
/* syscall.s */
char * ARGS_ON_STACK lineA0 P_((void));
Index: tty.c
@@ -87,6 +87,7 @@
unsigned char ch, *ptr;
int rdmode, mode;
struct tty *tty;
+ extern FILESYS bios_filesys;
tty = (struct tty *)f->devinfo;
assert(tty != 0);
@@ -108,6 +109,21 @@
rdmode = COOKED|NOECHO;
mode = T_TOS | T_ECHO;
}
+#if 1
+ /* see if we can do fast RAW byte IO thru the device driver... */
+ if (rdmode == RAW && !(tty->state & TS_ESC) &&
+ (f->fc.fs != &bios_filesys ||
+ (nbytes > 1 &&
+ ((struct bios_file *)f->fc.index)->drvsize >
+ offsetof (DEVDRV, readb))) &&
+ *f->dev->readb &&
+ ((f->flags & O_HEAD) ||
+ !tty->pgrp || tty->pgrp == curproc->pgrp ||
+ f->fc.dev != curproc->control->fc.dev ||
+ f->fc.index != curproc->control->fc.index) &&
+ (bytes_read = (*f->dev->readb)(f, buf, nbytes)) != EUNDEV)
+ return bytes_read;
+#endif
ptr = buf;
@@ -243,6 +259,31 @@
return bytes_read;
}
+/* job control checks */
+/* AKP: added T_TOSTOP; don't stop BG output if T_TOSTOP is clear */
+/*
+entropy: only do the job control if SIGTTOU is neither blocked nor ignored,
+and only for the controlling tty (IEEE 1003.1-1990 7.1.1.4 79-87).
+BUG: if the process group is orphaned and SIGTTOU *is not* blocked
+or ignored, we should return EIO instead of signalling.
+*/
+INLINE void
+tty_checkttou (f, tty)
+ FILEPTR *f;
+ struct tty *tty;
+{
+ if (tty->pgrp && tty->pgrp != curproc->pgrp &&
+ (tty->sg.sg_flags & T_TOSTOP) &&
+ (curproc->sighandle[SIGTTOU] != SIG_IGN) &&
+ ((curproc->sigmask & (1L << SIGTTOU)) == 0L) &&
+ (f->fc.dev == curproc->control->fc.dev) &&
+ (f->fc.index == curproc->control->fc.index)) {
+ TRACE(("job control: tty pgrp is %d proc pgrp is %d",
+ tty->pgrp, curproc->pgrp));
+ killgroup(curproc->pgrp, SIGTTOU);
+ }
+}
+
long
tty_write(f, buf, nbytes)
FILEPTR *f;
@@ -258,6 +299,7 @@
static long cr_char = '\r';
#define LBUFSIZ 128
long lbuf[LBUFSIZ];
+ extern FILESYS bios_filesys;
tty = (struct tty *)f->devinfo;
assert(tty != 0);
@@ -286,6 +328,20 @@
else
mode = 0;
+#if 1
+ /* see if we can do fast RAW byte IO thru the device driver... */
+ if (!mode && !use_putchar &&
+ (f->fc.fs != &bios_filesys ||
+ (nbytes > 1 && rwmode == RAW &&
+ ((struct bios_file *)f->fc.index)->drvsize >
+ offsetof (DEVDRV, writeb))) && *f->dev->writeb) {
+
+ tty_checkttou (f, tty);
+ if ((bytes_written = (*f->dev->writeb)(f, buf, nbytes)) != EUNDEV)
+ return bytes_written;
+ }
+#endif
+
/*
* we always write at least 1 byte with tty_putchar, since that takes
* care of job control and terminal states. After that, we may be able
@@ -801,6 +857,9 @@
}
goto do_putchar;
}
+#if 1
+ tty_checkttou (f, tty);
+#else
/* job control checks */
/* AKP: added T_TOSTOP; don't stop BG output if T_TOSTOP is clear */
/*
@@ -819,6 +878,7 @@
tty->pgrp, curproc->pgrp));
killgroup(curproc->pgrp, SIGTTOU);
}
+#endif
if (mode & COOKED) {
tty->state |= TS_COOKED;
Index: types.h
@@ -16,7 +16,9 @@
/* structure used to hold i/o buffers */
typedef struct io_rec {
char *bufaddr;
- short buflen, head, tail, low_water, hi_water;
+ short buflen;
+ volatile short head, tail;
+ short low_water, hi_water;
} IOREC_T;
/* Bconmap struct, * returned by Bconmap(-2) */
Index: xbios.c
@@ -104,6 +104,21 @@
if (!f) return EIHNDL;
if (is_terminal(f)) {
+#if 1
+ extern FILESYS bios_filesys;
+
+ /* see if we can do fast RAW byte IO thru the device driver... */
+ if ((f->fc.fs != &bios_filesys ||
+ (towrite > 1 &&
+ ((struct bios_file *)f->fc.index)->drvsize >
+ offsetof (DEVDRV, writeb))) && *f->dev->writeb) {
+ struct tty *tty = (struct tty *)f->devinfo;
+
+ tty_checkttou (f, tty);
+ if ((towrite = (*f->dev->writeb)(f, buf, towrite)) != EUNDEV)
+ return towrite;
+ }
+#endif
while (cnt >= 0) {
tty_putchar(f, (long)*buf, RAW);
buf++; cnt--;
@@ -163,7 +178,7 @@
{
long rsval;
static int oldbaud = -1;
- int ret_oldbaud = 0;
+ int b, ret_oldbaud = 0;
extern struct tty ttmfp_tty;
extern struct bios_tty bttys[];
extern short btty_max;
@@ -230,6 +245,10 @@
rsval = Rsconf(baud, flow, uc, rs, ts, sc);
if (ret_oldbaud)
rsval = (long) oldbaud;
+ b = 0;
+ if (baud >= 0 &&
+ (!has_bconmap || (unsigned)(b = curproc->bconmap-6) < btty_max))
+ bttys[b].ospeed = bttys[b].ispeed = (unsigned)baud<bttys[b].maxbaud ? bttys[b].baudmap[baud] : -1;
return rsval;
}
3. ptys can also have readb+writeb (for the slave->master pipe), and
use memcpy too (all pipes). and really buffer 4K not 4K - 1 byte so the
copies are aligned more often...
Index: pipefs.c
@@ -35,6 +35,10 @@
static long ARGS_ON_STACK pipe_open P_((FILEPTR *f));
static long ARGS_ON_STACK pipe_write P_((FILEPTR *f, const char *buf, long bytes));
static long ARGS_ON_STACK pipe_read P_((FILEPTR *f, char *buf, long bytes));
+static long ARGS_ON_STACK pty_write P_((FILEPTR *f, const char *buf, long bytes));
+static long ARGS_ON_STACK pty_read P_((FILEPTR *f, char *buf, long bytes));
+static long ARGS_ON_STACK pty_writeb P_((FILEPTR *f, const char *buf, long bytes));
+static long ARGS_ON_STACK pty_readb P_((FILEPTR *f, char *buf, long bytes));
static long ARGS_ON_STACK pipe_lseek P_((FILEPTR *f, long where, int whence));
static long ARGS_ON_STACK pipe_ioctl P_((FILEPTR *f, int mode, void *buf));
static long ARGS_ON_STACK pipe_datime P_((FILEPTR *f, short *time, int rwflag));
@@ -47,8 +51,10 @@
pipe_close, pipe_select, pipe_unselect
};
-/* ptys and pipes can share the same driver, for now */
-#define pty_device pipe_device
+DEVDRV pty_device = {
+ pipe_open, pty_write, pty_read, pipe_lseek, pipe_ioctl, pipe_datime,
+ pipe_close, pipe_select, pipe_unselect, pty_writeb, pty_readb
+};
FILESYS pipe_filesys = {
(FILESYS *)0,
@@ -79,7 +85,7 @@
int head, tail; /* pipe head, tail (head == tail for empty) */
long rsel; /* process that did select() for reads */
long wsel; /* process that did select() for writes */
- char buf[PIPESIZ]; /* pipe data */
+ char buf[PIPESIZ+4]; /* pipe data */
};
struct fifo {
@@ -610,8 +616,8 @@
if (nbytes > 0 && nbytes <= PIPE_BUF) {
check_atomicity:
r = p->tail - p->head;
- if (r < 0) r += PIPESIZ;
- r = (PIPESIZ-1) - r; /* r is the number of bytes we can write */
+ if (r < 0) r += PIPESIZ+4;
+ r = PIPESIZ - r; /* r is the number of bytes we can write */
if (r < nbytes) {
/* check for broken pipes */
if (p->readers == 0 || p->readers == VIRGIN_PIPE) {
@@ -635,9 +641,48 @@
while (nbytes > 0) {
ptail = p->tail; phead = p->head;
- j = ptail+1;
- if (j >= PIPESIZ) j = 0;
+ j = ptail+4;
+ if (j >= PIPESIZ+4) j -= PIPESIZ+4;
if (j != phead) {
+#if 1
+ int free, wrap;
+
+ pbuf = p->buf + ptail;
+ if ((free = phead - j) < 0)
+ free += PIPESIZ+4;
+ if (free == PIPESIZ && ((j = ((long)pbuf & 3)))) {
+ /* if pipe empty we can align pointers */
+ if ((ptail += 4-j) >= PIPESIZ+4)
+ ptail -= PIPESIZ+4;
+ p->head = phead = ptail;
+ pbuf = p->buf + ptail;
+ }
+ if (free > nbytes)
+ free = nbytes;
+ nbytes -= free;
+ bytes_written += free;
+
+ wrap = PIPESIZ+4 - ptail;
+ if (free < 5) {
+ while (free--) {
+ *pbuf++ = *buf++;
+ if (!--wrap)
+ pbuf -= PIPESIZ+4;
+ }
+ ptail = pbuf - p->buf;
+ } else {
+ if (wrap <= free) {
+ memcpy (pbuf, buf, wrap);
+ memcpy (p->buf, buf + wrap, free - wrap);
+ ptail = free - wrap;
+ } else {
+ memcpy (pbuf, buf, free);
+ ptail += free;
+ }
+ buf += free;
+ }
+#else
+ j -= 3;
pbuf = &p->buf[ptail];
do {
*pbuf++ = *buf++;
@@ -645,8 +690,9 @@
if ( (ptail = j) == 0 )
pbuf = &p->buf[0];
j++;
- if (j >= PIPESIZ) j = 0;
+ if (j >= PIPESIZ+4) j = 0;
} while ( (nbytes > 0) && (j != phead) );
+#endif
p->tail = ptail;
} else { /* pipe full */
if (p->readers == 0 || p->readers == VIRGIN_PIPE) {
@@ -706,16 +752,48 @@
while (nbytes > 0) {
phead = p->head; ptail = p->tail;
if (ptail != phead) {
+#if 1
+ int left, wrap;
+
+ pbuf = p->buf + phead;
+ if ((left = ptail - phead) < 0)
+ left += PIPESIZ+4;
+ if (left > nbytes)
+ left = nbytes;
+ nbytes -= left;
+ bytes_read += left;
+
+ wrap = PIPESIZ+4 - phead;
+ if (left <= 5) {
+ while (left--) {
+ *buf++ = *pbuf++;
+ if (!--wrap)
+ pbuf -= PIPESIZ+4;
+ }
+ phead = pbuf - p->buf;
+ } else {
+ if (wrap <= left) {
+ memcpy (buf, pbuf, wrap);
+ memcpy (buf + wrap, p->buf, left - wrap);
+ phead = left - wrap;
+ } else {
+ memcpy (buf, pbuf, left);
+ phead += left;
+ }
+ buf += left;
+ }
+#else
pbuf = &p->buf[phead];
do {
*buf++ = *pbuf++;
nbytes--; bytes_read++;
phead++;
- if (phead >= PIPESIZ) {
+ if (phead >= PIPESIZ+4) {
phead = 0;
pbuf = &p->buf[phead];
}
} while ( (nbytes > 0) && (phead != ptail) );
+#endif
p->head = phead;
}
else if (p->writers <= 0 || p->writers == VIRGIN_PIPE) {
@@ -752,6 +830,64 @@
}
static long ARGS_ON_STACK
+pty_write(f, buf, nbytes)
+ FILEPTR *f; const char *buf; long nbytes;
+{
+ long bytes_written = 0;
+
+ if (!nbytes)
+ return 0;
+ if (f->flags & O_HEAD)
+ return pipe_write(f, buf, nbytes);
+ if (nbytes != 4)
+ ALERT("pty_write: slave nbytes != 4");
+ bytes_written = pipe_write(f, buf+3, 1);
+ if (bytes_written == 1)
+ bytes_written = 4;
+ return bytes_written;
+}
+
+static long ARGS_ON_STACK
+pty_read(f, buf, nbytes)
+ FILEPTR *f; char *buf; long nbytes;
+{
+ long bytes_read = 0;
+
+ if (!nbytes)
+ return 0;
+ if (!(f->flags & O_HEAD))
+ return pipe_read(f, buf, nbytes);
+ if (nbytes != 4)
+ ALERT("pty_read: master nbytes != 4");
+ bytes_read = pipe_read(f, buf+3, 1);
+ if (bytes_read == 1)
+ bytes_read = 4;
+ return bytes_read;
+}
+
+static long ARGS_ON_STACK
+pty_writeb(f, buf, nbytes)
+ FILEPTR *f; const char *buf; long nbytes;
+{
+ if (!nbytes)
+ return 0;
+ if (f->flags & O_HEAD)
+ return EUNDEV;
+ return pipe_write(f, buf, nbytes);
+}
+
+static long ARGS_ON_STACK
+pty_readb(f, buf, nbytes)
+ FILEPTR *f; char *buf; long nbytes;
+{
+ if (!nbytes)
+ return 0;
+ if (!(f->flags & O_HEAD))
+ return EUNDEV;
+ return pipe_read(f, buf, nbytes);
+}
+
+static long ARGS_ON_STACK
pipe_ioctl(f, mode, buf)
FILEPTR *f; int mode; void *buf;
{
@@ -772,8 +908,8 @@
r = -1;
} else {
r = p->tail - p->head;
- if (r < 0) r += PIPESIZ;
- if (is_terminal(f))
+ if (r < 0) r += PIPESIZ+4;
+ if (is_terminal(f) && !(f->flags & O_HEAD))
r = r >> 2; /* r /= 4 */
if (!r && p->writers <= 0) {
DEBUG(("pipe FIONREAD: no writers"));
@@ -789,9 +925,9 @@
r = -1;
} else {
r = p->tail - p->head;
- if (r < 0) r += PIPESIZ;
- r = (PIPESIZ-1) - r;
- if (is_terminal(f))
+ if (r < 0) r += PIPESIZ+4;
+ r = PIPESIZ - r;
+ if (is_terminal(f) && (f->flags & O_HEAD))
r = r >> 2; /* r /= 4 */
}
*((long *) buf) = r;
@@ -867,8 +1003,8 @@
r = -1;
} else {
r = p->tail - p->head;
- if (r < 0) r += PIPESIZ;
- if (is_terminal(f))
+ if (r < 0) r += PIPESIZ+4;
+ if (is_terminal(f) && (f->flags & O_HEAD))
r = r >> 2; /* r /= 4 */
}
*((long *) buf) = r;
@@ -1081,8 +1217,8 @@
DEBUG(("write select on wrong end of pipe"));
return 0;
}
- j = p->tail+1;
- if (j >= PIPESIZ) j = 0;
+ j = p->tail+4;
+ if (j >= PIPESIZ+4) j -= PIPESIZ+4;
if (j != p->head || p->readers <= 0)
return 1; /* data may be written */
if (p->wsel)
Index: tty.c
@@ -330,14 +330,50 @@
#if 1
/* see if we can do fast RAW byte IO thru the device driver... */
- if (!mode && !use_putchar &&
+ if (!use_putchar &&
(f->fc.fs != &bios_filesys ||
- (nbytes > 1 && rwmode == RAW &&
- ((struct bios_file *)f->fc.index)->drvsize >
+ (((struct bios_file *)f->fc.index)->drvsize >
offsetof (DEVDRV, writeb))) && *f->dev->writeb) {
tty_checkttou (f, tty);
- if ((bytes_written = (*f->dev->writeb)(f, buf, nbytes)) != EUNDEV)
+ if (mode) { /* i.e. T_CRMODE */
+ if ((*f->dev->writeb)(f, buf, 0L) != EUNDEV) {
+ /* write in big chunks if possible; lines if CRMODE
+ * (if we get here flow control is taken care of by the device)
+ */
+ long bytes_to_write = 0;
+ unsigned const char *s = ptr;
+
+ while (nbytes-- > 0) {
+ if (*ptr++ == '\n') {
+ if ((bytes_to_write = ptr-s-1)) {
+ c = (*f->dev->writeb)(f, s,
+ bytes_to_write);
+ bytes_written += c;
+ if (c != bytes_to_write) {
+ if (c < 0)
+ bytes_written = c;
+ return bytes_written;
+ }
+ }
+ s = ptr-1;
+ c = (*f->dev->writeb)(f, "\r", 1);
+ if (c != 1) {
+ if (c < 0)
+ bytes_written = c;
+ return bytes_written;
+ }
+ }
+ }
+ if ((bytes_to_write = ptr-s)) {
+ c = (*f->dev->writeb)(f, s, bytes_to_write);
+ bytes_written += c;
+ if (c < 0)
+ bytes_written = c;
+ }
+ return bytes_written;
+ }
+ } else if ((bytes_written = (*f->dev->writeb)(f, buf, nbytes)) != EUNDEV)
return bytes_written;
}
#endif
Index: biosfs.c
@@ -1331,6 +1331,11 @@
int bdev = f->fc.aux;
struct bios_file *b = (struct bios_file *)f->fc.index;
+/* do the slow thing if tty is not in RAW mode until serial lines
+ * handle control chars properly
+ */
+ if (!(((struct tty *)f->devinfo)->sg.sg_flags & T_RAW))
+ return EUNDEV;
return iwrite (bdev, buf, bytes, (f->flags & O_NDELAY), b);
}
>
> Kay.
good luck
Juergen
--
J"urgen Lock / nox@jelal.north.de / UUCP: ..!uunet!unido!uniol!jelal!nox
...ohne Gewehr
PGP public key fingerprint = 8A 18 58 54 03 7B FC 12 1F 8B 63 C7 19 27 CF DA