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