Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


71bed117139bafe052b138359ac78c4acc60fa68
[palacios.releases.git] / palacios / src / devices / keyboard.c
1 /* 
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2008, Peter Dinda <pdinda@northwestern.edu> 
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Peter Dinda <pdinda@northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_dev_mgr.h>
22 #include <palacios/vmm_types.h>
23
24 #include <palacios/vmm_ringbuffer.h>
25 #include <palacios/vmm_lock.h>
26 #include <palacios/vmm_intr.h>
27 #include <palacios/vmm_host_events.h>
28 #include <palacios/vm_guest.h>
29
30
31 #ifndef CONFIG_DEBUG_KEYBOARD
32 #undef PrintDebug
33 #define PrintDebug(fmt, args...)
34 #endif
35
36 #define KEYBOARD_DEBUG_80H   0
37
38
39
40 #define KEYBOARD_60H          0x60  // keyboard microcontroller
41 #define KEYBOARD_64H          0x64  // onboard microcontroller
42
43 #define KEYBOARD_DELAY_80H    0x80  // written for timing
44
45 #define KEYBOARD_IRQ          0x1
46 #define MOUSE_IRQ             0xc   
47
48
49
50 // bits for the output port 
51 #define OUTPUT_RESET        0x01  // System reset on 0
52 #define OUTPUT_A20          0x02  // A20 gate (1= A20 is gated)
53 #define OUTPUT_RES1         0x04  // reserved
54 #define OUTPUT_RES2         0x08  // reserved
55 #define OUTPUT_OUTPUT_FULL  0x10  // output buffer full
56 #define OUTPUT_INPUT_EMPTY  0x20  // input buffer empty
57 #define OUTPUT_KBD_CLOCK    0x40  // keyboard clock (?)
58 #define OUTPUT_KBD_DATA     0x80  // keyboard data
59
60 // bits for the input port
61
62 #define INPUT_RES0          0x01  // reserved
63 #define INPUT_RES1          0x02  // reserved
64 #define INPUT_RES2          0x04  // reserved
65 #define INPUT_RES3          0x08  // reserved
66 #define INPUT_RAM           0x10  // set to 1 if RAM exists?
67 #define INPUT_JUMPER        0x20  // manufacturing jumper?
68 #define INPUT_DISPLAY       0x40  // 0=color, 1=mono
69 #define INPUT_KBD_INHIBIT   0x80  // 1=inhibit keyboard ?
70
71
72 #define MOUSE_ACK           0xfa
73
74 // for queue operations
75 #define QUEUE               0
76 #define OVERWRITE           1
77
78 // for queue operations - whether it's data or cmd waiting on 60h
79 #define DATA                0
80 #define COMMAND             1
81
82 // for queue operations - whether this is keyboard or mouse data on 60h
83 #define KEYBOARD            0
84 #define MOUSE               1
85
86
87
88 struct cmd_reg {
89     union {
90         uint8_t val;
91         struct {
92             uint8_t irq_en        : 1;  // 1=interrupts enabled
93             uint8_t mouse_irq_en  : 1;  // 1=interrupts enabled for mouse
94             uint8_t self_test_ok  : 1;  // 1= self test passed
95             uint8_t override      : 1;  // MBZ for  PS2
96             uint8_t disable       : 1;  // 1=disabled keyboard
97             uint8_t mouse_disable : 1;  // 1=disabled mouse
98             uint8_t translate     : 1;  // 1=translate to set 1 scancodes (For PC Compatibility)
99             uint8_t rsvd          : 1;  // must be zero
100         } __attribute__((packed));
101     } __attribute__((packed));
102 } __attribute__((packed));
103
104
105
106
107 struct status_reg {
108     union {
109         uint8_t val;
110         struct {
111             uint8_t out_buf_full        : 1; // 1=full (data for system)
112             uint8_t in_buf_full         : 1; // 1=full (data for 8042)
113             uint8_t self_test_ok        : 1; // 1=self-test-passed
114             uint8_t cmd                 : 1; // 0=data on 60h, 1=cmd on 64h
115             uint8_t enabled             : 1; // 1=keyboard is enabled
116             uint8_t mouse_buf_full      : 1; // 1= mouse output buffer full
117             uint8_t timeout_err         : 1; // 1=timeout of keybd
118             uint8_t parity_err          : 1; // 1=parity error
119         } __attribute__((packed));
120     } __attribute__((packed));
121 } __attribute__((packed));
122
123
124
125
126
127
128 /* This QUEUE_SIZE must be 256 */
129 /* Its designed this way to cause the start/end index to automatically
130    wrap around (2^8 = 256) so an overrun will automatically readjust the 
131    indexes 
132 */
133 #define QUEUE_SIZE 256
134 struct queue {
135     uint8_t queue[QUEUE_SIZE];
136
137     uint8_t start;
138     uint8_t end;
139     int count;
140 };
141
142 struct keyboard_internal {
143     // 
144     // 0x60 is the port for the keyboard microcontroller
145     //   writes are commands
146     //   reads from it usually return scancodes
147     //   however, it can also return other data 
148     //   depending on the state of the onboard microcontroller
149     //
150     // 0x64 is the port for the onboard microcontroller
151     //   writes are commands
152     //   reads are status
153     //
154
155     // state of the onboard microcontroller
156     // this is needed because sometimes 0x60 reads come
157     // from the onboard microcontroller
158     enum {// Normal mode measn we deliver keys
159         // to the vm and accept commands from it
160         NORMAL,
161         // after receiving cmd 0x60
162         // keybaord uC cmd will subsequently arrive
163         WRITING_CMD_BYTE,  
164         // after recieving 0xa5
165         // password arrives on data port, null terminated
166         TRANSMIT_PASSWD,
167         // after having a d1 sent to 64
168         // we wait for a new output byte on 60
169         WRITING_OUTPUT_PORT,
170         // after having a d2 sent to 64
171         // we wait for a new output byte on 60
172         // then make it available as a keystroke
173         INJECTING_KEY,
174         // after having a d3 sent to 64
175         // we wait for a new output byte on 60
176         // then make it available as a mouse event
177         INJECTING_MOUSE,
178         // after having a d4 sent to 64
179         // we wait for a new output byte on 60
180         // then send it to the mouse
181         IN_MOUSE,
182         // After the Keyboard LEDs are enabled
183         // we wait for the output byte on 64?
184         SET_LEDS,
185         // After the Keyboard SET_RATE is called
186         // we wait for the output byte on 64?
187         SET_RATE,
188     } state;
189
190
191     enum {
192         // Normal mouse state
193         STREAM, 
194         // this is used for setting sample rate
195         SAMPLE,
196         // set resolution
197         SET_RES,
198     } mouse_state;
199
200
201
202     struct cmd_reg cmd;
203     struct status_reg status;
204
205     uint8_t output_byte;      //  output port of onboard uC (e.g. A20)
206     uint8_t input_byte;       //  input port of onboard uC
207
208     // Data for system
209     uint8_t wrap;     
210
211     int mouse_enabled;
212
213     struct queue kbd_queue;
214     struct queue mouse_queue;
215
216     struct v3_vm_info * vm;
217
218     v3_lock_t kb_lock;
219 };
220
221
222 static int update_kb_irq(struct keyboard_internal * state) {
223     int irq_num = 0;
224
225
226     state->status.out_buf_full = 0;
227     state->status.mouse_buf_full = 0;
228
229
230     // If there is pending Keyboard data then it overrides mouse data
231     if (state->kbd_queue.count > 0) {
232         irq_num = KEYBOARD_IRQ;
233     } else if (state->mouse_queue.count > 0) {
234         irq_num = MOUSE_IRQ;
235         state->status.mouse_buf_full = 1;
236     } 
237     
238     PrintDebug("keyboard: interrupt 0x%d\n", irq_num);
239     
240     if (irq_num) {
241         // Global output buffer flag (for both Keyboard and mouse)
242         state->status.out_buf_full = 1;
243         
244         if (state->cmd.irq_en == 1) { 
245             v3_raise_irq(state->vm, irq_num);
246         }
247     }
248
249     return 0;
250 }
251
252
253
254 /* Only one byte is read per irq 
255  * So if the queue is still full after a data read, we re-raise the irq
256  * If we keep reading an empty queue we return the last queue entry
257  */
258
259 static int push_to_output_queue(struct keyboard_internal * state, uint8_t value, uint8_t cmd, uint8_t mouse) {
260     struct queue * q = NULL;
261
262
263     if (mouse) {
264         q = &(state->mouse_queue);
265     } else {
266         q = &(state->kbd_queue);
267     }
268
269     if (q->count >= QUEUE_SIZE) {
270         return 0;
271     }
272
273     if (cmd) {
274         state->status.cmd = 1;
275     } else {
276         state->status.cmd = 0;
277     }
278
279     q->queue[q->end] = value;
280
281     if (q->end >= (QUEUE_SIZE - 1)) {
282         q->end = 0;
283     } else {
284         q->end++;
285     }
286
287     q->count++;
288
289
290     update_kb_irq(state);
291
292     return 0;
293 }
294
295
296
297 static int pull_from_output_queue(struct keyboard_internal * state, uint8_t * value) {
298     struct queue * q = NULL;
299
300     if (state->kbd_queue.count > 0) {
301         q = &(state->kbd_queue);
302         PrintDebug("Reading from Keyboard Queue\n");
303     } else if (state->mouse_queue.count > 0) {
304         q = &(state->mouse_queue);
305         PrintDebug("Reading from Mouse Queue\n");
306     } else {
307         uint8_t idx = state->kbd_queue.start - 1;
308         PrintDebug("No Data in any queue\n");
309         *value = state->kbd_queue.queue[idx];
310         return 0;
311     }
312
313     *value = q->queue[q->start];
314
315     if (q->start >= (QUEUE_SIZE - 1)) {
316         q->start = 0;
317     } else {
318         q->start++;
319     }
320
321     q->count--;
322
323
324     PrintDebug("Read from Queue: %x\n", *value);
325     PrintDebug("QStart=%d, QEnd=%d\n", q->start, q->end);
326
327     update_kb_irq(state);
328
329     return 0;
330 }
331
332
333 #include <palacios/vmm_telemetry.h>
334 #ifdef CONFIG_SYMMOD
335 #include <palacios/vmm_symmod.h>
336 #endif
337
338 static int key_event_handler(struct v3_vm_info * vm, 
339                              struct v3_keyboard_event * evt, 
340                              void * private_data) {
341     struct keyboard_internal * state = (struct keyboard_internal *)private_data;
342
343     PrintDebug("keyboard: injected status 0x%x, and scancode 0x%x\n", evt->status, evt->scan_code);
344
345     if (evt->scan_code == 0x44) { // F10 debug dump
346         int i = 0;
347         for (i = 0; i < vm->num_cores; i++) {
348             v3_print_guest_state(&(vm->cores[i]));
349         }
350         //      PrintGuestPageTables(info, info->shdw_pg_state.guest_cr3);
351     } 
352 #ifdef CONFIG_SYMCALL
353     else if (evt->scan_code == 0x43) { // F9 Sym test
354         struct guest_info * core = &(vm->cores[0]);
355         PrintDebug("Testing sym call\n");
356         sym_arg_t a0 = 0x1111;
357         sym_arg_t a1 = 0x2222;
358         sym_arg_t a2 = 0x3333;
359         sym_arg_t a3 = 0x4444;
360         sym_arg_t a4 = 0x5555;
361         uint64_t call_start = 0;
362         uint64_t call_end = 0;
363         
364         V3_Print("Exits before symcall: %d\n", (uint32_t)core->num_exits);
365
366         rdtscll(call_start);
367         v3_sym_call5(core, SYMCALL_TEST, &a0, &a1, &a2, &a3, &a4);
368         rdtscll(call_end);
369         
370         V3_Print("Symcall latency = %d cycles (%d exits)\n", (uint32_t)(call_end - call_start), (uint32_t)core->num_exits);
371
372         V3_Print("Symcall  Test Returned arg0=%x, arg1=%x, arg2=%x, arg3=%x, arg4=%x\n",
373                  (uint32_t)a0, (uint32_t)a1, (uint32_t)a2, (uint32_t)a3, (uint32_t)a4);
374
375     } 
376 #endif
377     else if (evt->scan_code == 0x42) { // F8 debug toggle
378         extern int v3_dbg_enable;
379         
380         PrintDebug("Toggling Debugging\n");     
381         v3_dbg_enable ^= 1;
382
383     } 
384 #ifdef CONFIG_TELEMETRY
385
386     else if (evt->scan_code == 0x41) { // F7 telemetry dump
387         v3_print_telemetry(vm);
388     } 
389 #endif
390 #ifdef CONFIG_SYMMOD
391     else if (evt->scan_code == 0x40) { // F6 Test symmod load
392         v3_load_sym_capsule(vm, "lnx_test");
393     }
394 #endif
395
396
397     addr_t irq_state = v3_lock_irqsave(state->kb_lock);
398
399     if ( (state->status.enabled == 1)      // onboard is enabled
400          && (state->cmd.disable == 0) )  {   // keyboard is enabled
401     
402         push_to_output_queue(state, evt->scan_code, DATA, KEYBOARD);
403     }
404
405     v3_unlock_irqrestore(state->kb_lock, irq_state);
406   
407     return 0;
408 }
409
410
411 static int mouse_event_handler(struct v3_vm_info * vm, 
412                                struct v3_mouse_event * evt, 
413                                void * private_data) {
414     struct keyboard_internal * kbd = (struct keyboard_internal *)private_data;
415     int ret = 0;
416
417     PrintDebug("keyboard: injected mouse packet 0x %x %x %x\n",
418                evt->data[0], evt->data[1], evt->data[2]);
419   
420     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
421
422     switch (kbd->mouse_state) { 
423         case STREAM:
424
425             if (kbd->cmd.mouse_disable == 0) {
426                 push_to_output_queue(kbd, evt->data[0], DATA, MOUSE);
427                 push_to_output_queue(kbd, evt->data[1], DATA, MOUSE);
428                 push_to_output_queue(kbd, evt->data[2], DATA, MOUSE);
429             }
430             break;
431         default:
432             PrintError("Invalid mouse state\n");
433             ret = -1;
434             break;
435     }
436
437
438     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
439
440     return ret;
441 }
442
443
444
445
446
447
448
449
450
451
452 static int mouse_write_output(struct keyboard_internal * kbd, uint8_t data) {
453     switch (kbd->mouse_state) { 
454         case NORMAL:
455             switch (data) {
456
457                 case 0xff: //reset
458                     if (kbd->mouse_enabled == 0) {
459                         push_to_output_queue(kbd, 0xfe, DATA, MOUSE) ;   // no mouse!
460                     } else {
461                         push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
462                         push_to_output_queue(kbd, 0xaa, DATA, MOUSE) ; 
463                         push_to_output_queue(kbd, 0x00, DATA, MOUSE) ; 
464                     }
465                     break;
466
467 /*              case 0xfe: //resend */
468 /*                  PushToOutputQueue(kbd, 0xfa, OVERWRITE, DATA, MOUSE) ;  */
469 /*                  PrintDebug(" mouse resend begins "); */
470 /*                  kbd->mouse_done_after_ack = 0; */
471 /*                  kbd->mouse_needs_ack = 0; */
472 /*                  kbd->mouse_state = STREAM1; */
473 /*                  return 0;  // not done */
474 /*                  break; */
475       
476                 case 0xf6: // set defaults
477                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
478                     PrintDebug(" mouse set defaults ");
479
480                     break;
481       
482                 case 0xf5: // disable data reporting 
483                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
484                     PrintDebug(" mouse disable data reporting ");
485                     break;
486       
487                 case 0xf4: // enable data reporting 
488                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
489                     PrintDebug(" mouse enable data reporting ");
490                     break;
491       
492                 case 0xf3: // set sample rate
493                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
494                     kbd->mouse_state = SAMPLE;
495                     PrintDebug(" mouse set sample rate begins ");
496                     break;
497       
498                 case 0xf2: // get device id
499                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
500                     push_to_output_queue(kbd, 0x0,  DATA, MOUSE); 
501                     PrintDebug(" mouse get device id begins ");
502                     break;
503       
504                 case 0xf0: // set remote mode
505                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
506                     PrintDebug(" mouse set remote mode  ");
507                     break;
508
509                 case 0xee: // set wrap mode
510                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
511                     PrintError(" mouse set wrap mode (ignored)  ");
512                     break;
513
514                 case 0xec: // reset wrap mode
515                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
516                     PrintError(" mouse reset wrap mode (ignored)  ");
517                     break;
518
519                 case 0xeb: // read data
520                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
521                     PrintError(" mouse switch to wrap mode (ignored)  ");
522                     break;
523       
524                 case 0xea: // set stream mode
525                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
526                     PrintDebug(" mouse set stream mode  ");
527                     break;
528
529                 case 0xe9: // status request
530                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
531                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
532                     push_to_output_queue(kbd, 0x00, DATA, MOUSE);
533                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
534                     PrintDebug(" mouse status request begins  ");
535                     break;
536
537                 case 0xe8: // set resolution
538                     push_to_output_queue(kbd, MOUSE_ACK,  DATA, MOUSE) ; 
539                     PrintDebug(" mouse set resolution begins  ");
540                     kbd->mouse_state = SET_RES;
541                     break;
542
543                 case 0xe7: // set scaling 2:1
544                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
545                     PrintDebug(" mouse set scaling 2:1 ");
546                     break;
547
548                 case 0xe6: // set scaling 1:1
549                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
550                     PrintDebug(" mouse set scaling 1:1 ");
551                     break;
552       
553                 default:
554                     PrintDebug(" receiving unknown mouse command (0x%x) in acceptable kbd ", data);
555                     break;
556             }
557
558             break;
559         case SAMPLE:
560         case SET_RES:
561         default:
562             PrintDebug(" receiving mouse output in unhandled kbd (0x%x) ", kbd->mouse_state);
563             return -1;
564     }
565
566     return 0;
567 }
568
569
570
571 #if KEYBOARD_DEBUG_80H
572 static int keyboard_write_delay(ushort_t port, void * src,  uint_t length, void * priv_data) {
573
574     if (length == 1) { 
575         PrintDebug("keyboard: write of 0x%x to 80h\n", *((uint8_t*)src));
576         return 1;
577     } else {
578         PrintDebug("keyboard: write of >1 byte to 80h\n", *((uint8_t*)src));
579         return length;
580     }
581 }
582
583 static int keyboard_read_delay(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
584
585     if (length == 1) { 
586         *(uint8_t *)dest = v3_inb(port);
587
588         PrintDebug("keyboard: read of 0x%x from 80h\n", *((uint8_t*)dest));
589
590         return 1;
591     } else {
592         PrintDebug("keyboard: read of >1 byte from 80h\n");
593
594         return length;
595     }
596 }
597 #endif
598     
599   
600
601
602
603 static int keyboard_write_command(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
604     struct keyboard_internal * kbd = priv_data;
605     uint8_t cmd = *(uint8_t *)src;
606
607     // Should always be single byte write
608     if (length != 1) { 
609         PrintError("keyboard: write of >1 bytes (%d) to 64h\n", length);
610         return -1;
611     }
612
613
614     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
615
616     if (kbd->state != NORMAL) { 
617         PrintDebug("keyboard: warning - receiving command on 64h but state != NORMAL\n");
618     }
619   
620     PrintDebug("keyboard: command 0x%x on 64h\n", cmd);
621
622     switch (cmd) { 
623         case 0x20:  // READ COMMAND BYTE (returned in 60h)
624             push_to_output_queue(kbd, kbd->cmd.val, COMMAND, KEYBOARD);
625             PrintDebug("keyboard: command byte 0x%x returned\n", kbd->cmd.val);
626             break;
627
628         case 0x60:  // WRITE COMMAND BYTE (read from 60h)
629             kbd->state = WRITING_CMD_BYTE; // we need to make sure we send the next 0x60 byte appropriately
630             PrintDebug("keyboard: prepare to write command byte\n");
631             break;
632
633             // case 0x90-9f - write to output port  (?)
634
635         case 0xa1: // Get version number
636             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
637             PrintDebug("keyboard: version number 0x0 returned\n");
638             break;
639
640         case 0xa4:  // is password installed?  send result to 0x60
641             // we don't support passwords
642             push_to_output_queue(kbd, 0xf1, COMMAND, KEYBOARD);
643             PrintDebug("keyboard: password not installed\n");
644             break;
645
646         case 0xa5:  // new password will arrive on 0x60
647             kbd->state = TRANSMIT_PASSWD;
648             PrintDebug("keyboard: pepare to transmit password\n");
649             break;
650
651         case 0xa6:  // check passwd;
652             // since we do not support passwords, we will simply ignore this
653             // the implication is that any password check immediately succeeds 
654             // with a blank password
655             PrintDebug("keyboard: password check succeeded\n");
656             break;
657
658         case 0xa7:  // disable mouse
659             kbd->cmd.mouse_disable = 1;
660             PrintDebug("keyboard: mouse disabled\n");
661             break;
662
663         case 0xa8:  // enable mouse
664             kbd->cmd.mouse_disable = 0;
665             PrintDebug("keyboard: mouse enabled\n");
666             break;
667
668         case 0xa9:  // mouse interface test  (always succeeds)
669             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
670             PrintDebug("keyboard: mouse interface test succeeded\n");
671             break;
672
673         case 0xaa:  // controller self test (always succeeds)
674             push_to_output_queue(kbd, 0x55, COMMAND, KEYBOARD);
675             PrintDebug("keyboard: controller self test succeeded\n");
676             break;
677
678         case 0xab:  // keyboard interface test (always succeeds)
679             push_to_output_queue(kbd, 0, COMMAND, KEYBOARD);
680             PrintDebug("keyboard: keyboard interface test succeeded\n");
681             break;
682
683         case 0xad:  // disable keyboard
684             kbd->cmd.disable = 1;
685             PrintDebug("keyboard: keyboard disabled\n");
686             break;
687
688         case 0xae:  // enable keyboard
689             kbd->cmd.disable = 0;
690             PrintDebug("keyboard: keyboard enabled\n");
691             break;
692
693         case 0xaf:  // get version
694             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
695             PrintDebug("keyboard: version 0 returned \n");
696             break;
697
698         case 0xd0: // return microcontroller output on 60h
699             push_to_output_queue(kbd, kbd->output_byte, COMMAND, KEYBOARD);
700             PrintDebug("keyboard: output byte 0x%x returned\n", kbd->output_byte);
701             break;
702
703         case 0xd1: // request to write next byte on 60h to the microcontroller output port
704             kbd->state = WRITING_OUTPUT_PORT;
705             PrintDebug("keyboard: prepare to write output byte\n");
706             break;
707
708         case 0xd2:  //  write keyboard buffer (inject key)
709             kbd->state = INJECTING_KEY;
710             PrintDebug("keyboard: prepare to inject key\n");
711             break;
712
713         case 0xd3: //  write mouse buffer (inject mouse)
714             kbd->state = INJECTING_MOUSE;
715             PrintDebug("keyboard: prepare to inject mouse\n");
716             break;
717
718         case 0xd4: // write mouse device (command to mouse?)
719             kbd->state = IN_MOUSE;
720             PrintDebug("keyboard: prepare to inject mouse command\n");
721             break;
722
723         case 0xc0: //  read input port 
724             push_to_output_queue(kbd, kbd->input_byte, COMMAND, KEYBOARD);
725             PrintDebug("keyboard: input byte 0x%x returned\n", kbd->input_byte);
726             break;
727
728         case 0xc1:  //copy input port lsn to status msn
729             kbd->status.val &= 0x0f;
730             kbd->status.val |= (kbd->input_byte & 0xf) << 4;
731             PrintDebug("keyboard: copied input byte low 4 bits to status reg hi 4 bits\n");
732             break;
733
734         case 0xc2: // copy input port msn to status msn
735             kbd->status.val &= 0x0f;
736             kbd->status.val |= (kbd->input_byte & 0xf0);
737             PrintDebug("keyboard: copied input byte hi 4 bits to status reg hi 4 bits\n");
738             break;
739     
740         case 0xe0: // read test port
741             push_to_output_queue(kbd, kbd->output_byte >> 6, COMMAND, KEYBOARD);
742             PrintDebug("keyboard: read 0x%x from test port\n", kbd->output_byte >> 6);
743             break;
744
745    
746         case 0xf0:   // pulse output port
747         case 0xf1:   // this should pulse 0..3 of cmd_byte on output port 
748         case 0xf2:   // instead of what is currently in output_byte (I think)
749         case 0xf3:   // main effect is taht if bit zero is zero
750         case 0xf4:   // should cause reset
751         case 0xf5:   // I doubt anything more recent than a 286 running 
752         case 0xf6:   // OS2 with the penalty box will care
753         case 0xf7:
754         case 0xf8:
755         case 0xf9:
756         case 0xfa:
757         case 0xfb:
758         case 0xfc:
759         case 0xfd:
760         case 0xfe:
761         case 0xff:
762             PrintDebug("keyboard: ignoring pulse of 0x%x (low=pulsed) on output port\n", (cmd & 0xf));
763             break;
764    
765             // case ac  diagonstic - returns 16 bytes from keyboard microcontroler on 60h
766         default:
767             PrintDebug("keyboard: ignoring command (unimplemented)\n");
768             break;
769     }
770
771     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
772
773     return length;
774 }
775
776 static int keyboard_read_status(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
777     struct keyboard_internal * kbd = priv_data;
778
779     if (length != 1) { 
780         PrintError("keyboard: >1 byte read for status (64h)\n");
781         return -1;
782     }
783
784     PrintDebug("keyboard: read status (64h): ");
785
786     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
787
788     *(uint8_t *)dest = kbd->status.val;
789
790     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
791     
792     PrintDebug("0x%x\n", *(uint8_t *)dest);
793     
794     return length;
795 }
796
797 static int keyboard_write_output(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
798     struct keyboard_internal * kbd = priv_data;
799     int ret = length;
800
801     if (length != 1) { 
802         PrintError("keyboard: write of 60h with >1 byte\n");
803         return -1;
804     }
805
806     uint8_t data = *(uint8_t *)src;
807   
808     PrintDebug("keyboard: output 0x%x on 60h\n", data);
809
810     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
811
812     switch (kbd->state) {
813         case WRITING_CMD_BYTE:
814             kbd->cmd.val = data;
815             kbd->state = NORMAL;
816             PrintDebug("keyboard: wrote new command byte 0x%x\n", kbd->cmd.val);
817             break;
818
819         case WRITING_OUTPUT_PORT:
820             kbd->output_byte = data;
821             kbd->state = NORMAL;
822             PrintDebug("keyboard: wrote new output byte 0x%x\n", kbd->output_byte);
823             break;
824
825         case INJECTING_KEY:
826             push_to_output_queue(kbd, data, COMMAND, KEYBOARD);  // probably should be a call to deliver_key_to_vmm()
827             kbd->state = NORMAL;
828             PrintDebug("keyboard: injected key 0x%x\n", data);
829             break;
830
831         case INJECTING_MOUSE:
832             push_to_output_queue(kbd, data, DATA, MOUSE);
833             //      PrintDebug("keyboard: ignoring injected mouse event 0x%x\n", data);
834             PrintDebug("keyboard: injected mouse event 0x%x\n", data);
835             kbd->state = NORMAL;
836             break;
837
838         case IN_MOUSE:
839             PrintDebug("keyboard: mouse action: ");
840             if (mouse_write_output(kbd, data)) { 
841                 kbd->state = NORMAL;
842             }
843             PrintDebug("\n");
844             break;
845
846         case TRANSMIT_PASSWD:
847             if (data) {
848                 //ignore passwd
849                 PrintDebug("keyboard: ignoring password character 0x%x\n",data);
850             } else {
851                 // end of password
852                 kbd->state = NORMAL;
853                 PrintDebug("keyboard: done with password\n");
854             }
855             break;
856
857         case SET_LEDS:
858             PrintDebug("Keyboard: LEDs being set...\n");
859             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
860             kbd->state = NORMAL;
861             break;
862
863         case SET_RATE:
864             PrintDebug("Keyboard: Rate being set...\n");
865             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
866             kbd->state = NORMAL;
867             break;
868
869         default:
870         case NORMAL: {
871             // command is being sent to keyboard controller
872             switch (data) { 
873                 case 0xff: // reset
874                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD); // ack
875                     push_to_output_queue(kbd, 0xaa, COMMAND, KEYBOARD);
876                     PrintDebug("keyboard: reset complete and acked\n");
877                     break;
878
879                 case 0xf5: // disable scanning
880                 case 0xf4: // enable scanning
881                     // ack
882                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
883                     // should do something here... PAD
884                     PrintDebug("keyboard: %s scanning done and acked\n", (data == 0xf5) ? "disable" : "enable");
885                     break;
886
887                 case 0xf3:
888                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
889                     kbd->state = SET_RATE;
890                     break;
891
892                 case 0xf2: // get keyboard ID
893                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
894                     push_to_output_queue(kbd, 0xab, COMMAND, KEYBOARD);
895                     push_to_output_queue(kbd, 0x83, COMMAND, KEYBOARD);
896                     PrintDebug("Keyboard: Requesting Keyboard ID\n");
897                     break;
898
899                 case 0xed: // enable keyboard LEDs
900                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
901                     kbd->state = SET_LEDS;
902                     break;
903
904                 case 0xee: // echo, used by FreeBSD to probe controller
905                     push_to_output_queue(kbd, 0xee, COMMAND, KEYBOARD);
906                     break;
907
908                 case 0xfe: // resend
909                 case 0xfd: // set key type make
910                 case 0xfc: // set key typ make/break
911                 case 0xfb: // set key type typematic
912                 case 0xfa: // set all typematic make/break/typematic
913                 case 0xf9: // set all make
914                 case 0xf8: // set all make/break
915                 case 0xf7: // set all typemaktic
916                 case 0xf6: // set defaults
917                     PrintError("keyboard: unhandled known command 0x%x on output buffer (60h)\n", data);
918                     ret = -1;
919                     break;
920
921                 default:
922                     PrintError("keyboard: unhandled unknown command 0x%x on output buffer (60h)\n", data);
923                     kbd->status.out_buf_full = 1;
924                     ret = -1;
925                     break;
926             }
927             break;
928         }
929     }
930   
931     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
932
933     return ret;
934 }
935
936 static int keyboard_read_input(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
937     struct keyboard_internal * kbd = priv_data;
938
939     if (length != 1) {
940         PrintError("keyboard: unknown size read from input (60h)\n");
941         return -1;
942     }
943     
944     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
945
946     pull_from_output_queue(kbd, (uint8_t *)dest);
947       
948     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
949
950     PrintDebug("keyboard: read from input (60h): 0x%x\n", *(uint8_t *)dest);
951
952     return length;
953 }
954
955
956
957
958
959
960 static int keyboard_free(struct vm_device * dev) {
961     
962     return 0;
963 }
964
965
966
967
968 static int keyboard_reset_device(struct keyboard_internal * kbd) {
969   
970
971     kbd->mouse_queue.start = 0;
972     kbd->mouse_queue.end = 0;
973     kbd->mouse_queue.count = 0;
974
975     kbd->kbd_queue.start = 0;
976     kbd->kbd_queue.end = 0;
977     kbd->kbd_queue.count = 0;
978
979     kbd->mouse_enabled = 0;
980
981     kbd->state = NORMAL;
982     kbd->mouse_state = STREAM;
983
984     // PS2, keyboard+mouse enabled, generic translation    
985     kbd->cmd.val = 0;
986
987     kbd->cmd.irq_en = 1;
988     kbd->cmd.mouse_irq_en = 1;
989     kbd->cmd.self_test_ok = 1;
990     /** **/
991
992
993     // buffers empty, no errors
994     kbd->status.val = 0; 
995
996     kbd->status.self_test_ok = 1; // self-tests passed
997     kbd->status.enabled = 1;// keyboard ready
998     /** **/
999
1000     
1001     kbd->output_byte = 0;  //  ?
1002
1003     kbd->input_byte = INPUT_RAM;  // we have some
1004     // also display=color, jumper 0, keyboard enabled 
1005
1006     PrintDebug("keyboard: reset device\n");
1007  
1008     return 0;
1009
1010 }
1011
1012 static struct v3_device_ops dev_ops = { 
1013     .free = keyboard_free,
1014
1015 };
1016
1017
1018
1019
1020 static int keyboard_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
1021     struct keyboard_internal * kbd = NULL;
1022     char * dev_id = v3_cfg_val(cfg, "ID");
1023     int ret = 0;
1024
1025     PrintDebug("keyboard: init_device\n");
1026
1027     kbd = (struct keyboard_internal *)V3_Malloc(sizeof(struct keyboard_internal));
1028
1029     memset(kbd, 0, sizeof(struct keyboard_internal));
1030
1031     kbd->vm = vm;
1032
1033     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, kbd);
1034
1035     if (dev == NULL) {
1036         PrintError("Could not attach device %s\n", dev_id);
1037         V3_Free(kbd);
1038         return -1;
1039     }
1040
1041     keyboard_reset_device(kbd);
1042
1043
1044     v3_lock_init(&(kbd->kb_lock));
1045
1046
1047     // hook ports
1048     ret |= v3_dev_hook_io(dev, KEYBOARD_64H, &keyboard_read_status, &keyboard_write_command);
1049     ret |= v3_dev_hook_io(dev, KEYBOARD_60H, &keyboard_read_input, &keyboard_write_output);
1050
1051     if (ret != 0) {
1052         PrintError("Error hooking keyboard IO ports\n");
1053         v3_remove_device(dev);
1054         return -1;
1055     }
1056
1057     v3_hook_host_event(vm, HOST_KEYBOARD_EVT, V3_HOST_EVENT_HANDLER(key_event_handler), kbd);
1058     v3_hook_host_event(vm, HOST_MOUSE_EVT, V3_HOST_EVENT_HANDLER(mouse_event_handler), kbd);
1059
1060
1061 #if KEYBOARD_DEBUG_80H
1062     v3_dev_hook_io(dev, KEYBOARD_DELAY_80H, &keyboard_read_delay, &keyboard_write_delay);
1063 #endif
1064
1065   
1066     //
1067     // We do not hook the IRQ here.  Instead, the underlying device driver
1068     // is responsible to call us back
1069     // 
1070
1071     return 0;
1072 }
1073
1074
1075 device_register("KEYBOARD", keyboard_init)