[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