#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"
/*
* Register set for half the duart.
* There are really two sets in adjacent memory locations.
*/
struct Duartreg
{
uchar mr12; /* Mode Register Channels 1 & 2 */
uchar pad0[15];
uchar srcsr; /* Status Register/Clock Select Register */
uchar pad1[15];
uchar cmnd; /* Command Register */
uchar pad2[15];
uchar data; /* RX Holding / TX Holding Register */
uchar pad3[15];
uchar ipcacr; /* Input Uart Change/Aux. Control Register */
uchar pad4[15];
uchar isimr; /* Interrupt Status/Interrupt Mask Register */
uchar pad5[15];
uchar ctur; /* Counter/Timer Upper Register */
uchar pad6[15];
uchar ctlr; /* Counter/Timer Lower Register */
uchar pad7[15];
};
#define ppcr isimr /* in the second register set */
enum
{
DBD75 = 0,
DBD110 = 1,
DBD38400 = 2,
DBD150 = 3,
DBD300 = 4,
DBD600 = 5,
DBD1200 = 6,
DBD2000 = 7,
DBD2400 = 8,
DBD4800 = 9,
DBD1800 = 10,
DBD9600 = 11,
DBD19200 = 12,
CHARERR = 0x00, /* MR1x - Mode Register 1 */
EVENPAR = 0x00,
ODDPAR = 0x04,
NOPAR = 0x10,
CBITS8 = 0x03,
CBITS7 = 0x02,
CBITS6 = 0x01,
CBITS5 = 0x00,
NORMOP = 0x00, /* MR2x - Mode Register 2 */
TWOSTOPB = 0x0F,
ONESTOPB = 0x07,
ENBRX = 0x01, /* CRx - Command Register */
DISRX = 0x02,
ENBTX = 0x04,
DISTX = 0x08,
RESETMR = 0x10,
RESETRCV = 0x20,
RESETTRANS = 0x30,
RESETERR = 0x40,
RESETBCH = 0x50,
STRTBRK = 0x60,
STOPBRK = 0x70,
RCVRDY = 0x01, /* SRx - Channel Status Register */
FIFOFULL = 0x02,
XMTRDY = 0x04,
XMTEMT = 0x08,
OVRERR = 0x10,
PARERR = 0x20,
FRMERR = 0x40,
RCVDBRK = 0x80,
IMIPC = 0x80, /* IMRx/ISRx - Int Mask/Interrupt Status */
IMDBB = 0x40,
IMRRDYB = 0x20,
IMXRDYB = 0x10,
IMCRDY = 0x08,
IMDBA = 0x04,
IMRRDYA = 0x02,
IMXRDYA = 0x01,
BD38400 = 0xCC|0x0000,
BD19200 = 0xCC|0x0100,
BD9600 = 0xBB|0x0000,
BD4800 = 0x99|0x0000,
BD2400 = 0x88|0x0000,
BD1200 = 0x66|0x0000,
BD300 = 0x44|0x0000,
Maxduart = 8,
};
/*
* requests to perform on a duart
*/
enum
{
Dnone= 0,
Dbaud,
Dbreak,
Ddtr,
Dprint,
Dena,
Dstate,
};
/*
* a duart
*/
typedef struct Duart Duart;
struct Duart
{
QLock;
Duartreg *reg; /* duart registers */
uchar imr; /* sticky interrupt mask reg bits */
uchar acr; /* sticky auxiliary reg bits */
int inited;
};
Duart duart[Maxduart];
/*
* values specific to a single duart port
*/
typedef struct Uart Uart;
struct Uart
{
QLock;
Duart *d; /* device */
Duartreg *reg; /* duart registers (for this port) */
int c; /* character to restart output */
int op; /* operation requested */
int val; /* value of operation */
Rendez opr; /* waiot here for op to complete */
int printing; /* need kick */
int opens;
Rendez r;
/* buffers */
int (*putc)(Queue*, int);
Queue *iq;
Queue *oq;
};
Uart uart[2*Maxduart];
void duartkick(Uart*);
/*
* configure a duart port, default is 9600 baud, 8 bits/char, 1 stop bit,
* no parity
*/
void
duartsetup(Uart *p, Duart *d, int devno)
{
Duartreg *reg;
p->d = d;
reg = &d->reg[devno];
p->reg = reg;
reg->cmnd = RESETRCV|DISTX|DISRX;
reg->cmnd = RESETTRANS;
reg->cmnd = RESETERR;
reg->cmnd = STOPBRK;
reg->cmnd = RESETMR;
reg->mr12 = NOPAR|CBITS8;
reg->mr12 = ONESTOPB;
reg->srcsr = (DBD9600<<4)|DBD9600;
reg->cmnd = ENBTX|ENBRX;
p->iq = qopen(4*1024, 0, 0, 0);
p->oq = qopen(4*1024, 0, duartkick, p);
}
/*
* init the duart on the current processor
*/
void
duartinit(void)
{
Uart *p;
Duart *d;
d = &duart[m->machno];
if(d->inited)
return;
d->reg = DUARTREG;
d->imr = IMRRDYA|IMXRDYA|IMRRDYB|IMXRDYB;
d->reg->isimr = d->imr;
d->acr = 0x80; /* baud rate set 2 */
d->reg->ipcacr = d->acr;
p = &uart[2*m->machno];
duartsetup(p, d, 0);
p++;
duartsetup(p, d, 1);
d->inited = 1;
}
/*
* enable a duart port
*/
void
duartenable(Uart *p)
{
p->reg->cmnd = ENBTX|ENBRX;
}
void
duartenable0(void)
{
DUARTREG->cmnd = ENBTX|ENBRX;
}
void
duartbaud(Uart *p, int b)
{
int x;
switch(b){
case 38400:
x = BD38400;
break;
case 19200:
x = BD19200;
break;
case 9600:
x = BD9600;
break;
case 4800:
x = BD4800;
break;
case 2400:
x = BD2400;
break;
case 1200:
x = BD1200;
break;
case 300:
x = BD300;
break;
default:
return;
}
if(x & 0x0100)
p->d->acr |= 0x80;
else
p->d->acr &= ~0x80;
p->d->reg->ipcacr = p->d->acr;
p->reg->srcsr = x;
}
void
duartdtr(Uart *p, int val)
{
if (val)
p->reg->ctlr = 0x01;
else
p->reg->ctur = 0x01;
}
void
duartbreak(Uart *p, int val)
{
Duartreg *reg;
reg = p->reg;
if (val){
p->d->imr &= ~IMXRDYB;
p->d->reg->isimr = p->d->imr;
reg->cmnd = STRTBRK|ENBTX;
} else {
reg->cmnd = STOPBRK|ENBTX;
p->d->imr |= IMXRDYB;
p->d->reg->isimr = p->d->imr;
}
}
/*
* do anything requested for this CPU's duarts
*/
void
duartslave0(Uart *p)
{
switch(p->op){
case Ddtr:
duartdtr(p, p->val);
break;
case Dbaud:
duartbaud(p, p->val);
break;
case Dbreak:
duartbreak(p, p->val);
break;
case Dprint:
p->reg->cmnd = ENBTX;
p->reg->data = p->val;
break;
case Dena:
duartenable(p);
break;
case Dstate:
p->val = p->reg->ppcr;
break;
}
p->op = Dnone;
wakeup(&p->opr);
}
void
duartslave(void)
{
Uart *p;
p = &uart[2*m->machno];
if(p->op != Dnone)
duartslave0(p);
p++;
if(p->op != Dnone)
duartslave0(p);
}
void
duartrintr(Uart *p)
{
char ch;
int status;
Duartreg *reg;
reg = p->reg;
status = reg->srcsr;
ch = reg->data;
if(status & (FRMERR|OVRERR|PARERR))
reg->cmnd = RESETERR;
if(p->putc)
(*p->putc)(p->iq, ch);
else
qproduce(p->iq, &ch, 1);
}
/*
* (re)start output
*/
void
duartkick(Uart *p)
{
char ch;
int n, x;
x = splhi();
if(p->printing) {
splx(x);
return;
}
n = qconsume(p->oq, &ch, 1);
if(n <= 0){
splx(x);
return;
}
p->printing = 1;
p->val = ch;
p->op = Dprint;
splx(x);
}
void
duartxintr(Uart *p)
{
char ch;
Duartreg *reg;
reg = p->reg;
if(qconsume(p->oq, &ch, 1) <= 0) {
p->printing = 0;
reg->cmnd = DISTX;
}
else
reg->data = ch;
}
void
duartintr(void)
{
int cause;
Duartreg *reg;
Uart *p;
p = &uart[2*m->machno];
reg = p->reg;
cause = reg->isimr;
if(cause & IMRRDYA)
duartrintr(p);
if(cause & IMXRDYA)
duartxintr(p);
if(cause & IMRRDYB)
duartrintr(p+1);
if(cause & IMXRDYB)
duartxintr(p+1);
}
/*
* processor 0 only
*/
int
duartrawputc(int c)
{
int i;
Duartreg *reg;
reg = DUARTREG;
if(c == '\n') {
duartrawputc('\r');
delay(100);
}
reg->cmnd = ENBTX;
i = 0;
while((reg->srcsr&XMTRDY) == 0 && i++ < 100000)
;
reg->data = c;
return c;
}
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++)
duartrawputc(buf[i]);
return n;
}
void
duartspecial(int port, int s, Queue **in, Queue **out, int (*putc)(Queue*, int))
{
Uart *p;
p = &uart[port];
duartenable(p);
if(s)
duartbaud(p, s);
p->putc = putc;
if(in != 0)
*in = p->iq;
if(out != 0)
*out = p->oq;
p->opens++;
}
static int
opdone(void *x)
{
Uart *p = x;
return p->op == Dnone;
}
Dirtab *duartdir;
int nuart;
void
duartreset(void)
{
int i;
Dirtab *dp;
nuart = 2*conf.nmach;
duartdir = xalloc(2 * nuart * sizeof(Dirtab));
dp = duartdir;
for(i = 0; i < nuart; i++){
/* 2 directory entries per port */
print(dp->name, "eia%d", i);
dp->qid.path = NETQID(i, Ndataqid);
dp->perm = 0666;
dp++;
print(dp->name, "eia%dctl", i);
dp->qid.path = NETQID(i, Nctlqid);
dp->perm = 0666;
dp++;
}
}
Chan*
duartattach(char *spec)
{
return devattach('t', spec);
}
Chan*
duartclone(Chan *c, Chan *nc)
{
return devclone(c, nc);
}
int
duartwalk(Chan *c, char *name)
{
return devwalk(c, name, duartdir, 2*nuart, devgen);
}
void
duartstat(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, duartdir[2*i].name, qlen(p->iq), eve, 0660, &dir);
convD2M(&dir, dp);
break;
default:
devstat(c, dp, duartdir, 2*nuart, devgen);
break;
}
}
Chan*
duartopen(Chan *c, int omode)
{
Uart *p;
if(c->qid.path & CHDIR){
if(omode != OREAD)
error(Ebadarg);
}
else {
p = &uart[NETID(c->qid.path)];
qlock(p);
p->opens++;
if(p->opens == 1) {
/* enable the port */
p->op = Dena;
sleep(&p->opr, opdone, p);
qreopen(p->iq);
qreopen(p->oq);
}
qunlock(p);
}
c->mode = omode&~OTRUNC;
c->flag |= COPEN;
c->offset = 0;
return c;
}
void
duartcreate(Chan *c, char *name, int omode, ulong perm)
{
USED(c, name, omode, perm);
error(Eperm);
}
void
duartclose(Chan *c)
{
Uart *p;
if(c->qid.path & CHDIR)
return;
p = &uart[NETID(c->qid.path)];
qlock(p);
p->opens++;
if(p->opens == 0){
qclose(p->iq);
qclose(p->oq);
}
qunlock(p);
}
long
duartread(Chan *c, void *buf, long n, ulong offset)
{
Uart *p;
if(c->qid.path & CHDIR)
return devdirread(c, buf, n, duartdir, 2*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);
}
return 0;
}
static void
duartctl(Uart *p, char *cmd)
{
int n, i;
/* let output drain for a while */
for(i = 0; i < 16 && qlen(p->oq); i++)
tsleep(&p->r, qlen, p->oq, 125);
n = atoi(cmd+1);
switch(cmd[0]){
case 'B':
case 'b':
p->val = n;
p->op = Dbaud;
sleep(&p->opr, opdone, p);
break;
case 'D':
case 'd':
p->val = n;
p->op = Ddtr;
sleep(&p->opr, opdone, p);
break;
case 'K':
case 'k':
p->val = 1;
p->op = Dbreak;
if(!waserror()){
sleep(&p->opr, opdone, p);
tsleep(&p->opr, return0, 0, n);
poperror();
}
p->val = 0;
p->op = Dbreak;
sleep(&p->opr, opdone, p);
break;
case 'R':
case 'r':
/* can't control? */
break;
}
}
long
duartwrite(Chan *c, void *va, long n, ulong offset)
{
Uart *p;
char cmd[32];
USED(offset);
if(c->qid.path & CHDIR)
error(Eperm);
p = &uart[NETID(c->qid.path)];
switch(NETTYPE(c->qid.path)){
case Ndataqid:
return qwrite(p->oq, va, n, 0);
case Nctlqid:
if(n >= sizeof(cmd))
n = sizeof(cmd)-1;
memmove(cmd, va, n);
cmd[n] = 0;
duartctl(p, cmd);
return n;
}
}
void
duartremove(Chan *c)
{
USED(c);
error(Eperm);
}
void
duartwstat(Chan *c, char *p)
{
USED(c, p);
error(Eperm);
}