xref: /inferno-os/os/ks32/devuart.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 #include	"../port/error.h"
8 #include	"../port/netif.h"
9 
10 /*
11  * currently no DMA or flow control (hardware or software)
12  */
13 
14 /*
15  * problems fixed from previous vsn:
16  *
17  *	- no kick on queue's, so redirections weren't getting
18  *	  started until the clock tick
19  *
20  *	- lots of unnecessary overhead
21  *
22  *	- initialization sequencing
23  *
24  *	- uart[n] no longer indexed before calling uartinstall()
25  */
26 #define DEBUG	if(0)iprint
27 
28 static void uartintr(Ureg*, void*);
29 
30 enum
31 {
32 	Stagesize= 1024,
33 	Dmabufsize=Stagesize/2,
34 	Nuart=7,		/* max per machine */
35 };
36 
37 typedef struct Uart Uart;
38 struct Uart
39 {
40 	QLock;
41 
42 	int	opens;
43 
44 	int	enabled;
45 
46 	int	port;			/* 0 or 1 */
47 	int	kickme;		/* for kick */
48 	int	frame;		/* framing errors */
49 	int	overrun;	/* rcvr overruns */
50 	int	perror;		/* parity error */
51 	int	bps;		/* baud rate */
52 	uchar	bits;
53 	char	parity;
54 	uchar	stop;
55 
56 	int	inters;		/* total interrupt count */
57 	int	rinters;	/* interrupts due to read */
58 	int	winters;	/* interrupts due to write */
59 
60 	int	rcount;		/* total read count */
61 	int	wcount;		/* total output count */
62 
63 	/* buffers */
64 	int	(*putc)(Queue*, int);
65 	Queue	*iq;
66 	Queue	*oq;
67 
68 	UartReg	*reg;
69 
70 	/* staging areas to avoid some of the per character costs */
71 	uchar	*ip;
72 	uchar	*ie;
73 	uchar	*op;
74 	uchar	*oe;
75 
76 	/* put large buffers last to aid register-offset optimizations: */
77 	char	name[KNAMELEN];
78 	uchar	istage[Stagesize];
79 	uchar	ostage[Stagesize];
80 };
81 
82 #define UCON_ENABLEMASK (UCON_RXMDMASK | UCON_TXMDMASK | UCON_SINTMASK)
83 #define UCON_ENABLESET (UCON_RXMDINT | UCON_TXMDINT | UCON_SINTON)
84 #define UCON_DISABLESET (UCON_RXMDOFF | UCON_TXMDOFF | UCON_SINTOFF)
85 
86 static Uart *uart[Nuart];
87 static int nuart;
88 
89 static uchar
readstatus(Uart * p)90 readstatus(Uart *p)
91 {
92 	UartReg *reg = p->reg;
93 	uchar stat = reg->stat;
94 	if (stat & USTAT_OV)
95 		p->overrun++;
96 	if (stat & USTAT_PE)
97 		p->perror++;
98 	if (stat & USTAT_FE)
99 		p->frame++;
100 	return stat;
101 }
102 
103 static void
uartset(Uart * p)104 uartset(Uart *p)
105 {
106 	UartReg *reg = p->reg;
107 	ulong denom;
108 	ulong brdiv;
109 	int n;
110 	uchar lcon;
111 
112 	lcon= ULCON_CLOCKMCLK | ULCON_IROFF;
113 	lcon |= ULCON_WL5 + (p->bits - 5);
114 	lcon |= p->stop == 1 ? ULCON_STOP1 : ULCON_STOP2;
115 	switch (p->parity) {
116 	default:
117 	case 'n':
118 		lcon |= ULCON_PMDNONE;
119 		break;
120 	case 'o':
121 		lcon |= ULCON_PMDODD;
122 		break;
123 	case 'e':
124 		lcon |= ULCON_PMDEVEN;
125 		break;
126 	}
127 	reg->lcon = lcon;
128 
129 	/* clear the break and loopback bits; leave everything else alone */
130 	reg->con = (reg->con & ~(UCON_BRKMASK | UCON_LOOPMASK)) | UCON_BRKOFF | UCON_LOOPOFF;
131 
132 	denom = 2 * 16 * p->bps;
133 	brdiv = (TIMER_HZ + denom / 2) / denom - 1;
134 	reg->brdiv = brdiv << 4;
135 
136 	/* set buffer length according to speed, to allow
137 	 * at most a 200ms delay before dumping the staging buffer
138 	 * into the input queue
139 	 */
140 	n = p->bps/(10*1000/200);
141 	p->ie = &p->istage[n < Stagesize ? n : Stagesize];
142 }
143 
144 /*
145  *  send break
146  */
147 static void
uartbreak(Uart * p,int ms)148 uartbreak(Uart *p, int ms)
149 {
150 	UartReg *reg = p->reg;
151 	if(ms == 0)
152 		ms = 200;
153 	reg->con |= UCON_BRKON;
154 	tsleep(&up->sleep, return0, 0, ms);
155 	reg->con &= ~UCON_BRKON;
156 }
157 
158 /*
159  *  turn on a port
160  */
161 static void
uartenable(Uart * p)162 uartenable(Uart *p)
163 {
164 	UartReg *reg = p->reg;
165 
166 	if(p->enabled)
167 		return;
168 
169 	uartset(p);
170 	// enable receive, transmit, and receive interrupt:
171 	reg->con = (reg->con & UCON_ENABLEMASK) | UCON_ENABLESET;
172 	p->enabled = 1;
173 }
174 
175 /*
176  *  turn off a port
177  */
178 static void
uartdisable(Uart * p)179 uartdisable(Uart *p)
180 {
181 	p->reg->con = (p->reg->con & UCON_ENABLEMASK) | UCON_DISABLESET;
182 	p->enabled = 0;
183 }
184 
185 /*
186  *  put some bytes into the local queue to avoid calling
187  *  qconsume for every character
188  */
189 static int
stageoutput(Uart * p)190 stageoutput(Uart *p)
191 {
192 	int n;
193 	Queue *q = p->oq;
194 
195 	if(!q)
196 		return 0;
197 	n = qconsume(q, p->ostage, Stagesize);
198 	if(n <= 0)
199 		return 0;
200 	p->op = p->ostage;
201 	p->oe = p->ostage + n;
202 	return n;
203 }
204 
205 static void
uartxmit(Uart * p)206 uartxmit(Uart *p)
207 {
208 	UartReg *reg = p->reg;
209 	ulong gag = 1;
210 	while(p->op < p->oe || stageoutput(p)) {
211 		if(readstatus(p) & USTAT_TBE) {
212 			DEBUG("T");
213 			reg->txbuf = *(p->op++);
214 			p->wcount++;
215 		} else {
216 			DEBUG("F");
217 			gag = 0;
218 			break;
219 		}
220 	}
221 	if (gag) {
222 		DEBUG("G");
223 		p->kickme = 1;
224 		intrmask(UARTTXbit(p->port), 0);
225 	}
226 }
227 
228 static void
uartrecvq(Uart * p)229 uartrecvq(Uart *p)
230 {
231 	uchar *cp = p->istage;
232 	int n = p->ip - cp;
233 
234 	if(n == 0)
235 		return;
236 	if(p->putc)
237 		while(n-- > 0)
238 			p->putc(p->iq, *cp++);
239 	else if(p->iq)
240 		if(qproduce(p->iq, p->istage, n) < n)
241 			print("qproduce flow control");
242 	p->ip = p->istage;
243 }
244 
245 static void
uartrecv(Uart * p)246 uartrecv(Uart *p)
247 {
248 	UartReg *reg = p->reg;
249 	uchar stat = readstatus(p);
250 
251 DEBUG("R");
252 	if (stat & USTAT_RDR) {
253 		int c;
254 		c = reg->rxbuf;
255 		if (c == '?') {
256 			DEBUG("mod 0x%.8lx\n", INTREG->mod);
257 			DEBUG("msk 0x%.8lx\n", INTREG->msk);
258 			DEBUG("pnd 0x%.8lx\n", INTREG->pnd);
259 		}
260 		*p->ip++ = c;
261 /*		if(p->ip >= p->ie) */
262 			uartrecvq(p);
263 		p->rcount++;
264 	}
265 }
266 
267 static void
uartkick(void * a)268 uartkick(void *a)
269 {
270 	Uart *p = a;
271 	int x = splhi();
272 	DEBUG("k");
273 	if (p->kickme) {
274 		p->kickme = 0;
275 		DEBUG("K");
276 		intrunmask(UARTTXbit(p->port), 0);
277 	}
278 	splx(x);
279 }
280 
281 /*
282  *  UART Interrupt Handler
283  */
284 static void
uarttxintr(Ureg *,void * arg)285 uarttxintr(Ureg*, void* arg)
286 {
287 	Uart *p = arg;
288 	intrclear(UARTTXbit(p->port), 0);
289 	p->inters++;
290 	p->winters++;
291 	uartxmit(p);
292 }
293 
294 static void
uartrxintr(Ureg *,void * arg)295 uartrxintr(Ureg*, void* arg)
296 {
297 	Uart *p = arg;
298 	intrclear(UARTRXbit(p->port), 0);
299 	p->inters++;
300 	p->rinters++;
301 	uartrecv(p);
302 }
303 
304 
305 static void
uartsetup(ulong port,char * name)306 uartsetup(ulong port, char *name)
307 {
308 	Uart *p;
309 
310 	if(nuart >= Nuart)
311 		return;
312 
313 	p = xalloc(sizeof(Uart));
314 	uart[nuart++] = p;
315 	strcpy(p->name, name);
316 
317 	p->reg = &UARTREG[port];
318 	p->bps = 9600;
319 	p->bits = 8;
320 	p->parity = 'n';
321 	p->stop = 1;
322 	p->kickme = 0;
323 	p->port = port;
324 
325 	p->iq = qopen(4*1024, 0, 0 , p);
326 	p->oq = qopen(4*1024, 0, uartkick, p);
327 
328 	p->ip = p->istage;
329 	p->ie = &p->istage[Stagesize];
330 	p->op = p->ostage;
331 	p->oe = p->ostage;
332 
333 	intrenable(UARTTXbit(port), uarttxintr, p, 0);
334 	intrenable(UARTRXbit(port), uartrxintr, p, 0);
335 }
336 
337 static void
uartinstall(void)338 uartinstall(void)
339 {
340 	static int already;
341 
342 	if(already)
343 		return;
344 	already = 1;
345 
346 	uartsetup(0, "eia0");
347 //	uartsetup(1, "eia1");
348 }
349 
350 /*
351  *  called by main() to configure a duart port as a console or a mouse
352  */
353 void
uartspecial(int port,int bps,char parity,Queue ** in,Queue ** out,int (* putc)(Queue *,int))354 uartspecial(int port, int bps, char parity, Queue **in, Queue **out, int (*putc)(Queue*, int))
355 {
356 	Uart *p;
357 
358 	uartinstall();
359 	if(port >= nuart)
360 		return;
361 	p = uart[port];
362 	if(bps)
363 		p->bps = bps;
364 	if(parity)
365 		p->parity = parity;
366 	uartenable(p);
367 	p->putc = putc;
368 	if(in)
369 		*in = p->iq;
370 	if(out)
371 		*out = p->oq;
372 	p->opens++;
373 }
374 
375 Dirtab *uartdir;
376 int ndir;
377 
378 static void
setlength(int i)379 setlength(int i)
380 {
381 	Uart *p;
382 
383 	if(i > 0){
384 		p = uart[i];
385 		if(p && p->opens && p->iq)
386 			uartdir[1+3*i].length = qlen(p->iq);
387 	} else for(i = 0; i < nuart; i++){
388 		p = uart[i];
389 		if(p && p->opens && p->iq)
390 			uartdir[1+3*i].length = qlen(p->iq);
391 	}
392 }
393 
394 /*
395  *  all uarts must be uartsetup() by this point or inside of uartinstall()
396  */
397 static void
uartreset(void)398 uartreset(void)
399 {
400 	int i;
401 	Dirtab *dp;
402 
403 	uartinstall();
404 
405 	ndir = 1+3*nuart;
406 	uartdir = xalloc(ndir * sizeof(Dirtab));
407 	dp = uartdir;
408 	strcpy(dp->name, ".");
409 	mkqid(&dp->qid, 0, 0, QTDIR);
410 	dp->length = 0;
411 	dp->perm = DMDIR|0555;
412 	dp++;
413 	for(i = 0; i < nuart; i++){
414 		/* 3 directory entries per port */
415 		strcpy(dp->name, uart[i]->name);
416 		dp->qid.path = NETQID(i, Ndataqid);
417 		dp->perm = 0660;
418 		dp++;
419 		sprint(dp->name, "%sctl", uart[i]->name);
420 		dp->qid.path = NETQID(i, Nctlqid);
421 		dp->perm = 0660;
422 		dp++;
423 		sprint(dp->name, "%sstatus", uart[i]->name);
424 		dp->qid.path = NETQID(i, Nstatqid);
425 		dp->perm = 0444;
426 		dp++;
427 	}
428 }
429 
430 static Chan*
uartattach(char * spec)431 uartattach(char *spec)
432 {
433 	return devattach('t', spec);
434 }
435 
436 static Walkqid*
uartwalk(Chan * c,Chan * nc,char ** name,int nname)437 uartwalk(Chan *c, Chan *nc, char **name, int nname)
438 {
439 	return devwalk(c, nc, name, nname, uartdir, ndir, devgen);
440 }
441 
442 static int
uartstat(Chan * c,uchar * dp,int n)443 uartstat(Chan *c, uchar *dp, int n)
444 {
445 	if(NETTYPE(c->qid.path) == Ndataqid)
446 		setlength(NETID(c->qid.path));
447 	return devstat(c, dp, n, uartdir, ndir, devgen);
448 }
449 
450 static Chan*
uartopen(Chan * c,int omode)451 uartopen(Chan *c, int omode)
452 {
453 	Uart *p;
454 
455 	c = devopen(c, omode, uartdir, ndir, devgen);
456 
457 	switch(NETTYPE(c->qid.path)){
458 	case Nctlqid:
459 	case Ndataqid:
460 		p = uart[NETID(c->qid.path)];
461 		qlock(p);
462 		if(p->opens++ == 0){
463 			uartenable(p);
464 			qreopen(p->iq);
465 			qreopen(p->oq);
466 		}
467 		qunlock(p);
468 		break;
469 	}
470 
471 	return c;
472 }
473 
474 static void
uartclose(Chan * c)475 uartclose(Chan *c)
476 {
477 	Uart *p;
478 
479 	if(c->qid.type & QTDIR)
480 		return;
481 	if((c->flag & COPEN) == 0)
482 		return;
483 	switch(NETTYPE(c->qid.path)){
484 	case Ndataqid:
485 	case Nctlqid:
486 		p = uart[NETID(c->qid.path)];
487 		qlock(p);
488 		if(--(p->opens) == 0){
489 			uartdisable(p);
490 			qclose(p->iq);
491 			qclose(p->oq);
492 			p->ip = p->istage;
493 		}
494 		qunlock(p);
495 		break;
496 	}
497 }
498 
499 static long
uartstatus(Chan * c,Uart * p,void * buf,long n,long offset)500 uartstatus(Chan *c, Uart *p, void *buf, long n, long offset)
501 {
502 	char str[256];
503 	USED(c);
504 
505 	str[0] = 0;
506 	sprint(str, "opens %d ferr %d oerr %d perr %d baud %d parity %c"
507 			" intr %d rintr %d wintr %d"
508 			" rcount %d wcount %d",
509 		p->opens, p->frame, p->overrun, p->perror, p->bps, p->parity,
510 		p->inters, p->rinters, p->winters,
511 		p->rcount, p->wcount);
512 
513 	strcat(str, "\n");
514 	return readstr(offset, buf, n, str);
515 }
516 
517 static long
uartread(Chan * c,void * buf,long n,vlong offset)518 uartread(Chan *c, void *buf, long n, vlong offset)
519 {
520 	Uart *p;
521 
522 	if(c->qid.type & QTDIR){
523 		setlength(-1);
524 		return devdirread(c, buf, n, uartdir, ndir, devgen);
525 	}
526 
527 	p = uart[NETID(c->qid.path)];
528 	switch(NETTYPE(c->qid.path)){
529 	case Ndataqid:
530 		return qread(p->iq, buf, n);
531 	case Nctlqid:
532 		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
533 	case Nstatqid:
534 		return uartstatus(c, p, buf, n, offset);
535 	}
536 
537 	return 0;
538 }
539 
540 static void
uartctl(Uart * p,char * cmd)541 uartctl(Uart *p, char *cmd)
542 {
543 	int i, n;
544 
545 	/* let output drain for a while (up to 4 secs) */
546 	for(i = 0; i < 200 && (qlen(p->oq) || (readstatus(p) & USTAT_TC) == 0); i++)
547 		tsleep(&up->sleep, return0, 0, 20);
548 
549 	if(strncmp(cmd, "break", 5) == 0){
550 		uartbreak(p, 0);
551 		return;
552 	}
553 
554 	n = atoi(cmd+1);
555 	switch(*cmd){
556 	case 'B':
557 	case 'b':
558 		if(n <= 0)
559 			error(Ebadarg);
560 		p->bps = n;
561 		uartset(p);
562 		break;
563 	case 'f':
564 	case 'F':
565 		qflush(p->oq);
566 		break;
567 	case 'H':
568 	case 'h':
569 		qhangup(p->iq, 0);
570 		qhangup(p->oq, 0);
571 		break;
572 	case 'L':
573 	case 'l':
574 		if(n < 7 || n > 8)
575 			error(Ebadarg);
576 		p->bits = n;
577 		uartset(p);
578 		break;
579 	case 'n':
580 	case 'N':
581 		qnoblock(p->oq, n);
582 		break;
583 	case 'P':
584 	case 'p':
585 		p->parity = *(cmd+1);
586 		uartset(p);
587 		break;
588 	case 'K':
589 	case 'k':
590 		uartbreak(p, n);
591 		break;
592 	case 'Q':
593 	case 'q':
594 		qsetlimit(p->iq, n);
595 		qsetlimit(p->oq, n);
596 		break;
597 	case 's':
598 	case 'S':
599 		if(n < 1 || n > 2)
600 			error(Ebadarg);
601 		p->stop = n;
602 		uartset(p);
603 		break;
604 	}
605 }
606 
607 static long
uartwrite(Chan * c,void * buf,long n,vlong offset)608 uartwrite(Chan *c, void *buf, long n, vlong offset)
609 {
610 	Uart *p;
611 	char cmd[32];
612 
613 	USED(offset);
614 
615 	if(c->qid.type & QTDIR)
616 		error(Eperm);
617 
618 	p = uart[NETID(c->qid.path)];
619 
620 	switch(NETTYPE(c->qid.path)){
621 	case Ndataqid:
622 		return qwrite(p->oq, buf, n);
623 	case Nctlqid:
624 
625 		if(n >= sizeof(cmd))
626 			n = sizeof(cmd)-1;
627 		memmove(cmd, buf, n);
628 		cmd[n] = 0;
629 		uartctl(p, cmd);
630 		return n;
631 	}
632 }
633 
634 static int
uartwstat(Chan * c,uchar * dp,int n)635 uartwstat(Chan *c, uchar *dp, int n)
636 {
637 	error(Eperm);
638 	return 0;
639 #ifdef xxx
640 	Dir d;
641 	Dirtab *dt;
642 
643 	if(!iseve())
644 		error(Eperm);
645 	if(c->qid.type & QTDIR)
646 		error(Eperm);
647 	if(NETTYPE(c->qid.path) == Nstatqid)
648 		error(Eperm);
649 
650 	dt = &uartdir[3 * NETID(c->qid.path)];
651 	convM2D(dp, &d);
652 	d.mode &= 0666;
653 	dt[0].perm = dt[1].perm = d.mode;
654 #endif
655 }
656 
657 Dev uartdevtab = {
658 	't',
659 	"uart",
660 
661 	uartreset,
662 	devinit,
663 	devshutdown,
664 	uartattach,
665 	uartwalk,
666 	uartstat,
667 	uartopen,
668 	devcreate,
669 	uartclose,
670 	uartread,
671 	devbread,
672 	uartwrite,
673 	devbwrite,
674 	devremove,
675 	uartwstat,
676 };
677 
678 void
uartputc(int c)679 uartputc(int c)
680 {
681 	UartReg *u;
682 
683 	if (!c)
684 		return;
685 	u = &UARTREG[1];
686 	while ((u->stat & USTAT_TBE) == 0)
687 		;
688 	u->txbuf = c;
689 	if (c == '\n')
690 		while((u->stat & USTAT_TC) == 0)	/* flush xmit fifo */
691 			;
692 }
693 
694 void
uartputs(char * data,int len)695 uartputs(char *data, int len)
696 {
697 	int x;
698 
699 	clockpoll();
700 	x = splfhi();
701 	while (len--){
702 		if(*data == '\n')
703 			uartputc('\r');
704 		uartputc(*data++);
705 	}
706 	splx(x);
707 }
708 
709 int
uartgetc(void)710 uartgetc(void)
711 {
712 	UartReg *u;
713 
714 	clockcheck();
715 	u = &UARTREG[1];
716 	while((u->stat & USTAT_RDR) == 0)
717 		clockcheck();
718 	return u->rxbuf;
719 }
720