xref: /inferno-os/os/port/devuart.c (revision ecc9caba0e344ed50c05ee8156b2734f4d76e463)
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 
9 #include	"../port/netif.h"
10 
11 #include	"../port/uart.h"
12 
13 enum
14 {
15 	/* soft flow control chars */
16 	CTLS= 023,
17 	CTLQ= 021,
18 };
19 
20 extern Dev uartdevtab;
21 extern PhysUart* physuart[];
22 
23 static Uart* uartlist;
24 static Uart** uart;
25 static int uartnuart;
26 static Dirtab *uartdir;
27 static int uartndir;
28 
29 struct Uartalloc {
30 	Lock;
31 	Uart *elist;	/* list of enabled interfaces */
32 } uartalloc;
33 
34 static void	uartclock(void);
35 static void	uartflow(void*);
36 
37 /*
38  *  enable/disable uart and add/remove to list of enabled uarts
39  */
40 static Uart*
41 uartenable(Uart *p)
42 {
43 	Uart **l;
44 
45 	if(p->iq == nil){
46 		if((p->iq = qopen(4*1024, 0, uartflow, p)) == nil)
47 			return nil;
48 	}
49 	else
50 		qreopen(p->iq);
51 	if(p->oq == nil){
52 		if((p->oq = qopen(4*1024, 0, uartkick, p)) == nil){
53 			qfree(p->iq);
54 			p->iq = nil;
55 			return nil;
56 		}
57 	}
58 	else
59 		qreopen(p->oq);
60 
61 	p->ir = p->istage;
62 	p->iw = p->istage;
63 	p->ie = &p->istage[Stagesize];
64 	p->op = p->ostage;
65 	p->oe = p->ostage;
66 
67 	p->hup_dsr = p->hup_dcd = 0;
68 	p->dsr = p->dcd = 0;
69 
70 	/* assume we can send */
71 	p->cts = 1;
72 	p->ctsbackoff = 0;
73 
74 	if(p->bits == 0)
75 		uartctl(p, "l8");
76 	if(p->stop == 0)
77 		uartctl(p, "s1");
78 	if(p->parity == 0)
79 		uartctl(p, "pn");
80 	if(p->baud == 0)
81 		uartctl(p, "b9600");
82 	(*p->phys->enable)(p, 1);
83 
84 	lock(&uartalloc);
85 	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
86 		if(*l == p)
87 			break;
88 	}
89 	if(*l == 0){
90 		p->elist = uartalloc.elist;
91 		uartalloc.elist = p;
92 	}
93 	p->enabled = 1;
94 	unlock(&uartalloc);
95 
96 	return p;
97 }
98 
99 static void
100 uartdisable(Uart *p)
101 {
102 	Uart **l;
103 
104 	(*p->phys->disable)(p);
105 
106 	lock(&uartalloc);
107 	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
108 		if(*l == p){
109 			*l = p->elist;
110 			break;
111 		}
112 	}
113 	p->enabled = 0;
114 	unlock(&uartalloc);
115 }
116 
117 void
118 uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200)
119 {
120 	qlock(p);
121 	if(p->opens++ == 0 && uartenable(p) == nil){
122 		qunlock(p);
123 		error(Enodev);
124 	}
125 	if(setb1200)
126 		uartctl(p, "b1200");
127 	p->putc = putc;
128 	p->special = 1;
129 	qunlock(p);
130 }
131 
132 void
133 uartsetmouseputc(Uart* p, int (*putc)(Queue*, int))
134 {
135 	qlock(p);
136 	if(p->opens == 0 || p->special == 0){
137 		qunlock(p);
138 		error(Enodev);
139 	}
140 	p->putc = putc;
141 	qunlock(p);
142 }
143 
144 static void
145 setlength(int i)
146 {
147 	Uart *p;
148 
149 	if(i > 0){
150 		p = uart[i];
151 		if(p && p->opens && p->iq)
152 			uartdir[1+3*i].length = qlen(p->iq);
153 	} else for(i = 0; i < uartnuart; i++){
154 		p = uart[i];
155 		if(p && p->opens && p->iq)
156 			uartdir[1+3*i].length = qlen(p->iq);
157 	}
158 }
159 
160 /*
161  *  set up the '#t' directory
162  */
163 static void
164 uartreset(void)
165 {
166 	int i;
167 	Dirtab *dp;
168 	Uart *p, *tail;
169 
170 	tail = nil;
171 	for(i = 0; physuart[i] != nil; i++){
172 		if(physuart[i]->pnp == nil)
173 			continue;
174 		if((p = physuart[i]->pnp()) == nil)
175 			continue;
176 		if(uartlist != nil)
177 			tail->next = p;
178 		else
179 			uartlist = p;
180 		for(tail = p; tail->next != nil; tail = tail->next)
181 			uartnuart++;
182 		uartnuart++;
183 	}
184 
185 	if(uartnuart)
186 		uart = xalloc(uartnuart*sizeof(Uart*));
187 
188 	uartndir = 1 + 3*uartnuart;
189 	uartdir = xalloc(uartndir * sizeof(Dirtab));
190 	dp = uartdir;
191 	strcpy(dp->name, ".");
192 	mkqid(&dp->qid, 0, 0, QTDIR);
193 	dp->length = 0;
194 	dp->perm = DMDIR|0555;
195 	dp++;
196 	p = uartlist;
197 	for(i = 0; i < uartnuart; i++){
198 		/* 3 directory entries per port */
199 		sprint(dp->name, "eia%d", i);
200 		dp->qid.path = NETQID(i, Ndataqid);
201 		dp->perm = 0660;
202 		dp++;
203 		sprint(dp->name, "eia%dctl", i);
204 		dp->qid.path = NETQID(i, Nctlqid);
205 		dp->perm = 0660;
206 		dp++;
207 		sprint(dp->name, "eia%dstatus", i);
208 		dp->qid.path = NETQID(i, Nstatqid);
209 		dp->perm = 0444;
210 		dp++;
211 
212 		uart[i] = p;
213 		p->dev = i;
214 		if(p->console || p->special){
215 			if(uartenable(p) != nil){
216 				if(p->console){
217 					kbdq = p->iq;
218 					printq = p->oq;
219 					p->putc = kbdcr2nl;
220 				}
221 				p->opens++;
222 			}
223 		}
224 		p = p->next;
225 	}
226 
227 	if(uartnuart){
228 		/*
229 		 * at 115200 baud, the 1024 char buffer takes 56 ms to process,
230 		 * processing it every 22 ms should be fine
231 		 */
232 		addclock0link(uartclock, 22);
233 	}
234 }
235 
236 
237 static Chan*
238 uartattach(char *spec)
239 {
240 	return devattach('t', spec);
241 }
242 
243 static Walkqid*
244 uartwalk(Chan *c, Chan *nc, char **name, int nname)
245 {
246 	return devwalk(c, nc, name, nname, uartdir, uartndir, devgen);
247 }
248 
249 static int
250 uartstat(Chan *c, uchar *dp, int n)
251 {
252 	if(NETTYPE(c->qid.path) == Ndataqid)
253 		setlength(NETID(c->qid.path));
254 	return devstat(c, dp, n, uartdir, uartndir, devgen);
255 }
256 
257 static Chan*
258 uartopen(Chan *c, int omode)
259 {
260 	Uart *p;
261 
262 	c = devopen(c, omode, uartdir, uartndir, devgen);
263 
264 	switch(NETTYPE(c->qid.path)){
265 	case Nctlqid:
266 	case Ndataqid:
267 		p = uart[NETID(c->qid.path)];
268 		qlock(p);
269 		if(p->opens++ == 0 && uartenable(p) == nil){
270 			qunlock(p);
271 			c->flag &= ~COPEN;
272 			error(Enodev);
273 		}
274 		qunlock(p);
275 		break;
276 	}
277 
278 	c->iounit = qiomaxatomic;
279 	return c;
280 }
281 
282 static int
283 uartdrained(void* arg)
284 {
285 	Uart *p;
286 
287 	p = arg;
288 	return qlen(p->oq) == 0 && p->op == p->oe;
289 }
290 
291 static void
292 uartdrainoutput(Uart *p)
293 {
294 	if(!p->enabled)
295 		return;
296 
297 	p->drain = 1;
298 	if(waserror()){
299 		p->drain = 0;
300 		nexterror();
301 	}
302 	sleep(&p->r, uartdrained, p);
303 	poperror();
304 }
305 
306 static void
307 uartclose(Chan *c)
308 {
309 	Uart *p;
310 
311 	if(c->qid.type & QTDIR)
312 		return;
313 	if((c->flag & COPEN) == 0)
314 		return;
315 	switch(NETTYPE(c->qid.path)){
316 	case Ndataqid:
317 	case Nctlqid:
318 		p = uart[NETID(c->qid.path)];
319 		qlock(p);
320 		if(--(p->opens) == 0){
321 			qclose(p->iq);
322 			p->ir = p->iw = p->istage;
323 
324 			/*
325 			 */
326 			qhangup(p->oq, nil);
327 			if(!waserror()){
328 				uartdrainoutput(p);
329 				poperror();
330 			}
331 			qclose(p->oq);
332 			uartdisable(p);
333 			p->dcd = p->dsr = p->dohup = 0;
334 		}
335 		qunlock(p);
336 		break;
337 	}
338 }
339 
340 static long
341 uartread(Chan *c, void *buf, long n, vlong off)
342 {
343 	Uart *p;
344 	ulong offset = off;
345 
346 	if(c->qid.type & QTDIR){
347 		setlength(-1);
348 		return devdirread(c, buf, n, uartdir, uartndir, devgen);
349 	}
350 
351 	p = uart[NETID(c->qid.path)];
352 	switch(NETTYPE(c->qid.path)){
353 	case Ndataqid:
354 		return qread(p->iq, buf, n);
355 	case Nctlqid:
356 		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
357 	case Nstatqid:
358 		return (*p->phys->status)(p, buf, n, offset);
359 	}
360 
361 	return 0;
362 }
363 
364 int
365 uartctl(Uart *p, char *cmd)
366 {
367 	char *f[16];
368 	int i, n, nf;
369 
370 	nf = tokenize(cmd, f, nelem(f));
371 	for(i = 0; i < nf; i++){
372 		if(strncmp(f[i], "break", 5) == 0){
373 			(*p->phys->dobreak)(p, 0);
374 			continue;
375 		}
376 
377 		n = atoi(f[i]+1);
378 		switch(*f[i]){
379 		case 'B':
380 		case 'b':
381 			uartdrainoutput(p);
382 			if((*p->phys->baud)(p, n) < 0)
383 				return -1;
384 			break;
385 		case 'C':
386 		case 'c':
387 			p->hup_dcd = n;
388 			break;
389 		case 'D':
390 		case 'd':
391 			uartdrainoutput(p);
392 			(*p->phys->dtr)(p, n);
393 			break;
394 		case 'E':
395 		case 'e':
396 			p->hup_dsr = n;
397 			break;
398 		case 'f':
399 		case 'F':
400 			if(p->oq != nil)
401 				qflush(p->oq);
402 			break;
403 		case 'H':
404 		case 'h':
405 			if(p->iq != nil)
406 				qhangup(p->iq, 0);
407 			if(p->oq != nil)
408 				qhangup(p->oq, 0);
409 			break;
410 		case 'i':
411 		case 'I':
412 			uartdrainoutput(p);
413 			(*p->phys->fifo)(p, n);
414 			break;
415 		case 'K':
416 		case 'k':
417 			uartdrainoutput(p);
418 			(*p->phys->dobreak)(p, n);
419 			break;
420 		case 'L':
421 		case 'l':
422 			uartdrainoutput(p);
423 			if((*p->phys->bits)(p, n) < 0)
424 				return -1;
425 			break;
426 		case 'm':
427 		case 'M':
428 			uartdrainoutput(p);
429 			(*p->phys->modemctl)(p, n);
430 			break;
431 		case 'n':
432 		case 'N':
433 			if(p->oq != nil)
434 				qnoblock(p->oq, n);
435 			break;
436 		case 'P':
437 		case 'p':
438 			uartdrainoutput(p);
439 			if((*p->phys->parity)(p, *(f[i]+1)) < 0)
440 				return -1;
441 			break;
442 		case 'Q':
443 		case 'q':
444 			if(p->iq != nil)
445 				qsetlimit(p->iq, n);
446 			if(p->oq != nil)
447 				qsetlimit(p->oq, n);
448 			break;
449 		case 'R':
450 		case 'r':
451 			uartdrainoutput(p);
452 			(*p->phys->rts)(p, n);
453 			break;
454 		case 'S':
455 		case 's':
456 			uartdrainoutput(p);
457 			if((*p->phys->stop)(p, n) < 0)
458 				return -1;
459 			break;
460 		case 'T':
461 		case 't':
462 			p->dcdts = n;
463 			break;
464 		case 'W':
465 		case 'w':
466 			/* obsolete */
467 			break;
468 		case 'X':
469 		case 'x':
470 			if(p->enabled){
471 				ilock(&p->tlock);
472 				p->xonoff = n;
473 				iunlock(&p->tlock);
474 			}
475 			break;
476 		}
477 	}
478 	return 0;
479 }
480 
481 static long
482 uartwrite(Chan *c, void *buf, long n, vlong)
483 {
484 	Uart *p;
485 	char *cmd;
486 
487 	if(c->qid.type & QTDIR)
488 		error(Eperm);
489 
490 	p = uart[NETID(c->qid.path)];
491 
492 	switch(NETTYPE(c->qid.path)){
493 	case Ndataqid:
494 		qlock(p);
495 		if(waserror()){
496 			qunlock(p);
497 			nexterror();
498 		}
499 
500 		n = qwrite(p->oq, buf, n);
501 
502 		qunlock(p);
503 		poperror();
504 		break;
505 	case Nctlqid:
506 		cmd = malloc(n+1);
507 		memmove(cmd, buf, n);
508 		cmd[n] = 0;
509 		qlock(p);
510 		if(waserror()){
511 			qunlock(p);
512 			free(cmd);
513 			nexterror();
514 		}
515 
516 		/* let output drain */
517 		if(uartctl(p, cmd) < 0)
518 			error(Ebadarg);
519 
520 		qunlock(p);
521 		poperror();
522 		free(cmd);
523 		break;
524 	}
525 
526 	return n;
527 }
528 
529 static int
530 uartwstat(Chan *c, uchar *dp, int n)
531 {
532 	Dir d;
533 	Dirtab *dt;
534 
535 	if(!iseve())
536 		error(Eperm);
537 	if(QTDIR & c->qid.type)
538 		error(Eperm);
539 	if(NETTYPE(c->qid.path) == Nstatqid)
540 		error(Eperm);
541 
542 	dt = &uartdir[1 + 3 * NETID(c->qid.path)];
543 	n = convM2D(dp, n, &d, nil);
544 	if(n == 0)
545 		error(Eshortstat);
546 	if(d.mode != ~0UL)
547 		dt[0].perm = dt[1].perm = d.mode;
548 	return n;
549 }
550 
551 void
552 uartpower(int on)
553 {
554 	Uart *p;
555 
556 	for(p = uartlist; p != nil; p = p->next) {
557 		if(p->phys->power)
558 			(*p->phys->power)(p, on);
559 	}
560 }
561 
562 Dev uartdevtab = {
563 	't',
564 	"uart",
565 
566 	uartreset,
567 	devinit,
568 	devshutdown,
569 	uartattach,
570 	uartwalk,
571 	uartstat,
572 	uartopen,
573 	devcreate,
574 	uartclose,
575 	uartread,
576 	devbread,
577 	uartwrite,
578 	devbwrite,
579 	devremove,
580 	uartwstat,
581 	uartpower,
582 };
583 
584 /*
585  *  restart input if it's off
586  */
587 static void
588 uartflow(void *v)
589 {
590 	Uart *p;
591 
592 	p = v;
593 	if(p->modem)
594 		(*p->phys->rts)(p, 1);
595 }
596 
597 /*
598  *  put some bytes into the local queue to avoid calling
599  *  qconsume for every character
600  */
601 int
602 uartstageoutput(Uart *p)
603 {
604 	int n;
605 
606 	n = qconsume(p->oq, p->ostage, Stagesize);
607 	if(n <= 0)
608 		return 0;
609 	p->op = p->ostage;
610 	p->oe = p->ostage + n;
611 	return n;
612 }
613 
614 /*
615  *  restart output
616  */
617 void
618 uartkick(void *v)
619 {
620 	Uart *p = v;
621 
622 	if(p->blocked)
623 		return;
624 
625 	ilock(&p->tlock);
626 	(*p->phys->kick)(p);
627 	iunlock(&p->tlock);
628 
629 	if(p->drain && uartdrained(p)){
630 		p->drain = 0;
631 		wakeup(&p->r);
632 	}
633 }
634 
635 /*
636  *  receive a character at interrupt time
637  */
638 void
639 uartrecv(Uart *p,  char ch)
640 {
641 	uchar *next;
642 
643 	/* software flow control */
644 	if(p->xonoff){
645 		if(ch == CTLS){
646 			p->blocked = 1;
647 		}else if(ch == CTLQ){
648 			p->blocked = 0;
649 			p->ctsbackoff = 2; /* clock gets output going again */
650 		}
651 	}
652 
653 	/* receive the character */
654 	if(p->putc)
655 		p->putc(p->iq, ch);
656 	else{
657 		next = p->iw + 1;
658 		if(next == p->ie)
659 			next = p->istage;
660 		if(next != p->ir){
661 			*p->iw = ch;
662 			p->iw = next;
663 		}
664 	}
665 }
666 
667 /*
668  *  we save up input characters till clock time to reduce
669  *  per character interrupt overhead.
670  */
671 static void
672 uartclock(void)
673 {
674 	Uart *p;
675 	uchar *iw;
676 
677 	for(p = uartalloc.elist; p; p = p->elist){
678 
679 		/* this amortizes cost of qproduce to many chars */
680 		if(p->iw != p->ir){
681 			iw = p->iw;
682 			if(iw < p->ir){
683 				if(qproduce(p->iq, p->ir, p->ie-p->ir) < 0)
684 					(*p->phys->rts)(p, 0);
685 				p->ir = p->istage;
686 			}
687 			if(iw > p->ir)
688 				if(qproduce(p->iq, p->ir, iw-p->ir) < 0)
689 					(*p->phys->rts)(p, 0);
690 			p->ir = iw;
691 		}
692 
693 		/* hang up if requested */
694 		if(p->dohup){
695 			qhangup(p->iq, 0);
696 			qhangup(p->oq, 0);
697 			p->dohup = 0;
698 		}
699 
700 		/* this adds hysteresis to hardware/software flow control */
701 		if(p->ctsbackoff){
702 			ilock(&p->tlock);
703 			if(p->ctsbackoff){
704 				if(--(p->ctsbackoff) == 0)
705 					(*p->phys->kick)(p);
706 			}
707 			iunlock(&p->tlock);
708 		}
709 	}
710 }
711 
712 /*
713  * polling console input, output
714  */
715 
716 Uart* consuart;
717 
718 int
719 uartgetc(void)
720 {
721 	if(consuart == nil || consuart->phys->getc == nil)
722 		return -1;
723 	return consuart->phys->getc(consuart);
724 }
725 
726 void
727 uartputc(int c)
728 {
729 	if(consuart == nil || consuart->phys->putc == nil)
730 		return;
731 	consuart->phys->putc(consuart, c);
732 }
733 
734 void
735 uartputs(char *s, int n)
736 {
737 	char *e;
738 
739 	if(consuart == nil || consuart->phys->putc == nil)
740 		return;
741 
742 	e = s+n;
743 	for(; s<e; s++){
744 		if(*s == '\n')
745 			consuart->phys->putc(consuart, '\r');
746 		consuart->phys->putc(consuart, *s);
747 	}
748 }
749