]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devuart.c
pc kernel: fix wrong simd exception mask (fixes go bootstrap)
[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)
322                 return;
323
324         p->drain = 1;
325         if(waserror()){
326                 p->drain = 0;
327                 nexterror();
328         }
329         sleep(&p->r, uartdrained, p);
330         poperror();
331 }
332
333 static void
334 uartclose(Chan *c)
335 {
336         Uart *p;
337
338         if(c->qid.type & QTDIR)
339                 return;
340         if((c->flag & COPEN) == 0)
341                 return;
342         switch(NETTYPE(c->qid.path)){
343         case Ndataqid:
344         case Nctlqid:
345                 p = uart[NETID(c->qid.path)];
346                 qlock(p);
347                 if(--(p->opens) == 0){
348                         qclose(p->iq);
349                         ilock(&p->rlock);
350                         p->ir = p->iw = p->istage;
351                         iunlock(&p->rlock);
352
353                         /*
354                          */
355                         qhangup(p->oq, nil);
356                         if(!waserror()){
357                                 uartdrainoutput(p);
358                                 poperror();
359                         }
360                         qclose(p->oq);
361                         uartdisable(p);
362                         p->dcd = p->dsr = p->dohup = 0;
363                 }
364                 qunlock(p);
365                 break;
366         }
367 }
368
369 static long
370 uartread(Chan *c, void *buf, long n, vlong off)
371 {
372         Uart *p;
373         ulong offset = off;
374
375         if(c->qid.type & QTDIR){
376                 setlength(-1);
377                 return devdirread(c, buf, n, uartdir, uartndir, devgen);
378         }
379
380         p = uart[NETID(c->qid.path)];
381         switch(NETTYPE(c->qid.path)){
382         case Ndataqid:
383                 return qread(p->iq, buf, n);
384         case Nctlqid:
385                 return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
386         case Nstatqid:
387                 return (*p->phys->status)(p, buf, n, offset);
388         }
389
390         return 0;
391 }
392
393 int
394 uartctl(Uart *p, char *cmd)
395 {
396         char *f[16];
397         int i, n, nf;
398
399         nf = tokenize(cmd, f, nelem(f));
400         for(i = 0; i < nf; i++){
401                 if(strncmp(f[i], "break", 5) == 0){
402                         (*p->phys->dobreak)(p, 0);
403                         continue;
404                 }
405
406                 n = atoi(f[i]+1);
407                 switch(*f[i]){
408                 case 'B':
409                 case 'b':
410                         uartdrainoutput(p);
411                         if((*p->phys->baud)(p, n) < 0)
412                                 return -1;
413                         break;
414                 case 'C':
415                 case 'c':
416                         p->hup_dcd = n;
417                         break;
418                 case 'D':
419                 case 'd':
420                         uartdrainoutput(p);
421                         (*p->phys->dtr)(p, n);
422                         break;
423                 case 'E':
424                 case 'e':
425                         p->hup_dsr = n;
426                         break;
427                 case 'f':
428                 case 'F':
429                         if(p->oq != nil)
430                                 qflush(p->oq);
431                         break;
432                 case 'H':
433                 case 'h':
434                         if(p->iq != nil)
435                                 qhangup(p->iq, 0);
436                         if(p->oq != nil)
437                                 qhangup(p->oq, 0);
438                         break;
439                 case 'i':
440                 case 'I':
441                         uartdrainoutput(p);
442                         (*p->phys->fifo)(p, n);
443                         break;
444                 case 'K':
445                 case 'k':
446                         uartdrainoutput(p);
447                         (*p->phys->dobreak)(p, n);
448                         break;
449                 case 'L':
450                 case 'l':
451                         uartdrainoutput(p);
452                         if((*p->phys->bits)(p, n) < 0)
453                                 return -1;
454                         break;
455                 case 'm':
456                 case 'M':
457                         uartdrainoutput(p);
458                         (*p->phys->modemctl)(p, n);
459                         break;
460                 case 'n':
461                 case 'N':
462                         if(p->oq != nil)
463                                 qnoblock(p->oq, n);
464                         break;
465                 case 'P':
466                 case 'p':
467                         uartdrainoutput(p);
468                         if((*p->phys->parity)(p, *(f[i]+1)) < 0)
469                                 return -1;
470                         break;
471                 case 'Q':
472                 case 'q':
473                         if(p->iq != nil)
474                                 qsetlimit(p->iq, n);
475                         if(p->oq != nil)
476                                 qsetlimit(p->oq, n);
477                         break;
478                 case 'R':
479                 case 'r':
480                         uartdrainoutput(p);
481                         (*p->phys->rts)(p, n);
482                         break;
483                 case 'S':
484                 case 's':
485                         uartdrainoutput(p);
486                         if((*p->phys->stop)(p, n) < 0)
487                                 return -1;
488                         break;
489                 case 'W':
490                 case 'w':
491                         if(uarttimer == nil || n < 1)
492                                 return -1;
493                         uarttimer->tns = (vlong)n * 100000LL;
494                         break;
495                 case 'X':
496                 case 'x':
497                         ilock(&p->tlock);
498                         p->xonoff = n;
499                         p->blocked = 0;
500                         iunlock(&p->tlock);
501                         break;
502                 }
503         }
504         return 0;
505 }
506
507 static long
508 uartwrite(Chan *c, void *buf, long n, vlong)
509 {
510         Uart *p;
511         char *cmd;
512
513         if(c->qid.type & QTDIR)
514                 error(Eperm);
515
516         p = uart[NETID(c->qid.path)];
517
518         switch(NETTYPE(c->qid.path)){
519         case Ndataqid:
520                 qlock(p);
521                 if(waserror()){
522                         qunlock(p);
523                         nexterror();
524                 }
525
526                 n = qwrite(p->oq, buf, n);
527
528                 qunlock(p);
529                 poperror();
530                 break;
531         case Nctlqid:
532                 cmd = smalloc(n+1);
533                 memmove(cmd, buf, n);
534                 cmd[n] = 0;
535                 qlock(p);
536                 if(waserror()){
537                         qunlock(p);
538                         free(cmd);
539                         nexterror();
540                 }
541
542                 /* let output drain */
543                 if(uartctl(p, cmd) < 0)
544                         error(Ebadarg);
545
546                 qunlock(p);
547                 poperror();
548                 free(cmd);
549                 break;
550         }
551
552         return n;
553 }
554
555 static int
556 uartwstat(Chan *c, uchar *dp, int n)
557 {
558         Dir d;
559         Dirtab *dt;
560
561         if(!iseve())
562                 error(Eperm);
563         if(QTDIR & c->qid.type)
564                 error(Eperm);
565         if(NETTYPE(c->qid.path) == Nstatqid)
566                 error(Eperm);
567
568         dt = &uartdir[1 + 3 * NETID(c->qid.path)];
569         n = convM2D(dp, n, &d, nil);
570         if(n == 0)
571                 error(Eshortstat);
572         if(d.mode != ~0UL)
573                 dt[0].perm = dt[1].perm = d.mode;
574         return n;
575 }
576
577 void
578 uartpower(int on)
579 {
580         Uart *p;
581
582         for(p = uartlist; p != nil; p = p->next) {
583                 if(p->phys->power)
584                         (*p->phys->power)(p, on);
585         }
586 }
587
588 Dev uartdevtab = {
589         't',
590         "uart",
591
592         uartreset,
593         devinit,
594         devshutdown,
595         uartattach,
596         uartwalk,
597         uartstat,
598         uartopen,
599         devcreate,
600         uartclose,
601         uartread,
602         devbread,
603         uartwrite,
604         devbwrite,
605         devremove,
606         uartwstat,
607         uartpower,
608 };
609
610 /*
611  *  restart input if it's off
612  */
613 static void
614 uartflow(void *v)
615 {
616         Uart *p;
617
618         p = v;
619         if(p->modem)
620                 (*p->phys->rts)(p, 1);
621 }
622
623 /*
624  *  put some bytes into the local queue to avoid calling
625  *  qconsume for every character
626  */
627 int
628 uartstageoutput(Uart *p)
629 {
630         int n;
631
632         n = qconsume(p->oq, p->ostage, Stagesize);
633         if(n <= 0)
634                 return 0;
635         p->op = p->ostage;
636         p->oe = p->ostage + n;
637         return n;
638 }
639
640 /*
641  *  restart output
642  */
643 void
644 uartkick(void *v)
645 {
646         Uart *p = v;
647
648         if(p->blocked)
649                 return;
650
651         ilock(&p->tlock);
652         (*p->phys->kick)(p);
653         iunlock(&p->tlock);
654
655         if(p->drain && uartdrained(p)){
656                 p->drain = 0;
657                 wakeup(&p->r);
658         }
659 }
660
661 /*
662  * Move data from the interrupt staging area to
663  * the input Queue.
664  */
665 static void
666 uartstageinput(Uart *p)
667 {
668         int n;
669         uchar *ir, *iw;
670
671         while(p->ir != p->iw){
672                 ir = p->ir;
673                 if(p->ir > p->iw){
674                         iw = p->ie;
675                         p->ir = p->istage;
676                 }
677                 else{
678                         iw = p->iw;
679                         p->ir = p->iw;
680                 }
681                 if((n = qproduce(p->iq, ir, iw - ir)) < 0){
682                         p->serr++;
683                         (*p->phys->rts)(p, 0);
684                 }
685                 else if(n == 0)
686                         p->berr++;
687         }
688 }
689
690 /*
691  *  receive a character at interrupt time
692  */
693 void
694 uartrecv(Uart *p,  char ch)
695 {
696         uchar *next;
697
698         /* software flow control */
699         if(p->xonoff){
700                 if(ch == CTLS){
701                         p->blocked = 1;
702                 }else if(ch == CTLQ){
703                         p->blocked = 0;
704                         p->ctsbackoff = 2; /* clock gets output going again */
705                 }
706         }
707
708         /* receive the character */
709         if(p->putc)
710                 p->putc(p->iq, ch);
711         else if (p->iw) {               /* maybe the line isn't enabled yet */
712                 ilock(&p->rlock);
713                 next = p->iw + 1;
714                 if(next == p->ie)
715                         next = p->istage;
716                 if(next == p->ir)
717                         uartstageinput(p);
718                 if(next != p->ir){
719                         *p->iw = ch;
720                         p->iw = next;
721                 }
722                 iunlock(&p->rlock);
723         }
724 }
725
726 /*
727  *  we save up input characters till clock time to reduce
728  *  per character interrupt overhead.
729  */
730 static void
731 uartclock(void)
732 {
733         Uart *p;
734
735         ilock(&uartalloc);
736         for(p = uartalloc.elist; p; p = p->elist){
737
738                 /* this hopefully amortizes cost of qproduce to many chars */
739                 if(p->iw != p->ir){
740                         ilock(&p->rlock);
741                         uartstageinput(p);
742                         iunlock(&p->rlock);
743                 }
744
745                 /* hang up if requested */
746                 if(p->dohup){
747                         qhangup(p->iq, 0);
748                         qhangup(p->oq, 0);
749                         p->dohup = 0;
750                 }
751
752                 /* this adds hysteresis to hardware/software flow control */
753                 if(p->ctsbackoff){
754                         ilock(&p->tlock);
755                         if(p->ctsbackoff){
756                                 if(--(p->ctsbackoff) == 0)
757                                         (*p->phys->kick)(p);
758                         }
759                         iunlock(&p->tlock);
760                 }
761         }
762         iunlock(&uartalloc);
763 }
764
765 /*
766  * polling console input, output
767  */
768
769 Uart* consuart;
770
771 int
772 uartgetc(void)
773 {
774         if(consuart == nil || consuart->phys->getc == nil)
775                 return -1;
776         return consuart->phys->getc(consuart);
777 }
778
779 void
780 uartputc(int c)
781 {
782         if(consuart == nil || consuart->phys->putc == nil)
783                 return;
784         consuart->phys->putc(consuart, c);
785 }
786
787 void
788 uartputs(char *s, int n)
789 {
790         char *e;
791
792         if(consuart == nil || consuart->phys->putc == nil)
793                 return;
794
795         e = s+n;
796         for(; s<e; s++){
797                 if(*s == '\n')
798                         consuart->phys->putc(consuart, '\r');
799                 consuart->phys->putc(consuart, *s);
800         }
801 }