]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/vmx/io.c
merge
[plan9front.git] / sys / src / cmd / vmx / io.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <draw.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include "dat.h"
8 #include "fns.h"
9
10 uchar cmos[0x30] = {
11         [1] 0xff, [3] 0xff, [5] 0xff, 
12         [0xa] 0x26,
13         [0xb] 1<<1,
14         [0xd] 1<<7, /* cmos valid */
15         [0xf] 0x56, /* cmos tests pass */
16         [0x11] 0x80, /* mouse enabled */
17         [0x14] 0x2e, /* cga 80-column */
18         [0x2d] 0x1c, /* caches + fpu enabled */
19 };
20 static vlong rtcnext = -1;
21
22 static uchar
23 bcd(uchar m, uchar c)
24 {
25         return (m & 1) != 0 ? c : c / 10 << 4 | c % 10;
26 }
27
28 void
29 rtcadvance(void)
30 {
31         vlong t;
32         
33         if(rtcnext != -1){
34                 t = nsec();
35                 if(t >= rtcnext){
36                         cmos[0xc] |= 0x40;
37                         rtcnext = -1;
38                 }else
39                         settimer(rtcnext);
40         }
41         irqline(8, (cmos[0xc] & cmos[0xb] & 0x70) != 0); 
42 }
43
44 static void
45 rtcset(void)
46 {
47         vlong t, b;
48         int d;
49
50         rtcadvance();
51         if((cmos[0xa] >> 4) > 2 || (cmos[0xa] & 15) == 0 || (cmos[0xc] & 0x40) != 0){
52                 rtcnext = -1;
53                 return;
54         }
55         switch(cmos[0xa]){
56         case 0x21: d = 12; break;
57         case 0x22: d = 13; break;
58         default: d = 4 + (cmos[0xa] & 0xf);
59         }
60         b = (1000000000ULL << d) / 1048576;
61         t = nsec();
62         rtcnext = t + b - t % b;
63         settimer(rtcnext);
64 }
65
66 static u32int
67 rtcio(int isin, u16int port, u32int val, int sz, void *)
68 {
69         static u8int addr;
70         uintptr basemem, extmem;
71
72         static int cmosinit;
73         int i, s;
74         Tm *tm;
75         
76         if(cmosinit == 0){
77                 basemem = gavail(gptr(0, 0)) >> 10;
78                 if(basemem > 640) basemem = 640;
79                 extmem = gavail(gptr(1<<20, 0)) >> 10;
80                 if(extmem >= 65535) extmem = 65535;
81                 cmos[0x15] = basemem;
82                 cmos[0x16] = basemem >> 8;
83                 cmos[0x17] = extmem;
84                 cmos[0x18] = extmem >> 8;
85                 s = 0;
86                 for(i = 0x10; i < 0x2e; i++)
87                         s += cmos[i];
88                 cmos[0x2e] = s >> 8;
89                 cmos[0x2f] = s;
90                 cmosinit = 1;
91         }
92         if(sz != 1) vmerror("rtc: access size %d != 1", sz);
93         val = (u8int) val;
94         switch(isin << 16 | port){
95         case 0x70: addr = val; return 0;
96         case 0x71:
97                 switch(addr){
98                 case 0xa: cmos[addr] = val & 0x7f; rtcset(); break;
99                 case 0xb: cmos[addr] = val | 2; rtcadvance(); break;
100                 case 0xc: case 0xd: goto no;
101                 default:
102                         if(addr < nelem(cmos))
103                                 cmos[addr] = val;
104                         else no:
105                                 vmerror("rtc: write to unknown address %#x (val=%#x)", addr, val);
106                         return 0;
107                 }
108         case 0x10070: return addr;
109         case 0x10071:
110                 tm = gmtime(time(nil));
111                 switch(addr){
112                 case 0x00: return bcd(cmos[0xb], tm->sec);
113                 case 0x02: return bcd(cmos[0xb], tm->min);
114                 case 0x04: return bcd(cmos[0xb], tm->hour);
115                 case 0x06: return bcd(cmos[0xb], tm->wday + 1);
116                 case 0x07: return bcd(cmos[0xb], tm->mday);
117                 case 0x08: return bcd(cmos[0xb], tm->mon + 1);
118                 case 0x09: return bcd(cmos[0xb], tm->year % 100);
119                 case 0x0c:
120                         i = cmos[0xc] | ((cmos[0xc] & cmos[0xb] & 0x70) != 0) << 7;
121                         cmos[0xc] = 0;
122                         rtcset();
123                         return i;
124                 case 0x32: return bcd(cmos[0xb], tm->year / 100 + 19);
125                 default:
126                         if(addr < nelem(cmos))
127                                 return cmos[addr];
128                         vmerror("rtc: read from unknown address %#x", addr);
129                         return 0;
130                 }
131         }
132         return iowhine(isin, port, val, sz, "rtc");
133 }
134
135 typedef struct Pic Pic;
136 struct Pic {
137         enum {
138                 AEOI = 1,
139                 ROTAEOI = 2,
140                 MASKMODE = 4,
141                 POLL = 8,
142                 READSR = 16,
143         } flags;
144         u8int lines;
145         u8int irr, isr;
146         u8int imr;
147         u8int elcr;
148         u8int init;
149         u8int prio;
150         u8int base;
151 } pic[2];
152 int irqactive = -1;
153
154 static u8int
155 picprio(u8int v, u8int p, u8int *n)
156 {
157         p++;
158         v = v >> p | v << 8 - p;
159         v &= -v;
160         v = v << p | v >> 8 - p;
161         if(n != nil)
162                 *n = ((v & 0xf0) != 0) << 2 | ((v & 0xcc) != 0) << 1 | (v & 0xaa) != 0;
163         return v;
164 }
165
166 static u8int
167 piccheck(Pic *p, u8int *n)
168 {
169         u8int s;
170         
171         s = p->isr;
172         if((p->flags & MASKMODE) != 0 && p->imr != 0)
173                 s = 0;
174         return picprio(p->irr & ~p->imr | s, p->prio, n) & ~s;
175 }
176
177 static void
178 picaeoi(Pic *p, u8int b)
179 {
180         if((p->flags & AEOI) == 0)
181                 return;
182         p->isr &= ~(1<<b);
183         if((p->flags & ROTAEOI) != 0)
184                 p->prio = b;
185 }
186
187 static void
188 picupdate(Pic *p)
189 {
190         u8int m, n;
191         
192         if(p->init != 4) return;
193         m = piccheck(p, &n);
194         if(p == &pic[1])
195                 irqline(2, m != 0);
196         else{
197                 if(m != 0 && n == 2){
198                         m = piccheck(&pic[1], &n);
199                         n |= pic[1].base;
200                 }else
201                         n |= p->base;
202                 if(m != 0 && irqactive != n){
203                         if(ctl("irq %d", n) < 0)
204                                 sysfatal("ctl: %r");
205                         if(state == VMHALT)
206                                 state = VMRUNNING;
207                         irqactive = n;
208                 }else if(m == 0 && irqactive >= 0){
209                         if(ctl("irq") < 0)
210                                 sysfatal("ctl: %r");
211                         irqactive = -1;
212                 }
213         }
214 }
215
216 void
217 irqline(int n, int s)
218 {
219         Pic *p;
220         u8int ol, m;
221         
222         assert(n >= 0 && n <= 15);
223         p = &pic[n / 8];
224         n %= 8;
225         ol = p->lines;
226         m = 1<<n;
227         switch(s){
228         case 1: case IRQLLOHI: p->lines |= m; break;
229         case 0: p->lines &= ~m; break;
230         case IRQLTOGGLE: p->lines ^= m; break;
231         default: assert(0);
232         }
233         if((p->elcr & m) != 0)
234                 p->irr = p->irr & ~m | ~p->lines & m;
235         else
236                 p->irr |= p->lines & ~ol & m;
237         if(s == IRQLLOHI && (p->elcr & m) == 0)
238                 p->irr |= m;
239         picupdate(p);
240 }
241
242 void
243 irqack(int n)
244 {
245         Pic *p;
246         extern int nextexit;
247         
248         irqactive = -1;
249         if((n & ~7) == pic[0].base)
250                 p = &pic[0];
251         else if((n & ~7) == pic[1].base)
252                 p = &pic[1];
253         else
254                 return;
255         if(p == &pic[1]){
256                 irqack(pic[0].base + 2);
257                 irqline(2, 0);
258         }
259         n &= 7;
260         p->irr &= ~(1<<n);
261         p->isr |= 1<<n;
262         picaeoi(p, n);
263         picupdate(p);
264 }
265
266 void
267 elcr(u16int a)
268 {
269         pic[0].elcr = a;
270         pic[1].elcr = a >> 8;
271 }
272
273 static u32int
274 picio(int isin, u16int port, u32int val, int sz, void *)
275 {
276         Pic *p;
277         u8int m, b;
278         
279         p = &pic[(port & 0x80) != 0];
280         val = (u8int)val;
281         switch(isin << 16 | port){
282         case 0x20:
283         case 0xa0:
284                 if((val & 1<<4) != 0){ /* ICW1 */
285                         if(irqactive >= 0){
286                                 if(ctl("irq") < 0)
287                                         sysfatal("ctl: %r");
288                                 irqactive = -1;
289                         }
290                         p->irr = 0;
291                         p->isr = 0;
292                         p->imr = 0;
293                         p->prio = 7;
294                         p->flags = 0;
295                         if((val & 0x0b) != 0x01) vmerror("PIC%ld ICW1 with unsupported value %#ux", p-pic, val);
296                         p->init = 1;
297                         return 0;
298                 }
299                 if((val & 0x18) == 0){ /* OCW2 */
300                         switch(val >> 5){
301                         case 0: /* rotate in automatic eoi mode (clear) */
302                                 p->flags &= ~ROTAEOI;
303                                 break;
304                         case 1: /* non-specific eoi command */
305                                 p->isr &= ~picprio(p->isr, p->prio, nil);
306                                 break;
307                         case 2: /* no operation */
308                                 break;
309                         case 3: /* specific eoi command */
310                                 p->isr &= ~(1<<(val & 7));
311                                 break;
312                         case 4: /* rotate in automatic eoi mode (set) */
313                                 p->flags |= ROTAEOI;
314                                 break;
315                         case 5: /* rotate on non-specific eoi command */
316                                 p->isr &= ~picprio(p->isr, p->prio, &p->prio);
317                                 break;
318                         case 6: /* set priority */
319                                 p->prio = val & 7;
320                                 break;
321                         case 7: /* rotate on specific eoi command */
322                                 p->isr &= ~(1<<(val & 7));
323                                 p->prio = val & 7;
324                                 break;
325                         }
326                         picupdate(p);
327                         return 0;
328                 }
329                 if((val & 0x98) == 8){ /* OCW3 */
330                         if((val & 0x40) != 0)
331                                 if((val & 0x20) != 0)
332                                         p->flags |= MASKMODE;
333                                 else
334                                         p->flags &= ~MASKMODE;
335                         if((val & 4) != 0)
336                                 p->flags |= POLL;
337                         if((val & 2) != 0)
338                                 if((val & 10) != 0)
339                                         p->flags |= READSR;
340                                 else
341                                         p->flags &= ~READSR;
342                         picupdate(p);
343                         
344                 }
345                 return 0;
346         case 0x21:
347         case 0xa1:
348                 switch(p->init){
349                 default:
350                         vmerror("write to PIC%ld in init=%d state", p-pic, p->init);
351                         return 0;
352                 case 1:
353                         p->base = val;
354                         p->init = 2;
355                         return 0;
356                 case 2:
357                         if(p == &pic[0] && val != 4 || p == &pic[1] && val != 2)
358                                 vmerror("PIC%ld ICW3 with unsupported value %#ux", p-pic, val);
359                         p->init = 3;
360                         return 0;
361                 case 3:
362                         if((val & 0xfd) != 1) vmerror("PIC%ld ICW4 with unsupported value %#ux", p-pic, val);
363                         if((val & 2) != 0) p->flags |= AEOI;
364                         p->init = 4;
365                         picupdate(p);
366                         return 0;
367                 case 0:
368                 case 4:
369                         p->imr = val;
370                         picupdate(p); 
371                         return 0;
372                 }
373                 break;
374         case 0x10020:
375         case 0x100a0:
376                 if((p->flags & READSR) != 0)
377                         return p->isr;
378                 if((p->flags & POLL) != 0){
379                         p->flags &= ~POLL;
380                         m = piccheck(p, &b);
381                         if(m != 0){
382                                 p->irr &= ~m;
383                                 p->isr |= m;
384                                 picaeoi(p, b);
385                                 picupdate(p);
386                                 return 1<<7 | b;
387                         }
388                         return 0;
389                 }
390                 return p->irr;
391         case 0x10021:
392         case 0x100a1:
393                 return p->imr;
394         case 0x4d0:
395         case 0x4d1:
396                 pic[port & 1].elcr = val;
397                 return 0;
398         case 0x104d0:
399         case 0x104d1:
400                 return pic[port & 1].elcr;
401         }
402         return iowhine(isin, port, val, sz, "pic");
403 }
404
405 typedef struct PITChannel PITChannel;
406
407 struct PITChannel {
408         u8int mode;
409         u8int bcd;
410         u8int access;
411         u8int state;
412         u16int count, reload;
413         int latch;
414         enum { READLO, READHI, READLATLO, READLATHI } readstate;
415         u8int writestate;
416         vlong lastnsec;
417         u8int output;
418 };
419 PITChannel pit[3] = {
420         [0] { .state 1 },
421 };
422 u8int port61;
423 enum { PERIOD = 838 };
424
425 void
426 settimer(vlong targ)
427 {
428         extern vlong timerevent;
429         extern Lock timerlock;
430         extern int timerid;
431         int sendint;
432
433         sendint = 0;
434         lock(&timerlock);
435         if(targ < timerevent){
436                 timerevent = targ;
437                 sendint = 1;
438         }
439         unlock(&timerlock);
440         if(sendint)
441                 threadint(timerid);
442 }
443
444 static void
445 pitout(int n, int v)
446 {
447         if(n == 0)
448                 irqline(0, v);
449         switch(v){
450         case IRQLLOHI: case 1: pit[n].output = 1; break;
451         case 0: pit[n].output = 0; break;
452         case IRQLTOGGLE: pit[n].output ^= 1; break;
453         }
454 }
455
456 void
457 pitadvance(void)
458 {
459         int i;
460         int nc;
461         PITChannel *p;
462         vlong nt, t;
463         int rel;
464
465         for(i = 0; i < 3; i++){
466                 p = &pit[i];
467                 nt = nsec();
468                 t = nt - p->lastnsec;
469                 p->lastnsec = nt;
470                 switch(p->mode){
471                 case 0:
472                         if(p->state != 0){
473                                 nc = t / PERIOD;
474                                 if(p->count <= nc)
475                                         pitout(i, 1);
476                                 p->count -= nc;
477                                 p->lastnsec -= t % PERIOD;
478                                 if(!p->output)
479                                         settimer(p->lastnsec + p->count * PERIOD);
480                         }
481                         break;
482                 case 2:
483                         if(p->state != 0){
484                                 nc = t / PERIOD;
485                                 if(p->count == 0 || p->count - 1 > nc)
486                                         p->count -= nc;
487                                 else{
488                                         rel = p->reload - 1;
489                                         if(rel <= 0) rel = 65535;
490                                         nc -= p->count - 1;
491                                         nc %= rel;
492                                         p->count = rel - nc + 1;
493                                         pitout(i, IRQLLOHI);
494                                 }
495                                 p->lastnsec -= t % PERIOD;
496                                 settimer(p->lastnsec + p->count * PERIOD);
497                         }
498                         break;
499                 case 3:
500                         if(p->state != 0){
501                                 nc = 2 * (t / PERIOD);
502                                 if(p->count > nc)
503                                         p->count -= nc;
504                                 else{
505                                         rel = p->reload;
506                                         if(rel <= 1) rel = 65536;
507                                         nc -= p->count;
508                                         nc %= rel;
509                                         p->count = rel - nc;
510                                         pitout(i, IRQLTOGGLE);
511                                 }
512                                 p->lastnsec -= t % PERIOD;
513                                 settimer(p->lastnsec + p->count / 2 * PERIOD);
514                         }
515                         break;
516                 }
517         }
518 }
519
520 static void
521 pitsetreload(int n, int hi, u8int v)
522 {
523         PITChannel *p;
524         
525         p = &pit[n];
526         if(hi)
527                 p->reload = p->reload >> 8 | v << 8;
528         else
529                 p->reload = p->reload & 0xff00 | v;
530         switch(p->mode){
531         case 0:
532                 pitout(n, 0);
533                 if(p->access != 3 || hi){
534                         p->count = p->reload;
535                         p->state = 1;
536                         p->lastnsec = nsec();
537                         settimer(p->lastnsec + p->count * PERIOD);
538                 }else
539                         p->state = 0;
540                 break;
541         case 2:
542         case 3:
543                 if(p->state == 0 && (p->access != 3 || hi)){
544                         p->count = p->reload;
545                         p->state = 1;
546                         p->lastnsec = nsec();
547                         pitadvance();
548                 }
549                 break;
550         default:
551                 vmerror("PIT reload in mode %d not implemented", p->mode);
552                 break;
553         }       
554 }
555
556 static u32int
557 pitio(int isin, u16int port, u32int val, int sz, void *)
558 {
559         int n;
560
561         val = (u8int) val;
562         pitadvance();
563         switch(isin << 16 | port){
564         case 0x10040:
565         case 0x10041:
566         case 0x10042:
567                 n = port & 3;
568                 switch(pit[n].readstate){
569                 case READLO:
570                         if(pit[n].access == 3)
571                                 pit[n].readstate = READHI;
572                         return pit[n].count;
573                 case READHI:
574                         if(pit[n].access == 3)
575                                 pit[n].readstate = READLO;
576                         return pit[n].count >> 8;
577                 case READLATLO:
578                         pit[n].readstate = READLATHI;
579                         return pit[n].latch;
580                 case READLATHI:
581                         pit[n].readstate = pit[n].access == 1 ? READHI : READLO;
582                         return pit[n].latch >> 8;
583                 }
584                 return 0;
585         case 0x10061: return port61 | pit[2].output << 5;
586         case 0x40:
587         case 0x41:
588         case 0x42:
589                 n = port & 3;
590                 switch(pit[n].writestate){
591                 case READLO:
592                         if(pit[n].access == 3)
593                                 pit[n].writestate = READHI;
594                         pitsetreload(n, 0, val);
595                         break;
596                 case READHI:
597                         if(pit[n].access == 3)
598                                 pit[n].writestate = READLO;
599                         pitsetreload(n, 1, val);
600                         break;
601                 }
602                 return 0;
603         case 0x43:
604                 n = val >> 6;
605                 if(n == 3) return 0;
606                 if((val & ~0xc0) == 0){
607                         pit[n].latch = pit[n].count;
608                         pit[n].readstate = READLATLO;
609                 }else{
610                         pit[n].mode = val >> 1 & 7;
611                         pit[n].access = val >> 4 & 3;
612                         pit[n].bcd = val & 1;
613                         if(pit[n].bcd != 0)
614                                 vmerror("pit: bcd mode not implemented");
615                         switch(pit[n].mode){
616                         case 0: case 2: case 3: break;
617                         default: vmerror("pit: mode %d not implemented", pit[n].mode);
618                         }
619                         pit[n].state = 0;
620                         pit[n].count = 0;
621                         pit[n].reload = 0;
622                         pit[n].readstate = pit[n].access == 1 ? READHI : READLO;
623                         pit[n].writestate = pit[n].access == 1 ? READHI : READLO;
624                         pit[n].lastnsec = nsec();
625                         switch(pit[n].mode){
626                         case 0:
627                                 pitout(n, 0);
628                                 break;
629                         default:
630                                 pitout(n, 1);
631                         }
632                 }
633                 return 0;
634         case 0x61:
635                 port61 = port61 & 0xf0 | val & 0x0f;
636                 return 0;
637         }
638         return iowhine(isin, port, val, sz, "pit");
639 }
640
641 typedef struct I8042 I8042;
642 struct I8042 {
643         u8int cfg, stat, oport;
644         int cmd;
645         u16int buf; /* |0x100 == kbd, |0x200 == mouse, |0x400 == cmd */
646 } i8042 = {
647         .cfg 0x74,
648         .stat 0x10,
649         .oport 0x01,
650         .cmd -1,
651 };
652 Channel *kbdch, *mousech;
653 u8int mouseactive;
654 typedef struct PCKeyb PCKeyb;
655 struct PCKeyb {
656         u8int buf[64];
657         u8int bufr, bufw;
658         u8int actcmd;
659         u8int quiet;
660 } kbd;
661 typedef struct PCMouse PCMouse;
662 struct PCMouse {
663         Mouse;
664         u8int gotmouse;
665         enum {
666                 MOUSERESET,
667                 MOUSESTREAM,
668                 MOUSEREMOTE,
669                 MOUSEREP = 0x10,
670                 MOUSEWRAP = 0x20,
671         } state;
672         u8int buf[64];
673         u8int bufr, bufw;
674         u8int actcmd;
675         u8int scaling21, res, rate;
676 } mouse = {
677         .res = 2,
678         .rate = 100
679 };
680 #define keyputc(c) kbd.buf[kbd.bufw++ & 63] = (c)
681 #define mouseputc(c) mouse.buf[mouse.bufw++ & 63] = (c)
682
683 static void
684 i8042putbuf(u16int val)
685 {
686         i8042.buf = val;
687         i8042.stat = i8042.stat & ~0x20 | val >> 4 & 0x20;
688         if((i8042.cfg & 1) != 0 && (val & 0x100) != 0){
689                 irqline(1, 1);
690                 i8042.oport |= 0x10;
691         }
692         if((i8042.cfg & 2) != 0 && (val & 0x200) != 0){
693                 irqline(12, 1);
694                 i8042.oport |= 0x20;
695         }
696         if(val == 0){
697                 irqline(1, 0);
698                 irqline(12, 0);
699                 i8042.oport &= ~0x30;
700                 i8042.stat &= ~1;
701                 i8042kick(nil);
702         }else
703                 i8042.stat |= 1;
704 }
705
706 static void
707 kbdcmd(u8int val)
708 {
709         switch(kbd.actcmd){
710         case 0xf0: /* set scancode set */
711                 keyputc(0xfa);
712                 if(val == 0) keyputc(1);
713                 kbd.actcmd = 0;
714                 break;
715         case 0xed: /* set leds */
716                 keyputc(0xfa);
717                 kbd.actcmd = 0;
718                 break;
719         default:
720                 switch(val){
721                 case 0xed: case 0xf0: kbd.actcmd = val; keyputc(0xfa); break;
722
723                 case 0xff: keyputc(0xfa); keyputc(0xaa); break; /* reset */
724                 case 0xf5: kbd.quiet = 1; keyputc(0xfa); break; /* disable scanning */
725                 case 0xf4: kbd.quiet = 0; keyputc(0xfa); break; /* enable scanning */
726                 case 0xf2: keyputc(0xfa); keyputc(0xab); keyputc(0x41); break; /* keyboard id */
727                 case 0xee: keyputc(0xee); break; /* echo */
728                 default:
729                         vmerror("unknown kbd command %#ux", val);
730                         keyputc(0xfe);
731                 }
732         }
733         i8042kick(nil);
734 }
735
736 static void
737 updatemouse(void)
738 {
739         Mouse m;
740         
741         while(nbrecv(mousech, &m) > 0){
742                 mouse.xy = addpt(mouse.xy, m.xy);
743                 mouse.buttons = m.buttons;
744                 mouse.gotmouse = 1;
745         }
746 }
747
748 static void
749 clearmouse(void)
750 {
751         updatemouse();
752         mouse.xy = Pt(0, 0);
753         mouse.gotmouse = 0;
754 }
755
756 static void
757 mousepacket(int force)
758 {
759         int dx, dy;
760         u8int b0;
761
762         updatemouse();
763         if(!mouse.gotmouse && !force)
764                 return;
765         dx = mouse.xy.x;
766         dy = -mouse.xy.y;
767         b0 = 8;
768         if((ulong)(dx + 256) > 511) dx = dx >> 31 ^ 0xff;
769         if((ulong)(dy + 256) > 511) dy = dy >> 31 ^ 0xff;
770         b0 |= dx >> 5 & 0x10 | dy >> 4 & 0x20;
771         b0 |= (mouse.buttons * 0x111 & 0x421) % 7;
772         mouseputc(b0);
773         mouseputc((u8int)dx);
774         mouseputc((u8int)dy);
775         mouse.xy.x -= dx;
776         mouse.xy.y += dy;
777         mouse.gotmouse = mouse.xy.x != 0 || mouse.xy.y != 0;
778 }
779
780 static void
781 mousedefaults(void)
782 {
783         clearmouse();
784         mouse.res = 2;
785         mouse.rate = 100;
786 }
787
788 static void
789 mousecmd(u8int val)
790 {
791         if((mouse.state & MOUSEWRAP) != 0 && val != 0xec && val != 0xff){
792                 mouseputc(val);
793                 i8042kick(nil);
794                 return;
795         }
796         switch(mouse.actcmd){
797         case 0xe8: /* set resolution */
798                 mouse.res = val;
799                 mouseputc(0xfa);
800                 mouse.actcmd = 0;
801                 break;
802         case 0xf3: /* set sampling rate */
803                 mouse.rate = val;
804                 mouseputc(0xfa);
805                 mouse.actcmd = 0;
806                 break;
807         default:
808                 switch(val){
809                 case 0xf3: case 0xe8: mouseputc(0xfa); mouse.actcmd = val; break;
810                 
811                 case 0xff: mouseputc(0xfa); mousedefaults(); mouse.state = MOUSERESET; break; /* reset */
812                 case 0xf6: mouseputc(0xfa); mousedefaults(); mouse.state = mouse.state & ~0xf | MOUSESTREAM; break; /* set defaults */
813                 case 0xf5: mouseputc(0xfa); clearmouse(); if((mouse.state&0xf) == MOUSESTREAM) mouse.state &= ~MOUSEREP; break; /* disable reporting */
814                 case 0xf4: mouseputc(0xfa); clearmouse(); if((mouse.state&0xf) == MOUSESTREAM) mouse.state |= MOUSEREP; break; /* enable reporting */
815                 case 0xf2: mouseputc(0xfa); mouseputc(0x00); clearmouse(); break; /* report device id */
816                 case 0xf0: mouseputc(0xfa); clearmouse(); mouse.state = mouse.state & ~0xf | MOUSEREMOTE; break; /* set remote mode */
817                 case 0xee: mouseputc(0xfa); clearmouse(); mouse.state |= MOUSEWRAP; break; /* set wrap mode */
818                 case 0xec: mouseputc(0xfa); clearmouse(); mouse.state &= ~MOUSEWRAP; break; /* reset wrap mode */
819                 case 0xeb: mouseputc(0xfa); mousepacket(1); break; /* read data */
820                 case 0xea: mouseputc(0xfa); clearmouse(); mouse.state = mouse.state & ~0xf | MOUSESTREAM; break; /* set stream mode */
821                 case 0xe9: /* status request */
822                         mouseputc(0xfa);
823                         mouseputc(((mouse.state & 0xf) == MOUSEREMOTE) << 6 | ((mouse.state & MOUSEREP) != 0) << 5 | mouse.scaling21 << 4 | (mouse.buttons * 0x111 & 0x142) % 7);
824                         mouseputc(mouse.res);
825                         mouseputc(mouse.rate);
826                         break;
827                 case 0xe7: mouseputc(0xfa); mouse.scaling21 = 1; break; /* set 2:1 scaling */
828                 case 0xe6: mouseputc(0xfa); mouse.scaling21 = 0; break; /* set 1:1 scaling */
829                 
830                 case 0x88: case 0x00: case 0x0a: /* sentelic & cypress */
831                 case 0xe1: /* trackpoint */
832                         mouseputc(0xfe);
833                         break;
834
835                 default: vmerror("unknown mouse command %#ux", val); mouseputc(0xfe);
836                 }
837         }
838         i8042kick(nil);
839 }
840
841 static void
842 mousekick(void)
843 {       
844         switch(mouse.state){
845         case MOUSERESET:
846                 mouseputc(0xaa);
847                 mouseputc(0);
848                 mouse.state = MOUSESTREAM;
849                 break;
850         case MOUSESTREAM | MOUSEREP:
851                 if(mouse.actcmd == 0)
852                         mousepacket(0);
853                 break;
854         }
855 }
856
857
858 void
859 i8042kick(void *)
860 {
861         ulong ch;
862         
863         if((i8042.cfg & 0x10) == 0 && i8042.buf == 0)
864                 if(kbd.bufr != kbd.bufw)
865                         i8042putbuf(0x100 | kbd.buf[kbd.bufr++ & 63]);
866                 else if(!kbd.quiet && nbrecv(kbdch, &ch) > 0)
867                         i8042putbuf(0x100 | (u8int)ch);
868         if((i8042.cfg & 0x20) == 0 && i8042.buf == 0){
869                 if(mouse.bufr == mouse.bufw)
870                         mousekick();
871                 if(mouse.bufr != mouse.bufw)
872                         i8042putbuf(0x200 | mouse.buf[mouse.bufr++ & 63]);
873         }
874 }
875
876 static u32int
877 i8042io(int isin, u16int port, u32int val, int sz, void *)
878 {
879         int rc;
880
881         val = (u8int)val;
882         switch(isin << 16 | port){
883         case 0x60:
884                 i8042.stat &= ~8;
885                 switch(i8042.cmd){
886                 case 0x60: i8042.cfg = val; mouseactive = (val & 0x20) == 0; break;
887                 case 0xd1:
888                         i8042.oport = val;
889                         irqline(1, i8042.oport >> 4 & 1);
890                         irqline(12, i8042.oport >> 5 & 1);
891                         break;
892                 case 0xd2: i8042putbuf(0x100 | val); break;
893                 case 0xd3: i8042putbuf(0x200 | val); break;
894                 case 0xd4: mousecmd(val); break;
895                 case -1: kbdcmd(val); break;
896                 }
897                 i8042.cmd = -1;
898                 return 0;
899         case 0x10060:
900                 i8042kick(nil);
901                 rc = i8042.buf;
902                 i8042putbuf(0);
903                 return rc;
904         case 0x64:
905                 i8042.stat |= 8;
906                 switch(val){
907                 case 0x20: i8042putbuf(0x400 | i8042.cfg); return 0;
908                 case 0xa1: i8042putbuf(0x4f1); return 0; /* no keyboard password */
909                 case 0xa7: i8042.cfg |= 1<<5; mouseactive = 0; return 0;
910                 case 0xa8: i8042.cfg &= ~(1<<5); mouseactive = 1; return 0;
911                 case 0xa9: i8042putbuf(0x400); return 0; /* test second port */
912                 case 0xaa: i8042putbuf(0x455); return 0; /* test controller */
913                 case 0xab: i8042putbuf(0x400); return 0; /* test first port */
914                 case 0xad: i8042.cfg |= 1<<4; return 0;
915                 case 0xae: i8042.cfg &= ~(1<<4); return 0;
916                 case 0xd0: i8042putbuf(0x400 | i8042.oport); return 0;
917                 case 0x60: case 0xd1: case 0xd2: case 0xd3: case 0xd4:
918                         i8042.cmd = val;
919                         return 0;
920                 case 0xf0: case 0xf2: case 0xf4: case 0xf6: /* pulse reset line */
921                 case 0xf8: case 0xfa: case 0xfc: case 0xfe:
922                         sysfatal("i8042: system reset");
923                 case 0xf1: case 0xf3: case 0xf5: case 0xf7: /* no-op */
924                 case 0xf9: case 0xfb: case 0xfd: case 0xff:
925                         return 0;
926                 }
927                 vmerror("unknown i8042 command %#ux", val);
928                 return 0;
929         case 0x10064:
930                 i8042kick(nil);
931                 return i8042.stat | i8042.cfg & 4;
932         }
933         return iowhine(isin, port, val, sz, "i8042");
934 }
935
936 typedef struct UART UART;
937 struct UART {
938         u8int ier, fcr, lcr, lsr, mcr, scr, dll, dlh;
939         u8int rbr, tbr;
940         enum {
941                 UARTTXIRQ = 2,
942                 UARTRXIRQ = 1,
943         } irq;
944         int infd, outfd;
945         Channel *inch, *outch;
946 } uart[2] = { { .lsr = 0x60 }, { .lsr = 0x60 } };
947
948 static void
949 uartkick(UART *p)
950 {
951         char c;
952
953         irqline(4 - (p - uart), (p->irq & p->ier) != 0);
954         if((p->irq & UARTRXIRQ) == 0 && p->inch != nil && nbrecv(p->inch, &c) > 0){
955                 p->rbr = c;
956                 p->irq |= UARTRXIRQ;
957         }
958         if((p->lsr & 1<<5) == 0){
959                 if(p->outch == nil){
960                         p->lsr |= 3<<5;
961                         p->irq |= UARTTXIRQ;
962                 }else if(nbsend(p->outch, &p->tbr) > 0){
963                         p->tbr = 0;
964                         p->lsr |= 3<<5;
965                         p->irq |= UARTTXIRQ;
966                 }
967         }
968         irqline(4 - (p - uart), (p->irq & p->ier) != 0);
969 }
970
971 static u32int
972 uartio(int isin, u16int port, u32int val, int sz, void *)
973 {
974         UART *p;
975         int rc;
976
977         if((port & 0xff8) == 0x3f8) p = &uart[0];
978         else if((port & 0xff8) == 0x2f8) p = &uart[1];
979         else return 0;
980         
981         val = (u8int) val;
982         switch(isin << 4 | port & 7){
983         case 0x00:
984                 if((p->lcr & 1<<7) != 0)
985                         p->dll = val;
986                 else{ /* transmit byte */
987                         if((p->mcr & 1<<4) != 0){
988                                 p->irq |= UARTRXIRQ;
989                                 p->rbr = val;
990                                 p->lsr |= 3<<5;
991                         }else{
992                                 p->tbr = val;
993                                 p->lsr &= ~(3<<5);
994                                 p->irq &= ~UARTTXIRQ;
995                         }
996                         uartkick(p);
997                 }
998                 return 0;
999         case 0x01:
1000                 if((p->lcr & 1<<7) != 0)
1001                         p->dlh = val;
1002                 else
1003                         p->ier = val & 15;
1004                 return 0;
1005         case 0x02: p->fcr = val; return 0;
1006         case 0x03: p->lcr = val; return 0;
1007         case 0x04: p->mcr = val & 0x1f; return 0;
1008         case 0x07: p->scr = val; return 0;
1009         case 0x10:
1010                 if((p->lcr & 1<<7) != 0) return p->dll;
1011                 p->irq &= ~UARTRXIRQ;
1012                 rc = p->rbr;
1013                 uartkick(p);
1014                 return rc;
1015         case 0x11:
1016                 if((p->lcr & 1<<7) != 0) return p->dlh;
1017                 return p->ier;
1018         case 0x12:
1019                 rc = 0;
1020                 uartkick(p);
1021                 if((p->irq & UARTRXIRQ) != 0)
1022                         return rc | 4;
1023                 else if((p->irq & UARTTXIRQ) != 0){
1024                         p->irq &= ~UARTTXIRQ;
1025                         uartkick(p);
1026                         return rc | 2;
1027                 }else
1028                         return rc | 1;
1029         case 0x13: return p->lcr;
1030         case 0x14: return p->mcr;
1031         case 0x15:
1032                 uartkick(p);
1033                 rc = p->lsr; /* line status */
1034                 if((p->irq & UARTRXIRQ) != 0)
1035                         rc |= 1;
1036                 return rc;
1037         case 0x16: /* modem status */
1038                 if((p->mcr & 0x10) != 0)
1039                         return (p->mcr << 1 & 2 | p->mcr >> 1 & 1 | p->mcr & 0xc) << 4;
1040                 return 0x90; /* DCD + CTS asserted */
1041         case 0x17: return p->scr;
1042         }
1043         return iowhine(isin, port, val, sz, "uart");
1044 }
1045
1046 static void
1047 uartrxproc(void *uv)
1048 {
1049         UART *u;
1050         char buf[128], *p;
1051         int rc;
1052         int eofctr;
1053         
1054         threadsetname("uart rx");
1055         u = uv;
1056         eofctr = 0;
1057         for(;;){
1058                 rc = read(u->infd, buf, sizeof(buf));
1059                 if(rc < 0){
1060                         vmerror("read(uartrx): %r");
1061                         threadexits("read: %r");
1062                 }
1063                 if(rc == 0){
1064                         if(++eofctr == 100){ /* keep trying but give up eventually */
1065                                 vmerror("read(uartrx): eof");
1066                                 threadexits("read: eof");
1067                         }
1068                         continue;
1069                 }else eofctr = 0;
1070                 for(p = buf; p < buf + rc; p++){
1071                         send(u->inch, p);
1072                         sendnotif((void(*)(void*))uartkick, u);
1073                 }
1074         }
1075 }
1076
1077 static void
1078 uarttxproc(void *uv)
1079 {
1080         UART *u;
1081         char buf[128], *p;
1082         
1083         threadsetname("uart tx");
1084         u = uv;
1085         for(;;){
1086                 p = buf;
1087                 recv(u->outch, p);
1088                 p++;
1089                 sendnotif((void(*)(void*))uartkick, u);
1090                 sleep(1);
1091                 while(sendnotif((void(*)(void*))uartkick, u), p < buf+sizeof(buf) && nbrecv(u->outch, p) > 0)
1092                         p++;
1093                 if(write(u->outfd, buf, p - buf) < p - buf)
1094                         vmerror("write(uarttx): %r");
1095         }
1096 }
1097
1098 void
1099 uartinit(int n, char *cfg)
1100 {
1101         char *p, *infn, *outfn;
1102         
1103         p = strchr(cfg, ',');
1104         if(p == nil){
1105                 infn = cfg;
1106                 outfn = cfg;
1107         }else{
1108                 *p = 0;
1109                 infn = cfg;
1110                 outfn = p + 1;
1111         }
1112         if(infn != nil && *infn != 0){
1113                 uart[n].infd = open(infn, OREAD);
1114                 if(uart[n].infd < 0)
1115                         sysfatal("open: %r");
1116                 uart[n].inch = chancreate(sizeof(char), 256);
1117                 proccreate(uartrxproc, &uart[n], 4096);
1118         }
1119         if(outfn != nil && *outfn != 0){
1120                 uart[n].outfd = open(outfn, OWRITE);
1121                 if(uart[n].outfd < 0)
1122                         sysfatal("open: %r");
1123                 uart[n].outch = chancreate(sizeof(char), 256);
1124                 proccreate(uarttxproc, &uart[n], 4096);
1125         }
1126 }
1127
1128
1129 /* floppy dummy controller */
1130 typedef struct Floppy Floppy;
1131 struct Floppy {
1132         u8int dor;
1133         u8int dump, irq;
1134         u8int fdc;
1135         u8int inq[16];
1136         u8int inqr, inqw;
1137 } fdc;
1138 #define fdcputc(c) (fdc.inq[fdc.inqw++ & 15] = (c))
1139
1140 static void
1141 fdccmd(u8int val)
1142 {
1143         vmdebug("fdc: cmd %#x", val);
1144         switch(val){
1145         case 0x03:
1146                 fdc.dump = 2;
1147                 break;
1148         case 0x07:
1149                 fdc.dump = 1;
1150                 fdc.irq = 1;
1151                 break;
1152         case 0x08:
1153                 irqline(6, 0);
1154                 fdcputc(0x80);
1155                 fdcputc(0);
1156                 break;
1157         default:
1158                 vmerror("unknown fdc command %.2x", val);
1159         }
1160 }
1161
1162 static u32int
1163 fdcio(int isin, u16int port, u32int val, int sz, void *)
1164 {
1165         u8int rc;
1166
1167         if(sz != 1) vmerror("fdc: access size %d != 1", sz);
1168         val = (u8int) val;
1169         switch(isin << 4 | port & 7){
1170         case 0x02: fdc.dor = val; return 0;
1171         case 0x05:
1172                 if(fdc.dump > 0){
1173                         if(--fdc.dump == 0 && fdc.inqr == fdc.inqw && fdc.irq != 0){
1174                                 irqline(6, 1);
1175                                 fdc.irq = 0;
1176                         }
1177                 }else if(fdc.inqr == fdc.inqw)
1178                         fdccmd(val);
1179                 return 0;
1180         case 0x12: return fdc.dor;
1181         case 0x14:
1182                 rc = 0x80;
1183                 if(fdc.dump == 0 && fdc.inqr != fdc.inqw)
1184                         rc |= 0x40;
1185                 if(fdc.dump != 0 || fdc.inqr != fdc.inqw)
1186                         rc |= 0x10;
1187                 return rc;
1188         case 0x15:
1189                 if(fdc.dump == 0 && fdc.inqr != fdc.inqw)
1190                         return fdc.inq[fdc.inqr++ & 15];
1191                 return 0;
1192         }
1193         return iowhine(isin, port, val, sz, "fdc");
1194 }
1195
1196 static u32int
1197 nopio(int, u16int, u32int, int, void *)
1198 {
1199         return -1;
1200 }
1201
1202 u32int
1203 iowhine(int isin, u16int port, u32int val, int sz, void *mod)
1204 {
1205         if(isin)
1206                 vmerror("%s%sread from unknown i/o port %#ux ignored (sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, sz, rget(RPC));
1207         else
1208                 vmerror("%s%swrite to unknown i/o port %#ux ignored (val=%#ux, sz=%d, pc=%#ullx)", mod != nil ? mod : "", mod != nil ? ": " : "", port, val, sz, rget(RPC));
1209         return -1;
1210 }
1211
1212 typedef struct IOHandler IOHandler;
1213 struct IOHandler {
1214         u16int lo, hi;
1215         u32int (*io)(int, u16int, u32int, int, void *);
1216         void *aux;
1217 };
1218
1219 u32int vgaio(int, u16int, u32int, int, void *);
1220 u32int pciio(int, u16int, u32int, int, void *);
1221 u32int vesaio(int, u16int, u32int, int, void *);
1222 u32int ideio(int, u16int, u32int, int, void *);
1223 IOHandler handlers[] = {
1224         0x20, 0x21, picio, nil,
1225         0x40, 0x43, pitio, nil,
1226         0x70, 0x71, rtcio, nil,
1227         0xa0, 0xa1, picio, nil,
1228         0x60, 0x60, i8042io, nil,
1229         0x61, 0x61, pitio, nil, /* pc speaker */
1230         0x64, 0x64, i8042io, nil,
1231         0x2f8, 0x2ff, uartio, nil,
1232         0x3b0, 0x3bb, vgaio, nil,
1233         0x3c0, 0x3df, vgaio, nil,
1234         0x3f8, 0x3ff, uartio, nil,
1235         0x4d0, 0x4d1, picio, nil,
1236         0xcf8, 0xcff, pciio, nil,
1237         0xfee0, 0xfeef, vesaio, nil,
1238
1239         0x170, 0x177, ideio, nil, /* ide secondary */
1240         0x376, 0x376, ideio, nil, /* ide secondary (aux) */
1241         0x1f0, 0x1f7, ideio, nil, /* ide primary */
1242         0x3f6, 0x3f6, ideio, nil, /* ide primary (aux) */
1243         0x3f0, 0x3f7, fdcio, nil, /* floppy */
1244
1245         0x080, 0x080, nopio, nil, /* dma -- used by linux for delay by dummy write */
1246         0x084, 0x084, nopio, nil, /* dma -- used by openbsd for delay by dummy read */
1247         0x100, 0x110, nopio, nil, /* elnk3 */
1248         0x240, 0x25f, nopio, nil, /* ne2000 */
1249         0x278, 0x27a, nopio, nil, /* LPT1 / ISA PNP */
1250         0x280, 0x29f, nopio, nil, /* ne2000 */
1251         0x2e8, 0x2ef, nopio, nil, /* COM4 */
1252         0x300, 0x31f, nopio, nil, /* ne2000 */
1253         0x320, 0x32f, nopio, nil, /* etherexpress */
1254         0x330, 0x33f, nopio, nil, /* uha scsi */
1255         0x340, 0x35f, nopio, nil, /* adaptec scsi */
1256         0x360, 0x373, nopio, nil, /* isolan */
1257         0x378, 0x37a, nopio, nil, /* LPT1 */
1258         0x3e0, 0x3e5, nopio, nil, /* cardbus or isa pci bridges */
1259         0x3e8, 0x3ef, nopio, nil, /* COM3 */
1260         0x650, 0x65f, nopio, nil, /* 3c503 ethernet */
1261         0x778, 0x77a, nopio, nil, /* LPT1 (ECP) */
1262         0xa79, 0xa79, nopio, nil, /* isa pnp */
1263 };
1264
1265 static u32int
1266 io0(int dir, u16int port, u32int val, int size)
1267 {
1268         IOHandler *h;
1269         extern PCIBar iobars;
1270         PCIBar *p;
1271
1272         for(h = handlers; h < handlers + nelem(handlers); h++)
1273                 if(port >= h->lo && port <= h->hi)
1274                         return h->io(dir, port, val, size, h->aux);
1275         for(p = iobars.busnext; p != &iobars; p = p->busnext)
1276                 if(port >= p->addr && port < p->addr + p->length)
1277                         return p->io(dir, port - p->addr, val, size, p->aux);
1278         return iowhine(dir, port, val, size, nil);
1279 }
1280
1281 u32int iodebug[32];
1282
1283 u32int
1284 io(int isin, u16int port, u32int val, int sz)
1285 {
1286         int dbg;
1287         
1288         dbg = port < 0x400 && (iodebug[port >> 5] >> (port & 31) & 1) != 0;
1289         if(isin){
1290                 val = io0(isin, port, val, sz);
1291                 if(sz == 1) val = (u8int)val;
1292                 else if(sz == 2) val = (u16int)val;
1293                 if(dbg)
1294                         vmdebug("in  %#.4ux <- %#.*ux", port, sz*2, val);
1295                 return val;
1296         }else{
1297                 if(sz == 1) val = (u8int)val;
1298                 else if(sz == 2) val = (u16int)val;
1299                 io0(isin, port, val, sz);
1300                 if(dbg)
1301                         vmdebug("out %#.4ux <- %#.*ux", port, sz*2, val);
1302                 return 0;
1303         }
1304 }