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.


reworked device IO hook framework
[palacios.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 static int keyboard_reset_device(struct keyboard_internal * kbd) {
445   
446     memset(kbd, 0, sizeof(struct keyboard_internal));
447
448     kbd->state = NORMAL;
449     kbd->mouse_state = STREAM;
450
451
452     // PS2, keyboard+mouse enabled, generic translation    
453     kbd->cmd.val = 0;
454
455     kbd->cmd.irq_en = 1;
456     kbd->cmd.mouse_irq_en = 1;
457     kbd->cmd.self_test_ok = 1;
458     /** **/
459
460
461     // buffers empty, no errors
462     kbd->status.val = 0; 
463
464     kbd->status.self_test_ok = 1; // self-tests passed
465     kbd->status.enabled = 1;// keyboard ready
466     /** **/
467
468     
469     kbd->output_byte = 0;  //  ?
470
471     kbd->input_byte = INPUT_RAM;  // we have some
472     // also display=color, jumper 0, keyboard enabled 
473
474     PrintDebug("keyboard: reset device\n");
475  
476     return 0;
477
478 }
479
480
481
482
483
484
485
486 static int mouse_write_output(struct keyboard_internal * kbd, uint8_t data) {
487     switch (kbd->mouse_state) { 
488         case NORMAL:
489             switch (data) {
490
491                 case 0xff: //reset
492                     if (kbd->mouse_enabled == 0) {
493                         push_to_output_queue(kbd, 0xfe, DATA, MOUSE) ;   // no mouse!
494                     } else {
495                         push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
496                         push_to_output_queue(kbd, 0xaa, DATA, MOUSE) ; 
497                         push_to_output_queue(kbd, 0x00, DATA, MOUSE) ; 
498                     }
499                     break;
500
501 /*              case 0xfe: //resend */
502 /*                  PushToOutputQueue(kbd, 0xfa, OVERWRITE, DATA, MOUSE) ;  */
503 /*                  PrintDebug(" mouse resend begins "); */
504 /*                  kbd->mouse_done_after_ack = 0; */
505 /*                  kbd->mouse_needs_ack = 0; */
506 /*                  kbd->mouse_state = STREAM1; */
507 /*                  return 0;  // not done */
508 /*                  break; */
509       
510                 case 0xf6: // set defaults
511                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
512                     PrintDebug(" mouse set defaults ");
513
514                     break;
515       
516                 case 0xf5: // disable data reporting 
517                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
518                     PrintDebug(" mouse disable data reporting ");
519                     break;
520       
521                 case 0xf4: // enable data reporting 
522                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
523                     PrintDebug(" mouse enable data reporting ");
524                     break;
525       
526                 case 0xf3: // set sample rate
527                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
528                     kbd->mouse_state = SAMPLE;
529                     PrintDebug(" mouse set sample rate begins ");
530                     break;
531       
532                 case 0xf2: // get device id
533                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
534                     push_to_output_queue(kbd, 0x0,  DATA, MOUSE); 
535                     PrintDebug(" mouse get device id begins ");
536                     break;
537       
538                 case 0xf0: // set remote mode
539                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
540                     PrintDebug(" mouse set remote mode  ");
541                     break;
542
543                 case 0xee: // set wrap mode
544                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
545                     PrintError(" mouse set wrap mode (ignored)  ");
546                     break;
547
548                 case 0xec: // reset wrap mode
549                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
550                     PrintError(" mouse reset wrap mode (ignored)  ");
551                     break;
552
553                 case 0xeb: // read data
554                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
555                     PrintError(" mouse switch to wrap mode (ignored)  ");
556                     break;
557       
558                 case 0xea: // set stream mode
559                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
560                     PrintDebug(" mouse set stream mode  ");
561                     break;
562
563                 case 0xe9: // status request
564                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
565                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
566                     push_to_output_queue(kbd, 0x00, DATA, MOUSE);
567                     push_to_output_queue(kbd, 0x00, DATA, MOUSE); 
568                     PrintDebug(" mouse status request begins  ");
569                     break;
570
571                 case 0xe8: // set resolution
572                     push_to_output_queue(kbd, MOUSE_ACK,  DATA, MOUSE) ; 
573                     PrintDebug(" mouse set resolution begins  ");
574                     kbd->mouse_state = SET_RES;
575                     break;
576
577                 case 0xe7: // set scaling 2:1
578                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
579                     PrintDebug(" mouse set scaling 2:1 ");
580                     break;
581
582                 case 0xe6: // set scaling 1:1
583                     push_to_output_queue(kbd, MOUSE_ACK, DATA, MOUSE) ; 
584                     PrintDebug(" mouse set scaling 1:1 ");
585                     break;
586       
587                 default:
588                     PrintDebug(" receiving unknown mouse command (0x%x) in acceptable kbd ", data);
589                     break;
590             }
591
592             break;
593         case SAMPLE:
594         case SET_RES:
595         default:
596             PrintDebug(" receiving mouse output in unhandled kbd (0x%x) ", kbd->mouse_state);
597             return -1;
598     }
599
600     return 0;
601 }
602
603
604
605 #if KEYBOARD_DEBUG_80H
606 static int keyboard_write_delay(ushort_t port, void * src,  uint_t length, void * priv_data) {
607
608     if (length == 1) { 
609         PrintDebug("keyboard: write of 0x%x to 80h\n", *((uint8_t*)src));
610         return 1;
611     } else {
612         PrintDebug("keyboard: write of >1 byte to 80h\n", *((uint8_t*)src));
613         return length;
614     }
615 }
616
617 static int keyboard_read_delay(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
618
619     if (length == 1) { 
620         *(uint8_t *)dest = v3_inb(port);
621
622         PrintDebug("keyboard: read of 0x%x from 80h\n", *((uint8_t*)dest));
623
624         return 1;
625     } else {
626         PrintDebug("keyboard: read of >1 byte from 80h\n");
627
628         return length;
629     }
630 }
631 #endif
632     
633   
634
635
636
637 static int keyboard_write_command(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
638     struct keyboard_internal * kbd = priv_data;
639     uint8_t cmd = *(uint8_t *)src;
640
641     // Should always be single byte write
642     if (length != 1) { 
643         PrintError("keyboard: write of >1 bytes (%d) to 64h\n", length);
644         return -1;
645     }
646
647
648     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
649
650     if (kbd->state != NORMAL) { 
651         PrintDebug("keyboard: warning - receiving command on 64h but state != NORMAL\n");
652     }
653   
654     PrintDebug("keyboard: command 0x%x on 64h\n", cmd);
655
656     switch (cmd) { 
657         case 0x20:  // READ COMMAND BYTE (returned in 60h)
658             push_to_output_queue(kbd, kbd->cmd.val, COMMAND, KEYBOARD);
659             PrintDebug("keyboard: command byte 0x%x returned\n", kbd->cmd.val);
660             break;
661
662         case 0x60:  // WRITE COMMAND BYTE (read from 60h)
663             kbd->state = WRITING_CMD_BYTE; // we need to make sure we send the next 0x60 byte appropriately
664             PrintDebug("keyboard: prepare to write command byte\n");
665             break;
666
667             // case 0x90-9f - write to output port  (?)
668
669         case 0xa1: // Get version number
670             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
671             PrintDebug("keyboard: version number 0x0 returned\n");
672             break;
673
674         case 0xa4:  // is password installed?  send result to 0x60
675             // we don't support passwords
676             push_to_output_queue(kbd, 0xf1, COMMAND, KEYBOARD);
677             PrintDebug("keyboard: password not installed\n");
678             break;
679
680         case 0xa5:  // new password will arrive on 0x60
681             kbd->state = TRANSMIT_PASSWD;
682             PrintDebug("keyboard: pepare to transmit password\n");
683             break;
684
685         case 0xa6:  // check passwd;
686             // since we do not support passwords, we will simply ignore this
687             // the implication is that any password check immediately succeeds 
688             // with a blank password
689             PrintDebug("keyboard: password check succeeded\n");
690             break;
691
692         case 0xa7:  // disable mouse
693             kbd->cmd.mouse_disable = 1;
694             PrintDebug("keyboard: mouse disabled\n");
695             break;
696
697         case 0xa8:  // enable mouse
698             kbd->cmd.mouse_disable = 0;
699             PrintDebug("keyboard: mouse enabled\n");
700             break;
701
702         case 0xa9:  // mouse interface test  (always succeeds)
703             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
704             PrintDebug("keyboard: mouse interface test succeeded\n");
705             break;
706
707         case 0xaa:  // controller self test (always succeeds)
708             push_to_output_queue(kbd, 0x55, COMMAND, KEYBOARD);
709             PrintDebug("keyboard: controller self test succeeded\n");
710             break;
711
712         case 0xab:  // keyboard interface test (always succeeds)
713             push_to_output_queue(kbd, 0, COMMAND, KEYBOARD);
714             PrintDebug("keyboard: keyboard interface test succeeded\n");
715             break;
716
717         case 0xad:  // disable keyboard
718             kbd->cmd.disable = 1;
719             PrintDebug("keyboard: keyboard disabled\n");
720             break;
721
722         case 0xae:  // enable keyboard
723             kbd->cmd.disable = 0;
724             PrintDebug("keyboard: keyboard enabled\n");
725             break;
726
727         case 0xaf:  // get version
728             push_to_output_queue(kbd, 0x00, COMMAND, KEYBOARD);
729             PrintDebug("keyboard: version 0 returned \n");
730             break;
731
732         case 0xd0: // return microcontroller output on 60h
733             push_to_output_queue(kbd, kbd->output_byte, COMMAND, KEYBOARD);
734             PrintDebug("keyboard: output byte 0x%x returned\n", kbd->output_byte);
735             break;
736
737         case 0xd1: // request to write next byte on 60h to the microcontroller output port
738             kbd->state = WRITING_OUTPUT_PORT;
739             PrintDebug("keyboard: prepare to write output byte\n");
740             break;
741
742         case 0xd2:  //  write keyboard buffer (inject key)
743             kbd->state = INJECTING_KEY;
744             PrintDebug("keyboard: prepare to inject key\n");
745             break;
746
747         case 0xd3: //  write mouse buffer (inject mouse)
748             kbd->state = INJECTING_MOUSE;
749             PrintDebug("keyboard: prepare to inject mouse\n");
750             break;
751
752         case 0xd4: // write mouse device (command to mouse?)
753             kbd->state = IN_MOUSE;
754             PrintDebug("keyboard: prepare to inject mouse command\n");
755             break;
756
757         case 0xc0: //  read input port 
758             push_to_output_queue(kbd, kbd->input_byte, COMMAND, KEYBOARD);
759             PrintDebug("keyboard: input byte 0x%x returned\n", kbd->input_byte);
760             break;
761
762         case 0xc1:  //copy input port lsn to status msn
763             kbd->status.val &= 0x0f;
764             kbd->status.val |= (kbd->input_byte & 0xf) << 4;
765             PrintDebug("keyboard: copied input byte low 4 bits to status reg hi 4 bits\n");
766             break;
767
768         case 0xc2: // copy input port msn to status msn
769             kbd->status.val &= 0x0f;
770             kbd->status.val |= (kbd->input_byte & 0xf0);
771             PrintDebug("keyboard: copied input byte hi 4 bits to status reg hi 4 bits\n");
772             break;
773     
774         case 0xe0: // read test port
775             push_to_output_queue(kbd, kbd->output_byte >> 6, COMMAND, KEYBOARD);
776             PrintDebug("keyboard: read 0x%x from test port\n", kbd->output_byte >> 6);
777             break;
778
779    
780         case 0xf0:   // pulse output port
781         case 0xf1:   // this should pulse 0..3 of cmd_byte on output port 
782         case 0xf2:   // instead of what is currently in output_byte (I think)
783         case 0xf3:   // main effect is taht if bit zero is zero
784         case 0xf4:   // should cause reset
785         case 0xf5:   // I doubt anything more recent than a 286 running 
786         case 0xf6:   // OS2 with the penalty box will care
787         case 0xf7:
788         case 0xf8:
789         case 0xf9:
790         case 0xfa:
791         case 0xfb:
792         case 0xfc:
793         case 0xfd:
794         case 0xfe:
795         case 0xff:
796             PrintDebug("keyboard: ignoring pulse of 0x%x (low=pulsed) on output port\n", (cmd & 0xf));
797             break;
798    
799             // case ac  diagonstic - returns 16 bytes from keyboard microcontroler on 60h
800         default:
801             PrintDebug("keyboard: ignoring command (unimplemented)\n");
802             break;
803     }
804
805     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
806
807     return length;
808 }
809
810 static int keyboard_read_status(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
811     struct keyboard_internal * kbd = priv_data;
812
813     if (length != 1) { 
814         PrintError("keyboard: >1 byte read for status (64h)\n");
815         return -1;
816     }
817
818     PrintDebug("keyboard: read status (64h): ");
819
820     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
821
822     *(uint8_t *)dest = kbd->status.val;
823
824     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
825     
826     PrintDebug("0x%x\n", *(uint8_t *)dest);
827     
828     return length;
829 }
830
831 static int keyboard_write_output(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
832     struct keyboard_internal * kbd = priv_data;
833     int ret = length;
834
835     if (length != 1) { 
836         PrintError("keyboard: write of 60h with >1 byte\n");
837         return -1;
838     }
839
840     uint8_t data = *(uint8_t *)src;
841   
842     PrintDebug("keyboard: output 0x%x on 60h\n", data);
843
844     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
845
846     switch (kbd->state) {
847         case WRITING_CMD_BYTE:
848             kbd->cmd.val = data;
849             kbd->state = NORMAL;
850             PrintDebug("keyboard: wrote new command byte 0x%x\n", kbd->cmd.val);
851             break;
852
853         case WRITING_OUTPUT_PORT:
854             kbd->output_byte = data;
855             kbd->state = NORMAL;
856             PrintDebug("keyboard: wrote new output byte 0x%x\n", kbd->output_byte);
857             break;
858
859         case INJECTING_KEY:
860             push_to_output_queue(kbd, data, COMMAND, KEYBOARD);  // probably should be a call to deliver_key_to_vmm()
861             kbd->state = NORMAL;
862             PrintDebug("keyboard: injected key 0x%x\n", data);
863             break;
864
865         case INJECTING_MOUSE:
866             push_to_output_queue(kbd, data, DATA, MOUSE);
867             //      PrintDebug("keyboard: ignoring injected mouse event 0x%x\n", data);
868             PrintDebug("keyboard: injected mouse event 0x%x\n", data);
869             kbd->state = NORMAL;
870             break;
871
872         case IN_MOUSE:
873             PrintDebug("keyboard: mouse action: ");
874             if (mouse_write_output(kbd, data)) { 
875                 kbd->state = NORMAL;
876             }
877             PrintDebug("\n");
878             break;
879
880         case TRANSMIT_PASSWD:
881             if (data) {
882                 //ignore passwd
883                 PrintDebug("keyboard: ignoring password character 0x%x\n",data);
884             } else {
885                 // end of password
886                 kbd->state = NORMAL;
887                 PrintDebug("keyboard: done with password\n");
888             }
889             break;
890
891         case SET_LEDS:
892             PrintDebug("Keyboard: LEDs being set...\n");
893             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
894             kbd->state = NORMAL;
895             break;
896
897         case SET_RATE:
898             PrintDebug("Keyboard: Rate being set...\n");
899             push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
900             kbd->state = NORMAL;
901             break;
902
903         default:
904         case NORMAL: {
905             // command is being sent to keyboard controller
906             switch (data) { 
907                 case 0xff: // reset
908                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD); // ack
909                     push_to_output_queue(kbd, 0xaa, COMMAND, KEYBOARD);
910                     PrintDebug("keyboard: reset complete and acked\n");
911                     break;
912
913                 case 0xf5: // disable scanning
914                 case 0xf4: // enable scanning
915                     // ack
916                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
917                     // should do something here... PAD
918                     PrintDebug("keyboard: %s scanning done and acked\n", (data == 0xf5) ? "disable" : "enable");
919                     break;
920
921                 case 0xf3:
922                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
923                     kbd->state = SET_RATE;
924                     break;
925
926                 case 0xf2: // get keyboard ID
927                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
928                     push_to_output_queue(kbd, 0xab, COMMAND, KEYBOARD);
929                     push_to_output_queue(kbd, 0x83, COMMAND, KEYBOARD);
930                     PrintDebug("Keyboard: Requesting Keyboard ID\n");
931                     break;
932
933                 case 0xed: // enable keyboard LEDs
934                     push_to_output_queue(kbd, 0xfa, COMMAND, KEYBOARD);
935                     kbd->state = SET_LEDS;
936                     break;
937
938                 case 0xee: // echo, used by FreeBSD to probe controller
939                     push_to_output_queue(kbd, 0xee, COMMAND, KEYBOARD);
940                     break;
941
942                 case 0xfe: // resend
943                 case 0xfd: // set key type make
944                 case 0xfc: // set key typ make/break
945                 case 0xfb: // set key type typematic
946                 case 0xfa: // set all typematic make/break/typematic
947                 case 0xf9: // set all make
948                 case 0xf8: // set all make/break
949                 case 0xf7: // set all typemaktic
950                 case 0xf6: // set defaults
951                     PrintError("keyboard: unhandled known command 0x%x on output buffer (60h)\n", data);
952                     ret = -1;
953                     break;
954
955                 default:
956                     PrintError("keyboard: unhandled unknown command 0x%x on output buffer (60h)\n", data);
957                     kbd->status.out_buf_full = 1;
958                     ret = -1;
959                     break;
960             }
961             break;
962         }
963     }
964   
965     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
966
967     return ret;
968 }
969
970 static int keyboard_read_input(struct guest_info * core, ushort_t port, void * dest, uint_t length, void * priv_data) {
971     struct keyboard_internal * kbd = priv_data;
972
973     if (length != 1) {
974         PrintError("keyboard: unknown size read from input (60h)\n");
975         return -1;
976     }
977     
978     addr_t irq_state = v3_lock_irqsave(kbd->kb_lock);
979
980     pull_from_output_queue(kbd, (uint8_t *)dest);
981       
982     v3_unlock_irqrestore(kbd->kb_lock, irq_state);
983
984     PrintDebug("keyboard: read from input (60h): 0x%x\n", *(uint8_t *)dest);
985
986     return length;
987 }
988
989
990
991
992
993
994 static int keyboard_free(struct vm_device * dev) {
995     
996     return 0;
997 }
998
999
1000
1001
1002
1003 static struct v3_device_ops dev_ops = { 
1004     .free = keyboard_free,
1005
1006 };
1007
1008
1009
1010
1011 static int keyboard_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
1012     struct keyboard_internal * kbd = NULL;
1013     char * dev_id = v3_cfg_val(cfg, "ID");
1014
1015     PrintDebug("keyboard: init_device\n");
1016
1017     kbd = (struct keyboard_internal *)V3_Malloc(sizeof(struct keyboard_internal));
1018
1019     memset(kbd, 0, sizeof(struct keyboard_internal));
1020
1021     kbd->vm = vm;
1022
1023     kbd->mouse_queue.start = 0;
1024     kbd->mouse_queue.end = 0;
1025     kbd->mouse_queue.count = 0;
1026
1027     kbd->kbd_queue.start = 0;
1028     kbd->kbd_queue.end = 0;
1029     kbd->kbd_queue.count = 0;
1030
1031     kbd->mouse_enabled = 0;
1032
1033     struct vm_device * dev = v3_allocate_device(dev_id, &dev_ops, kbd);
1034
1035     if (v3_attach_device(vm, dev) == -1) {
1036         PrintError("Could not attach device %s\n", dev_id);
1037         return -1;
1038     }
1039
1040     keyboard_reset_device(kbd);
1041
1042
1043     v3_lock_init(&(kbd->kb_lock));
1044
1045
1046     // hook ports
1047     v3_dev_hook_io(dev, KEYBOARD_64H, &keyboard_read_status, &keyboard_write_command);
1048     v3_dev_hook_io(dev, KEYBOARD_60H, &keyboard_read_input, &keyboard_write_output);
1049
1050     v3_hook_host_event(vm, HOST_KEYBOARD_EVT, V3_HOST_EVENT_HANDLER(key_event_handler), dev);
1051     v3_hook_host_event(vm, HOST_MOUSE_EVT, V3_HOST_EVENT_HANDLER(mouse_event_handler), dev);
1052
1053
1054 #if KEYBOARD_DEBUG_80H
1055     v3_dev_hook_io(dev, KEYBOARD_DELAY_80H, &keyboard_read_delay, &keyboard_write_delay);
1056 #endif
1057
1058   
1059     //
1060     // We do not hook the IRQ here.  Instead, the underlying device driver
1061     // is responsible to call us back
1062     // 
1063
1064     return 0;
1065 }
1066
1067
1068 device_register("KEYBOARD", keyboard_init)