From 6a9c500e0bfd986687fcbd9ce7cd60d262d3b951 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Fri, 2 Sep 1994 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1994-09-02 --- carrera/clock.c | 1 + carrera/fns.h | 7 +- carrera/kbd.c | 1 + carrera/main.c | 2 +- carrera/ns16552.h | 41 +++ carrera/trap.c | 4 +- pc/fault386.c | 4 +- pc/fns.h | 4 +- pc/kbd.c | 5 +- pc/ns16552.h | 147 ++++++++ pc/trap.c | 1 + port/devcons.c | 12 +- port/devns16552.c | 895 ++++++++++++++++++++++++++++++++++++++++++++++ port/devpipe.c | 4 +- port/portfns.h | 4 +- port/qio.c | 44 ++- power/devduart.c | 2 +- 17 files changed, 1145 insertions(+), 33 deletions(-) create mode 100644 carrera/ns16552.h create mode 100644 pc/ns16552.h create mode 100644 port/devns16552.c diff --git a/carrera/clock.c b/carrera/clock.c index 4c2db468d23f867127cdada3646369e35a49d035..0b7c8294d4078d69a57410ebac008787cea067fd 100644 --- a/carrera/clock.c +++ b/carrera/clock.c @@ -91,6 +91,7 @@ clock(Ureg *ur) } checkalarms(); + uartclock(); mouseclock(); if(up == 0 || up->state != Running) diff --git a/carrera/fns.h b/carrera/fns.h index 4123fbaeb3adc7b330b40664333f5af890e51948..86def8eb916d993710e7aab57314e4965e64bb52 100644 --- a/carrera/fns.h +++ b/carrera/fns.h @@ -96,6 +96,7 @@ void vector0(void); void vector100(void); void vector180(void); void vmereset(void); +void uartclock(void); void uvst(void*, void*); void uvld(void*, void*); void wbflush(void); @@ -103,9 +104,9 @@ void wrcompare(ulong); void Xdelay(int); void serialinit(void); -void NS16552special(int, int, Queue**, Queue**, int (*)(Queue*, int)); -void NS16552setup(ulong, ulong); -void NS16552intr(int); +void ns16552special(int, int, Queue**, Queue**, int (*)(Queue*, int)); +void ns16552setup(ulong, ulong); +void ns16552intr(int); void etherintr(void); void iomapinit(void); void enetaddr(uchar*); diff --git a/carrera/kbd.c b/carrera/kbd.c index 69d9fe20af89303ea6092130317eb8ea1ced6c6d..074b923bde57a9ca33798f1a4947c01df8b6f9f6 100644 --- a/carrera/kbd.c +++ b/carrera/kbd.c @@ -354,6 +354,7 @@ kbdinit(void) int i; kbdq = qopen(4*1024, 0, 0, 0); + qnoblock(kbdq, 1); /* * empty the buffer diff --git a/carrera/main.c b/carrera/main.c index 95950001b9d31f5956920039f9b26529b26a6437..1cc7996f44c8aef2d0ce231b3bde0bd3008d5e97 100644 --- a/carrera/main.c +++ b/carrera/main.c @@ -163,7 +163,7 @@ machinit(void) void serialinit(void) { - NS16552setup(Uart1, UartFREQ); + ns16552install(); kbdinit(); } diff --git a/carrera/ns16552.h b/carrera/ns16552.h new file mode 100644 index 0000000000000000000000000000000000000000..bd71ce809f2d34d460e65a2753c4fdaa7114454c --- /dev/null +++ b/carrera/ns16552.h @@ -0,0 +1,41 @@ +#define outb(p, ch) *(uchar*)((p)^7) = ch +#define uartwrreg(u,r,v) outb((u)->port + (r), (u)->sticky[r] | (v)) +#define uartrdreg(u,r) *(uchar*)(((u)->port + (r))^7) + +#define uartpower(x, y) + +void +ns16552install(void) +{ + static int already; + + if(already) + return; + already = 1; + + ns16552setup(Uart1, UartFREQ); +} + +#define RD(r) (*(uchar*)((Uart1+r)^7)) +static void +ns16552iputc(char c) +{ + while((RD(5) & (1<<5)) == 0) + ; + *(uchar*)(Uart1^7) = c; + while((RD(5) & (1<<5)) == 0) + ; +} + +int +iprint(char *fmt, ...) +{ + int n, i; + char buf[512]; + + n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; + for(i = 0; i < n; i++) + ns16552iputc(buf[i]); + + return n; +} diff --git a/carrera/trap.c b/carrera/trap.c index 04ac679aa68ae43385d3dfe12e5c078311fec910..dd476e49f9996ae7ec4e6aa47dcac72b69926aa5 100644 --- a/carrera/trap.c +++ b/carrera/trap.c @@ -298,10 +298,10 @@ intr(Ureg *ur) default: panic("unknown devint=#%lux", devint); case 0x28: /* Serial 1 */ - NS16552intr(0); + ns16552intr(0); break; case 0x24: /* Serial 2 */ - NS16552intr(1); + ns16552intr(1); break; case 0x10: fiberint(0); diff --git a/pc/fault386.c b/pc/fault386.c index 1bb9beb52fcf7e7a242a80daa8f78bf68a82f89b..5fbd8e376aabd120bda3e69ee60686748e03ec50 100644 --- a/pc/fault386.c +++ b/pc/fault386.c @@ -24,7 +24,6 @@ fault386(Ureg *ur, void *arg) read = !(ur->ecode & 2); user = (ur->cs&0xffff) == UESEL; spllo(); -/*print("F%d:A#%lux:U%d:R%d|", up->pid, addr, user, read);/**/ n = fault(addr, read); if(n < 0){ if(user){ @@ -33,9 +32,8 @@ fault386(Ureg *ur, void *arg) postnote(up, 1, buf, NDebug); return; } -print("fault: 0x%lux", addr); dumpregs(ur); - panic("fault: 0x%lux", addr); + panic("fault: 0x%lux\n", addr); } up->insyscall = insyscall; } diff --git a/pc/fns.h b/pc/fns.h index 1fe864bf0db08f0c19455387b5cdab12290a2f8e..69f058f51f4ecc3944223b12f3b9ec92fd11c6c1 100644 --- a/pc/fns.h +++ b/pc/fns.h @@ -97,9 +97,7 @@ int x86(void); #define KADDR(a) ((void*)((ulong)(a)|KZERO)) #define PADDR(a) ((ulong)(a)&~KZERO) -void NS16552special(int, int, Queue**, Queue**, int (*)(Queue*, int)); -int NS16552m3mouse(Queue*, int); -int NS16552mouse(Queue*, int); +void ns16552special(int, int, Queue**, Queue**, int (*)(Queue*, int)); void hnputl(void*, ulong v); void hnputs(void*, ushort v); diff --git a/pc/kbd.c b/pc/kbd.c index 3c74b4ca338b4a6d906aa91460871e576abb1899..49f60631b6f0da619dc4787fa50663f8af114339 100644 --- a/pc/kbd.c +++ b/pc/kbd.c @@ -254,9 +254,9 @@ serialmouse(int port, char *type, int setspeed) if(setspeed) setspeed = 1200; if(type && *type == 'M') - NS16552special(port, setspeed, &mouseq, 0, m3mouseputc); + ns16552special(port, setspeed, &mouseq, 0, m3mouseputc); else - NS16552special(port, setspeed, &mouseq, 0, mouseputc); + ns16552special(port, setspeed, &mouseq, 0, mouseputc); mousetype = Mouseserial; } @@ -597,6 +597,7 @@ kbdinit(void) int c; kbdq = qopen(4*1024, 0, 0, 0); + qnoblock(kbdq, 1); setvec(Kbdvec, kbdintr, 0); diff --git a/pc/ns16552.h b/pc/ns16552.h new file mode 100644 index 0000000000000000000000000000000000000000..e1f7460a5c2ea8e8ac79e3dea836e74976b50bb6 --- /dev/null +++ b/pc/ns16552.h @@ -0,0 +1,147 @@ +/* + * PC specific code for the ns16552. It includes support for the 2 built + * in uarts plus up to 5 MP-008 8 uart ISA cards. + */ +enum +{ + Maxcard= 5, /* max serial cards */ + UartFREQ= 1843200, + + Serial= 0, + Modem= 1, +}; + +#define uartwrreg(u,r,v) outb((u)->port + r, (u)->sticky[r] | (v)) +#define uartrdreg(u,r) inb((u)->port + r) + +void ns16552setup(ulong, ulong); + +/* + * definition of an optional serial card + */ +typedef struct Scard +{ + ISAConf; /* card configuration */ + int first; /* number of first port */ +} Scard; +static Scard *scard[Maxcard]; /* configs for the serial card */ + +/* power management currently only makes sense on the AT&T safari */ +static void +uartpower(int dev, int onoff) +{ + switch(dev){ + case Modem: + if(modem(onoff) < 0) + print("can't turn %s modem speaker\n", onoff?"on":"off"); + break; + case Serial: + if(serial(onoff) < 0) + print("can't turn %s serial port power\n", onoff?"on":"off"); + break; + } +} + +/* + * handle an interrupt to a single uart + */ +static void +ns16552intrx(Ureg *ur, void *arg) +{ + USED(ur); + + ns16552intr((ulong)arg); +} + +/* + * interrupts from the multiport card, MP-008. A polling port + * tells which of 8 devices interrupted. + */ +static void +mp008intr(Ureg *ur, void *arg) +{ + int i, loops; + uchar n; + Scard *mp; + + USED(ur); + mp = arg; + for(loops = 0;; loops++){ + if(loops > 1024) + panic("mp008intr"); + n = ~inb(mp->mem); + if(n == 0) + return; + for(i = 0; n; i++){ + if(n & 1) + ns16552intrx(ur, uart[mp->first+i]); + n >>= 1; + } + } +} + +/* + * install the uarts (called by reset) + */ +static void +ns16552install(void) +{ + int i, j, port; + Scard *sc; + + /* first two ports are always there and always the normal frequency */ + ns16552setup(0x3F8, UartFREQ); + setvec(Uart0vec, ns16552intrx, (void*)0); + ns16552setup(0x2F8, UartFREQ); + setvec(Uart1vec, ns16552intrx, (void*)1); + + /* the rest come out of plan9.ini */ + for(i = 0; i < Maxcard; i++){ + sc = scard[i] = xalloc(sizeof(Scard)); + if(isaconfig("serial", i, sc) == 0){ + xfree(sc); + break; + } + + if(strcmp(sc->type, "MP008") == 0 || strcmp(sc->type, "mp008") == 0){ + /* + * port gives base port address for uarts + * irq is interrupt + * mem is the polling port + * size is the number of serial ports on the same polling port + * freq is the baud rate generator frequency + */ + if(sc->size == 0) + sc->size = 8; + if(sc->freq == 0) + sc->freq = UartFREQ; + sc->first = nuart; + setvec(Int0vec+sc->irq, mp008intr, sc); + port = sc->port; + for(j=0; j < sc->size; j++){ + ns16552setup(port, sc->freq); + port += 8; + } + } else { + /* + * port gives base port address for the uart + * irq is interrupt + * freq is the baud rate generator frequency + */ + ns16552setup(sc->port, sc->freq); + setvec(Int0vec+sc->irq, ns16552intrx, (void*)(nuart-1)); + } + } +} + +int +iprint(char *fmt, ...) +{ + char buf[PRINTSIZE]; + int n; + + n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; + screenputs(buf, n); + + return n; +} diff --git a/pc/trap.c b/pc/trap.c index e47c0d158ba6cb2174066b97b54159a10021dae2..23b67b2066e89ed66422e25ce54a427862b68313 100644 --- a/pc/trap.c +++ b/pc/trap.c @@ -346,6 +346,7 @@ dumpregs(Ureg *ur) x = (ulong*)(ur+1); dumpregs2(ur); + print(" CR0 %8.8lux CR2 %8.8lux\n", getcr0(), getcr2()); print(" magic %lux %lux %lux\n", x[0], x[1], x[2]); print(" ur %lux up %lux\n", ur, up); } diff --git a/port/devcons.c b/port/devcons.c index fd01cc28c67eba6691d983766ce3f085f1fdbd9e..5ea6ba784a09ee43a888ba2c0b1a0bb7f4e6262b 100644 --- a/port/devcons.c +++ b/port/devcons.c @@ -34,7 +34,9 @@ void printinit(void) { klogq = qopen(32*1024, 0, 0, 0); + qnoblock(klogq, 1); lineq = qopen(2*1024, 0, 0, 0); + qnoblock(lineq, 1); } /* @@ -73,7 +75,7 @@ putstrn0(char *str, int n, int usewrite) buf[m] = '\r'; buf[m+1] = '\n'; if(usewrite) - qwrite(printq, buf, m+2, 0); + qwrite(printq, buf, m+2); else { x = splhi(); qproduce(printq, buf, m+2); @@ -83,7 +85,7 @@ putstrn0(char *str, int n, int usewrite) n -= m + 1; } else { if(usewrite) - qwrite(printq, str, n, 0); + qwrite(printq, str, n); else { x = splhi(); qproduce(printq, str, n); @@ -138,7 +140,7 @@ kprint(char *fmt, ...) int n; n = doprint(buf, buf+sizeof(buf), fmt, (&fmt+1)) - buf; - qwrite(klogq, buf, n, 1); + qwrite(klogq, buf, n); return n; } @@ -540,7 +542,7 @@ consread(Chan *c, void *buf, long n, ulong offset) if(kbd.x == sizeof(kbd.line) || eol){ if(ch == 0x04) kbd.x--; - qwrite(lineq, kbd.line, kbd.x, 1); + qwrite(lineq, kbd.line, kbd.x); kbd.x = 0; } } @@ -743,7 +745,7 @@ conswrite(Chan *c, void *va, long n, ulong offset) if(strncmp(a, "rawon", 5) == 0){ qlock(&kbd); if(kbd.x){ - qwrite(kbdq, kbd.line, kbd.x, 1); + qwrite(kbdq, kbd.line, kbd.x); kbd.x = 0; } kbd.raw = 1; diff --git a/port/devns16552.c b/port/devns16552.c new file mode 100644 index 0000000000000000000000000000000000000000..bceee69920eff9ce1faa0d686879affb7712ab9e --- /dev/null +++ b/port/devns16552.c @@ -0,0 +1,895 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "devtab.h" +#include "../port/netif.h" + +/* + * Driver for the ns16552. + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Istat= 2, /* interrupt flag (read) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Outready=(1<<5), /* output buffer full */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ring= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + CTLS= 023, + CTLQ= 021, + + Stagesize= 1024, + Nuart= 32, /* max per machine */ +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + + Uart *elist; /* next enabled interface */ + + uchar sticky[8]; /* sticky write register values */ + ulong port; + Lock plock; /* for printing variable */ + int printing; /* true if printing */ + ulong freq; /* clock frequency */ + int opens; + uchar mask; /* bits/char */ + uchar istat; /* last istat read */ + uchar fifoon; /* fifo's enabled */ + uchar nofifo; /* earlier chip version with nofifo */ + int enabled; + int dev; + + int frame; + int overrun; + + /* flow control */ + int xonoff; /* software flow control on */ + int blocked; + int modem; /* hardware flow control on */ + int cts; + int ctsbackoff; + Rendez r; + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + /* staging areas to avoid some of the per character costs */ + uchar istage[Stagesize]; + uchar *ip; + uchar *ie; + + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; +}; + +static Uart *uart[Nuart]; +static int nuart; + +static int haveinput; +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +void ns16552intr(int); + +/* + * pick up architecture specific routines and definitions + */ +#include "ns16552.h" + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +ns16552setbaud(Uart *p, int rate) +{ + ulong brconst; + + brconst = (p->freq+8*rate-1)/(16*rate); + + uartwrreg(p, Format, Dra); + outb(p->port + Dmsb, (brconst>>8) & 0xff); + outb(p->port + Dlsb, brconst & 0xff); + uartwrreg(p, Format, 0); +} + +static void +ns16552parity(Uart *p, char type) +{ + switch(type){ + case 'e': + p->sticky[Format] |= Pena|Peven; + break; + case 'o': + p->sticky[Format] &= ~Peven; + p->sticky[Format] |= Pena; + break; + default: + p->sticky[Format] &= ~(Pena|Peven); + break; + } + uartwrreg(p, Format, 0); +} + +/* + * set bits/character, default 8 + */ +void +ns16552bits(Uart *p, int bits) +{ + if(bits < 5 || bits > 8) + error(Ebadarg); + + p->sticky[Format] &= ~3; + p->sticky[Format] |= bits-5; + + uartwrreg(p, Format, 0); +} + + +/* + * toggle DTR + */ +void +ns16552dtr(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Dtr; + else + p->sticky[Mctl] &= ~Dtr; + + uartwrreg(p, Mctl, 0); +} + +/* + * toggle RTS + */ +void +ns16552rts(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Rts; + else + p->sticky[Mctl] &= ~Rts; + + uartwrreg(p, Mctl, 0); +} + +/* + * send break + */ +void +ns16552break(Uart *p, int ms) +{ + if(ms == 0) + ms = 200; + + uartwrreg(p, Format, Break); + tsleep(&up->sleep, return0, 0, ms); + uartwrreg(p, Format, 0); +} + +void +ns16552fifoon(Uart *p) +{ + ulong i, x, c; + + if(p->nofifo) + return; + + x = splhi(); + + /* empty buffer */ + c = 0; /* c is to to avoid an unused warning */ + for(i = 0; i < 16; i++) + c = uartrdreg(p, Data); + USED(c); + ns16552intr(p->dev); + + /* turn on fifo */ + p->fifoon = 1; + uartwrreg(p, Fifoctl, Fena|Ftrig); + + ns16552intr(p->dev); + USED(c); + if((p->istat & Fenabd) == 0){ + /* didn't work, must be an earlier chip type */ + p->nofifo = 1; + } + + splx(x); +} + +/* + * modem flow control on/off (rts/cts) + */ +void +ns16552mflow(Uart *p, int n) +{ + if(n){ + p->sticky[Iena] |= Imstat; + uartwrreg(p, Iena, 0); + p->modem = 1; + p->cts = uartrdreg(p, Mstat) & Cts; + + /* turn on fifo's */ + ns16552fifoon(p); + } else { + p->sticky[Iena] &= ~Imstat; + uartwrreg(p, Iena, 0); + p->modem = 0; + p->cts = 1; + + /* turn off fifo's */ + p->fifoon = 0; + uartwrreg(p, Fifoctl, 0); + } +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +void +ns16552enable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + uartpower(p->dev, 1); + + /* + * turn on interrupts + */ + p->sticky[Iena] = Ircv | Ixmt | Irstat; + uartwrreg(p, Iena, 0); + + /* + * turn on DTR and RTS + */ + ns16552dtr(p, 1); + ns16552rts(p, 1); + + /* + * assume we can send + */ + p->cts = 1; + p->blocked = 0; + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +void +ns16552disable(Uart *p) +{ + Uart **l; + + /* + * turn off interrpts + */ + p->sticky[Iena] = 0; + uartwrreg(p, Iena, 0); + + /* + * revert to default settings + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + ns16552dtr(p, 0); + ns16552rts(p, 0); + ns16552mflow(p, 0); + p->xonoff = p->blocked = 0; + + uartpower(p->dev, 0); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * (re)start output + */ +static void +ns16552kick(Uart *p) +{ + int x, n; + + x = splhi(); + lock(&p->plock); + if(p->printing == 0 || (uartrdreg(p, Lstat) & Outready)) + if(p->cts && p->blocked == 0){ + n = 0; + while((uartrdreg(p, Lstat) & Outready) == 0){ + if(++n > 10000){ + print("stuck serial line\n"); + break; + } + } + do{ + if(p->op >= p->oe && stageoutput(p) == 0) + break; + outb(p->port + Data, *(p->op++)); + }while(uartrdreg(p, Lstat) & Outready); + } + unlock(&p->plock); + splx(x); +} + +/* + * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, + * transmit and receive enabled, interrupts disabled. + */ +static void +ns16552setup0(Uart *p) +{ + memset(p->sticky, 0, sizeof(p->sticky)); + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrpts enabled. + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + p->sticky[Mctl] |= Inton; + uartwrreg(p, Mctl, 0x0); + + ns16552setbaud(p, 9600); + + p->iq = qopen(4*1024, 0, 0, 0); + p->oq = qopen(4*1024, 0, ns16552kick, p); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; +} + +/* + * called by main() to create a new duart + */ +void +ns16552setup(ulong port, ulong freq) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + p->dev = nuart; + nuart++; + p->port = port; + p->freq = freq; + ns16552setup0(p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +ns16552special(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p = uart[port]; + + ns16552enable(p); + if(baud) + ns16552setbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +/* + * handle an interrupt to a single uart + */ +void +ns16552intr(int dev) +{ + uchar ch; + int s, l, loops; + Uart *p = uart[dev]; + + for(loops = 0;; loops++){ + if(loops > 100000) + panic("ns16552intr"); + p->istat = s = uartrdreg(p, Istat); + switch(s&0x3f){ + case 6: /* receiver line status */ + l = uartrdreg(p, Lstat); + if(l & Ferror) + p->frame++; + if(l & Oerror) + p->overrun++; + break; + + case 4: /* received data available */ + case 12: + ch = uartrdreg(p, Data) & 0xff; + if(p->xonoff){ + if(ch == CTLS){ + p->blocked = 1; + }else if (ch == CTLQ){ + p->blocked = 0; + /* clock gets output going again */ + } + } + if(p->putc) + (*p->putc)(p->iq, ch); + else { + if(p->ip < p->ie) + *p->ip++ = ch; + haveinput = 1; + } + break; + + case 2: /* transmitter not full */ + lock(&p->plock); + l = uartrdreg(p, Lstat) & Outready; + if(l && p->cts && p->blocked == 0) + if(p->op < p->oe || stageoutput(p)){ + do{ + outb(p->port + Data, *(p->op++)); + if(p->op >= p->oe && stageoutput(p) == 0) + break; + l = uartrdreg(p, Lstat) & Outready; + }while(l); + } + if(l) + p->printing = 0; + unlock(&p->plock); + break; + + case 0: /* modem status */ + ch = uartrdreg(p, Mstat); + if(ch & Ctsc){ + p->cts = ch & Cts; /* clock gets output going again */ + p->ctsbackoff += 1; + } + break; + + default: + if(s&1) + return; + print("weird modem interrupt #%2.2ux\n", s); + break; + } + } +} + +/* + * we save up input characters till clock time + * + * There's also a bit of code to get a stalled print going. + * It shouldn't happen, but it does. Obviously I don't + * understand interrupts. -- presotto + */ +void +uartclock(void) +{ + int n; + Uart *p; + + for(p = uartalloc.elist; p; p = p->elist){ + ns16552intr(p->dev); + if(haveinput){ + n = p->ip - p->istage; + if(n > 0 && p->iq){ + if(n > Stagesize) + panic("uartclock"); + if(qproduce(p->iq, p->istage, n) != n) + ; + p->ip = p->istage; + } + } + if(p->ctsbackoff-- < 0){ + p->ctsbackoff = 0; + ns16552kick(p); + } + } + haveinput = 0; +} + +Dirtab *ns16552dir; + +/* + * all uarts must be ns16552setup() by this point or inside of ns16552install() + */ +void +ns16552reset(void) +{ + int i; + Dirtab *dp; + + ns16552install(); /* architecture specific */ + + ns16552dir = xalloc(3 * nuart * sizeof(Dirtab)); + dp = ns16552dir; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + sprint(dp->name, "eia%d", i); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "eia%dctl", i); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "eia%dstat", i); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +void +ns16552init(void) +{ +} + +Chan* +ns16552attach(char *spec) +{ + return devattach('t', spec); +} + +Chan* +ns16552clone(Chan *c, Chan *nc) +{ + return devclone(c, nc); +} + +int +ns16552walk(Chan *c, char *name) +{ + return devwalk(c, name, ns16552dir, 3*nuart, devgen); +} + +void +ns16552stat(Chan *c, char *dp) +{ + int i; + Uart *p; + Dir dir; + + i = NETID(c->qid.path); + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + p = uart[i]; + devdir(c, c->qid, ns16552dir[3*i].name, qlen(p->iq), eve, 0660, &dir); + convD2M(&dir, dp); + break; + default: + devstat(c, dp, ns16552dir, 3*nuart, devgen); + break; + } +} + +Chan* +ns16552open(Chan *c, int omode) +{ + Uart *p; + + if(c->qid.path & CHDIR){ + if(omode != OREAD) + error(Ebadarg); + } else if(NETTYPE(c->qid.path) == Nstatqid){ + ; + } else { + p = uart[NETID(c->qid.path)]; + qlock(p); + p->opens++; + ns16552enable(p); + qreopen(p->iq); + qreopen(p->oq); + qunlock(p); + } + + c->mode = omode&~OTRUNC; + c->flag |= COPEN; + c->offset = 0; + return c; +} + +void +ns16552create(Chan *c, char *name, int omode, ulong perm) +{ + USED(c, name, omode, perm); + error(Eperm); +} + +void +ns16552close(Chan *c) +{ + Uart *p; + + if(c->qid.path & CHDIR) + return; + if(NETTYPE(c->qid.path) == Nstatqid) + return; + + p = uart[NETID(c->qid.path)]; + if(c->flag & COPEN){ + qlock(p); + p->opens--; + if(p->opens == 0){ + ns16552disable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + } + qunlock(p); + } +} + +static long +uartstatus(Chan *c, Uart *p, void *buf, long n, long offset) +{ + uchar mstat; + uchar tstat; + char str[256]; + int i; + + str[0] = 0; + tstat = p->sticky[Mctl]; + mstat = uartrdreg(p, Mstat); + i = sprint(str, "%d printing %d cts %d qlen %d/%d", NETID(c->qid.path), + p->printing, p->cts, qlen(p->iq), qlen(p->oq)); + sprint(str+i, " lstat %ux mstat %ux istat %ux ier %ux ferr %d oerr %d", + uartrdreg(p, Lstat), mstat, uartrdreg(p, Istat), uartrdreg(p, Iena), + p->frame, p->overrun); + if(mstat & Cts) + strcat(str, " cts"); + if(mstat & Dsr) + strcat(str, " dsr"); + if(mstat & Ring) + strcat(str, " ring"); + if(mstat & Dcd) + strcat(str, " dcd"); + if(tstat & Dtr) + strcat(str, " dtr"); + if(tstat & Rts) + strcat(str, " rts"); + strcat(str, "\n"); + return readstr(offset, buf, n, str); +} + +long +ns16552read(Chan *c, void *buf, long n, ulong offset) +{ + Uart *p; + + if(c->qid.path & CHDIR) + return devdirread(c, buf, n, ns16552dir, 3*nuart, devgen); + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +ns16552ctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + ns16552break(p, 0); + return; + } + + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + ns16552setbaud(p, n); + break; + case 'D': + case 'd': + ns16552dtr(p, n); + break; + case 'H': + case 'h': + qhangup(p->iq); + qhangup(p->oq); + break; + case 'L': + case 'l': + ns16552bits(p, n); + break; + case 'm': + case 'M': + ns16552mflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + ns16552parity(p, *(cmd+1)); + break; + case 'K': + case 'k': + ns16552break(p, n); + break; + case 'R': + case 'r': + ns16552rts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + p->xonoff = n; + break; + } +} + +long +ns16552write(Chan *c, void *buf, long n, ulong offset) +{ + Uart *p; + char cmd[32]; + + USED(offset); + + if(c->qid.path & CHDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + /* + * The fifo's turn themselves off sometimes. + * It must be something I don't understand. -- presotto + */ + if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0) + ns16552fifoon(p); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + ns16552ctl(p, cmd); + return n; + } +} + +void +ns16552remove(Chan *c) +{ + USED(c); + error(Eperm); +} + +void +ns16552wstat(Chan *c, char *dp) +{ + USED(c, dp); + error(Eperm); +} diff --git a/port/devpipe.c b/port/devpipe.c index 8f99eeefbb52ad3bc2f2de47bca61dfe618b7dd6..cef15394661feb1adcaf17022c34138d795ab650 100644 --- a/port/devpipe.c +++ b/port/devpipe.c @@ -313,11 +313,11 @@ pipewrite(Chan *c, void *va, long n, ulong offset) switch(NETTYPE(c->qid.path)){ case Qdata0: - n = qwrite(p->q[1], va, n, 0); + n = qwrite(p->q[1], va, n); break; case Qdata1: - n = qwrite(p->q[0], va, n, 0); + n = qwrite(p->q[0], va, n); break; default: diff --git a/port/portfns.h b/port/portfns.h index 67eba9326721f1d425c5ea6c26b45364eefa710f..531928cb123c081e850763a89ec338c426761bf5 100644 --- a/port/portfns.h +++ b/port/portfns.h @@ -202,7 +202,9 @@ long qread(Queue*, void*, int); void qreopen(Queue*); void qunlock(QLock*); int qwindow(Queue*); -long qwrite(Queue*, void*, int, int); +long qwrite(Queue*, void*, int); +void qsetlimit(Queue*, int); +void qnoblock(Queue*, int); int readnum(ulong, char*, ulong, ulong, int); int readstr(ulong, char*, ulong, char*); void ready(Proc*); diff --git a/port/qio.c b/port/qio.c index 75e0a8a9cc0fd3c370caab92a882e9f55b7821aa..264307c837a0fb951b636e47f4a21fb2df3d698d 100644 --- a/port/qio.c +++ b/port/qio.c @@ -13,6 +13,8 @@ struct static int debuging; +#define QDEBUG if(0) + /* * IO queues */ @@ -27,7 +29,9 @@ struct Queue int len; /* bytes in queue */ int limit; /* max bytes in queue */ + int inilim; /* initial limit */ int state; + int noblock; /* true if writes return immediately when q full */ int eof; /* number of eofs read by user */ void (*kick)(void*); /* restart output */ @@ -172,7 +176,7 @@ qconsume(Queue *q, void *vp, int len) unlock(q); return -1; } -checkb(b, "qconsume 1"); + QDEBUG checkb(b, "qconsume 1"); n = BLEN(b); if(n < len) @@ -195,7 +199,7 @@ checkb(b, "qconsume 1"); if(dowakeup) wakeup(&q->wr); -checkb(b, "qconsume 2"); + QDEBUG checkb(b, "qconsume 2"); /* discard the block if we're done with it */ if((q->state & Qmsg) || len == n) freeb(b); @@ -238,7 +242,7 @@ qpass(Queue *q, Block *b) q->bfirst = b; q->blast = b; q->len += len; -checkb(b, "qpass"); + QDEBUG checkb(b, "qpass"); if(q->len >= q->limit/2) q->state |= Qflow; @@ -308,7 +312,7 @@ qproduce(Queue *q, void *vp, int len) q->bfirst = b; q->blast = b; q->len += len; -checkb(b, "qproduce"); + QDEBUG checkb(b, "qproduce"); if(q->state & Qstarve){ q->state &= ~Qstarve; @@ -337,7 +341,7 @@ qopen(int limit, int msg, void (*kick)(void*), void *arg) if(q == 0) return 0; - q->limit = limit; + q->limit = q->inilim = limit; q->kick = kick; q->arg = arg; q->state = msg ? Qmsg : 0; @@ -422,7 +426,7 @@ qread(Queue *q, void *vp, int len) sleep(&q->rr, notempty, q); } } -checkb(b, "qread 1"); + QDEBUG checkb(b, "qread 1"); /* remove a buffered block */ q->bfirst = b->next; @@ -443,7 +447,7 @@ checkb(b, "qread 1"); memmove(p, b->rp, n); b->rp += n; -checkb(b, "qread 2"); + QDEBUG checkb(b, "qread 2"); /* free it or put what's left on the queue */ if(b->rp >= b->wp || (q->state&Qmsg)) { freeb(b); @@ -482,7 +486,7 @@ qnotfull(void *a) * all copies should be outside of ilock since they can fault. */ long -qwrite(Queue *q, void *vp, int len, int nowait) +qwrite(Queue *q, void *vp, int len) { int n, sofar, dowakeup; Block *b; @@ -530,7 +534,7 @@ qwrite(Queue *q, void *vp, int len, int nowait) /* flow control */ while(!qnotfull(q)){ - if(nowait){ + if(q->noblock){ freeb(b); qunlock(&q->wlock); poperror(); @@ -549,7 +553,7 @@ qwrite(Queue *q, void *vp, int len, int nowait) } poperror(); -checkb(b, "qwrite"); + QDEBUG checkb(b, "qwrite"); if(q->syncbuf){ /* we guessed wrong and did an extra copy */ if(n > q->synclen) @@ -645,6 +649,8 @@ qreopen(Queue *q) q->state &= ~Qclosed; q->state |= Qstarve; q->eof = 0; + q->noblock = 0; + q->limit = q->inilim; } /* @@ -678,3 +684,21 @@ qcanread(Queue *q) { return q->bfirst!=0; } + +/* + * change queue limit + */ +void +qsetlimit(Queue *q, int limit) +{ + q->limit = limit; +} + +/* + * set blocking/nonblocking + */ +void +qnoblock(Queue *q, int onoff) +{ + q->noblock = onoff; +} diff --git a/power/devduart.c b/power/devduart.c index 5292028e1b5c08401658314fa07ae73e247ae690..896386994c310515fffaa7f39ee5917e2f5966a8 100644 --- a/power/devduart.c +++ b/power/devduart.c @@ -672,7 +672,7 @@ duartwrite(Chan *c, void *va, long n, ulong offset) p = &uart[NETID(c->qid.path)]; switch(NETTYPE(c->qid.path)){ case Ndataqid: - return qwrite(p->oq, va, n, 0); + return qwrite(p->oq, va, n); case Nctlqid: if(n >= sizeof(cmd)) n = sizeof(cmd)-1;