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.


Actual emulated keyboard (but not yet debugged)
[palacios.git] / palacios / src / devices / keyboard.c
1 #include <devices/keyboard.h>
2 #include <geekos/io.h>
3 #include <palacios/vmm.h>
4 #include <palacios/vmm_types.h>
5
6 #define KEYBOARD_DEBUG 1
7
8 #if KEYBOARD_DEBUG
9 #define KEYBOARD_DEBUG_PRINT(first, rest...) PrintDebug(first, ##rest)
10 #else
11 #define KEYBOARD_DEBUG_PRINT(first, rest...)
12 #endif
13
14
15 extern struct vmm_os_hooks *os_hooks;
16
17 extern void SerialPrint(const char *format, ...);
18
19
20
21 #define KEYBOARD_60H          0x60  // keyboard microcontroller
22 #define KEYBOARD_64H          0x64  // onboard microcontroller
23
24 #define KEYBOARD_IRQ    0x1
25
26
27 // extract bits for status byte
28 #define STATUS_OUTPUT_BUFFER_FULL   0x01  // 1=full
29 #define STATUS_INPUT_BUFFER_FULL    0x02  // 1=full
30 #define STATUS_SYSTEM               0x04  // 1=self-test-passed
31 #define STATUS_COMMAND_DATA_AVAIL   0x08  // internal: 0=data on 60h, 0=cmd on 64h
32 #define STATUS_ENABLED              0x10  // 1=keyboard is enabled
33 #define STATUS_MOUSE_BUFFER_FULL    0x20  // 1= mouse output buffer full
34 #define STATUS_TIMEOUT              0x40  // 1=timeout of keybd
35 #define STATUS_PARITY               0x80  // 1=parity error
36
37 // bits for cmd byte
38
39 #define CMD_INTR                0x01  // 1=interrupts enabled
40 #define CMD_MOUSE_INTR          0x02  // 1=interrupts enabled for mouse
41 #define CMD_SYSTEM              0x04  // 1= self test passed
42 #define CMD_OVERRIDE            0x08  // FORCE 0 for  PS2
43 #define CMD_DISABLE             0x10  // 1=disabled keyboard
44 #define CMD_MOUSE_DISABLE       0x20  // 1=disabled mouse
45 #define CMD_SCANCODE_XLATE      0x40  // 1=translate to set 1 scancodes
46 #define CMD_RESERVED            0x80  // should be zero
47
48 // The currently targetted keyboard
49 static struct vm_device *thekeyboard=NULL;
50
51
52 struct keyboard_internal {
53   // 
54   // 0x60 is the port for the keyboard microcontroller
55   //   writes are commands
56   //   reads from it usually return scancodes
57   //   however, it can also return other data 
58   //   depending on the state of the onboard microcontroller
59   //
60   // 0x64 is the port for the onboard microcontroller
61   //   writes are commands
62   //   reads are status
63   //
64
65   // state of the onboard microcontroller
66   // this is needed because sometimes 0x60 reads come
67   // from the onboard microcontroller
68   enum {NORMAL,
69         // after receiving cmd 0x20 
70         // keyboard uC cmd byte pushed onto output queue
71         WRITING_CMD_BYTE,  
72         // after receiving cmd 0x60
73         // keybaord uC cmd will subsequently arive on data port
74         TRANSMIT_PASSWD,
75         // after recieving 0xa5
76         // password arrives on data port, null terminated
77   } state;
78         
79
80
81   uchar_t cmd_byte;         //  for keyboard uC - read/written 
82                             //     via read/write cmd byte command
83   uchar_t status_byte;      //  for on-board uC - read via 64h
84
85   uchar_t input_queue;      //  Read via 60h
86   uint_t  input_queue_len;  //  num items queued
87   uchar_t output_queue;     //  Written by 60h
88   uint_t  output_queue_len; //  num items queued 
89 };
90
91
92 // 
93 // push item onto outputqueue, optionally overwriting if there is no room
94 // returns 0 if successful
95 //
96 static int PushToOutputQueue(struct vm_device *dev, uchar_t value, uchar_t overwrite) 
97 {
98   struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
99   
100   if (state->output_queue_len==0 || overwrite) { 
101     state->output_queue=value;
102     state->output_queue_len=1;
103     state->status_byte |= STATUS_OUTPUT_BUFFER_FULL;
104     return 0;
105   } else {
106     KEYBOARD_DEBUG_PRINT("keyboard: PushToOutputQueue Failed - Queue Full\n");
107     return -1;
108   }
109 }
110
111 #if 0
112 // 
113 // pull item from outputqueue 
114 // returns 0 if successful
115 //
116 static int PullFromOutputQueue(struct vm_device *dev,uchar_t *value) 
117 {
118   struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
119   if (state->output_queue_len==1) { 
120     *value=state->input_queue;
121     state->output_queue_len=0;
122     state->status_byte &= ~STATUS_OUTPUT_BUFFER_FULL;
123     return 0;
124   } else {
125     KEYBOARD_DEBUG_PRINT("keyboard: PullFromOutputQueue Failed - Queue Empty\n");
126     return -1;
127   }
128 }
129 #endif
130
131 // 
132 // push item onto inputqueue, optionally overwriting if there is no room
133 // returns 0 if successful
134 //
135 static int PushToInputQueue(struct vm_device *dev, uchar_t value, uchar_t overwrite) 
136 {
137   struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
138   if (state->input_queue_len==0 || overwrite) { 
139     state->input_queue=value;
140     state->input_queue_len=1;
141     state->status_byte |= STATUS_INPUT_BUFFER_FULL;
142     return 0;
143   } else {
144     KEYBOARD_DEBUG_PRINT("keyboard: PushToOutputQueue Failed - Queue Full\n");
145     return -1;
146   }
147 }
148
149 // 
150 // pull item from inputqueue 
151 // returns 0 if successful
152 //
153 static int PullFromInputQueue(struct vm_device *dev, uchar_t *value) 
154 {
155   struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
156   if (state->input_queue_len==1) { 
157     *value=state->input_queue;
158     state->input_queue_len=0;
159     state->status_byte &=~STATUS_INPUT_BUFFER_FULL;
160     return 0;
161   } else {
162     KEYBOARD_DEBUG_PRINT("keyboard: PullFromInputQueue Failed - Queue Empty\n");
163     return -1;
164   }
165 }
166
167 static struct vm_device *demultiplex_injected_key(uchar_t status, uchar_t scancode)
168 {
169   // this currently does nothing
170   return thekeyboard;
171 }
172
173 int keyboard_interrupt(uint_t irq,struct vm_device * dev);
174
175 void deliver_key_to_vmm(uchar_t status, uchar_t scancode)
176 {
177   struct vm_device *dev = demultiplex_injected_key(status,scancode);
178
179   struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
180
181   KEYBOARD_DEBUG_PRINT("keyboard: injected status 0x%x, and scancode 0x%x\n", status,scancode);
182   
183   if (    (state->status_byte & STATUS_ENABLED)      // onboard is enabled
184           && (!(state->cmd_byte & CMD_DISABLE)))  {   // keyboard is enabled
185
186     PushToInputQueue(dev,scancode,1);
187
188     if (state->cmd_byte & CMD_INTR) { 
189       keyboard_interrupt(KEYBOARD_IRQ,dev);
190     }
191         
192   }
193 }
194
195 int keyboard_reset_device(struct vm_device * dev)
196 {
197   struct keyboard_internal *data = (struct keyboard_internal *) dev->private_data;
198   
199   memset(data,0,sizeof(struct keyboard_internal));
200
201   data->cmd_byte =   
202       CMD_INTR        // interrupts on
203     | CMD_MOUSE_INTR  // mouse interupts on
204     | CMD_SYSTEM ;    // self test passed
205                       // PS2, keyboard+mouse enabled, generic translation    
206   
207   data->status_byte = 
208       STATUS_SYSTEM     // self-tests passed
209     | STATUS_ENABLED ;  // keyboard ready
210                         // buffers empty, no errors
211
212   KEYBOARD_DEBUG_PRINT("keyboard: reset device\n");
213  
214   return 0;
215
216 }
217
218
219
220 int keyboard_start_device(struct vm_device *dev)
221 {
222   KEYBOARD_DEBUG_PRINT("keyboard: start device\n");
223   return 0;
224 }
225
226
227 int keyboard_stop_device(struct vm_device *dev)
228 {
229   KEYBOARD_DEBUG_PRINT("keyboard: stop device\n");
230   return 0;
231 }
232
233
234
235
236 int keyboard_write_command(ushort_t port,
237                            void * src, 
238                            uint_t length,
239                            struct vm_device * dev)
240 {
241   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
242   uchar_t cmd;
243
244   // Should always be single byte write
245
246   if (length!=1) { 
247     KEYBOARD_DEBUG_PRINT("keyboard: write of >1 bytes to 64h");
248     return -1;
249   }
250
251   cmd =  *((uchar_t*)src); 
252
253   if (state->state!=NORMAL) { 
254     KEYBOARD_DEBUG_PRINT("keyboard: warning - receiving command on 64h but state!=NORMAL\n");
255   }
256   
257   KEYBOARD_DEBUG_PRINT("keyboard: command 0x%x on 64h\n",cmd);
258
259   switch (cmd) { 
260
261   case 0x20:  // READ COMMAND BYTE (returned in 60h)
262     PushToInputQueue(dev,state->cmd_byte,1);
263     state->state=NORMAL;  // the next read on 0x60 will get the right data
264     break;
265
266   case 0x60:  // WRITE COMMAND BYTE (read from 60h)
267     state->state=WRITING_CMD_BYTE; // we need to make sure we send the next 0x60 byte appropriately
268     break;
269
270   // case 0x90-9f - write to output port  (?)
271
272   case 0xa1: // Get version number
273     PushToInputQueue(dev,0,1);
274     state->state=NORMAL;
275     break;
276
277   case 0xa4:  // is password installed?  send result to 0x60
278     // we don't support passwords
279     PushToOutputQueue(dev,0xf1,1);
280     state->state=NORMAL;
281     break;
282
283   case 0xa5:  // new password will arrive on 0x60
284     state->state=TRANSMIT_PASSWD;
285     break;
286
287   case 0xa6:  // check passwd;
288     // since we do not support passwords, we will simply ignore this
289     // the implication is that any password check immediately succeeds 
290     // with a blank password
291     state->state=NORMAL;
292     break;
293
294   case 0xa7:  // disable mouse
295     state->cmd_byte |= CMD_MOUSE_DISABLE;
296     state->state=NORMAL;
297     break;
298
299   case 0xa8:  // enable mouse
300     state->cmd_byte &= ~CMD_MOUSE_DISABLE;
301     state->state=NORMAL;
302     break;
303
304   case 0xa9:  // mouse interface test  (always succeeds)
305     PushToInputQueue(dev,0,1);
306     state->state=NORMAL;
307     break;
308
309   case 0xaa:  // controller self test (always succeeds)
310     PushToInputQueue(dev,0x55,1);
311     state->state=NORMAL;
312     break;
313
314   case 0xab:  // keyboard interface test (always succeeds)
315     PushToInputQueue(dev,0,1);
316     state->state=NORMAL;
317     break;
318
319   case 0xad:  // disable keyboard
320     state->cmd_byte |= CMD_DISABLE;
321     state->state=NORMAL;
322     break;
323
324   case 0xae:  // enable keyboard
325     state->cmd_byte &= ~CMD_DISABLE;
326     state->state=NORMAL;
327     break;
328
329   case 0xaf:  // get version
330     PushToInputQueue(dev,0x00,1);
331     state->state=NORMAL;
332     break;
333
334   // case c0  read input port ?
335   // case c1  copy input port lsn to status
336   // case c2  copy input port msn to status
337   
338   // case d0 read output port
339   // case d1 write output port
340   // case d2 write keyboard buffer (inject key)
341   // case d3 write mouse buffer (inject mouse)
342   // case d4 write mouse device (command to mouse?)
343
344   // case e0 read test port
345   
346   // case f0..ff pulse output port ?
347
348    
349   default:
350     KEYBOARD_DEBUG_PRINT("keyboard: ignoring command (unimplemented)\n");
351     state->state=NORMAL;
352     break;
353   }
354
355   return 1;
356
357 }
358
359 int keyboard_read_status(ushort_t port,
360                          void * dest, 
361                          uint_t length,
362                          struct vm_device * dev)
363 {
364   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
365
366   if (length==1) { 
367     KEYBOARD_DEBUG_PRINT("keyboard: read status (64h): ");
368     *((uchar_t*)dest)=state->status_byte;
369     KEYBOARD_DEBUG_PRINT("0x%x\n",*((uchar_t*)dest));
370     return 1;
371   } else {
372     KEYBOARD_DEBUG_PRINT("keyboard: >1 byte read for status (64h)\n");
373     return -1;
374   }
375 }
376
377 int keyboard_write_output(ushort_t port,
378                           void * src, 
379                           uint_t length,
380                           struct vm_device * dev)
381 {
382   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
383
384   if (length!=1) { 
385     KEYBOARD_DEBUG_PRINT("keyboard: write of 60h with >1 byte\n");
386     return -1;
387   }
388
389   uchar_t data=*((uchar_t*)src);
390   
391   switch (state->state) {
392   case WRITING_CMD_BYTE:
393     state->cmd_byte=data;
394     state->state=NORMAL;
395     break;
396   case TRANSMIT_PASSWD:
397     if (data) {
398       //ignore passwd
399     } else {
400       // end of password
401       state->state=NORMAL;
402     }
403     break;
404   case NORMAL:
405     // command is being sent to keyboard controller
406     switch (data) { 
407     case 0xff: // reset
408       PushToInputQueue(dev,0xfa,1); // ack
409       state->state=NORMAL;
410       break;
411     case 0xfe: // resend
412     case 0xfd: // set key type make
413     case 0xfc: // set key typ make/break
414     case 0xfb: // set key type typematic
415     case 0xfa: // set all typematic make/break/typematic
416     case 0xf9: // set all make
417     case 0xf8: // set all make/break
418     case 0xf7: // set all typemaktic
419     case 0xf6: // set defaults
420     case 0xf5: // disable scanning
421     case 0xf4: // enable scanning
422     case 0xf3: // set typematic delay/rate
423     default:
424       KEYBOARD_DEBUG_PRINT("keyboard: unhandled command 0x%x on output buffer (60h)\n");
425       break;
426     }
427   default:
428     KEYBOARD_DEBUG_PRINT("keyboard: unknown state %x on command 0x%x on output buffer (60h)\n",state->state, data);
429   }
430   
431   return 1;
432 }
433
434 int keyboard_read_input(ushort_t port,
435                         void * dest, 
436                         uint_t length,
437                         struct vm_device * dev)
438 {
439   if (length==1) { 
440     uchar_t data;
441     KEYBOARD_DEBUG_PRINT("keyboard: read from input (60h): ");
442     PullFromInputQueue(dev,&data);
443     KEYBOARD_DEBUG_PRINT("0x%x\n",data);
444     *((uchar_t*)dest)=data;
445     return 1;
446   } else {
447     KEYBOARD_DEBUG_PRINT("keyboard: unknown size read from input (60h)\n");
448     return -1;
449   }
450 }
451
452
453 int keyboard_interrupt(uint_t irq,
454                        struct vm_device * dev) 
455 {
456   KEYBOARD_DEBUG_PRINT("keyboard: interrupt\n");
457
458   dev->vm->vm_ops.raise_irq(dev->vm,irq);
459
460   return 0;
461
462 }
463
464
465 int keyboard_init_device(struct vm_device * dev) 
466 {
467  
468   //  struct keyboard_internal *data = (struct keyboard_internal *) dev->private_data;
469
470   KEYBOARD_DEBUG_PRINT("keyboard: init_device\n");
471
472   // Would read state here
473
474   keyboard_reset_device(dev);
475
476   // hook ports
477   dev_hook_io(dev, KEYBOARD_64H, &keyboard_read_status, &keyboard_write_command);
478   dev_hook_io(dev, KEYBOARD_60H, &keyboard_read_input, &keyboard_write_output);
479   
480   //
481   // We do not hook the IRQ here.  Instead, the underlying device driver
482   // is responsible to call us back
483   // 
484
485   return 0;
486 }
487
488 int keyboard_deinit_device(struct vm_device *dev)
489 {
490
491   dev_unhook_io(dev, KEYBOARD_60H);
492   dev_unhook_io(dev, KEYBOARD_64H);
493   keyboard_reset_device(dev);
494   return 0;
495 }
496
497
498
499
500
501 static struct vm_device_ops dev_ops = { 
502   .init = keyboard_init_device, 
503   .deinit = keyboard_deinit_device,
504   .reset = keyboard_reset_device,
505   .start = keyboard_start_device,
506   .stop = keyboard_stop_device,
507 };
508
509
510
511
512 struct vm_device *create_keyboard() {
513   
514   if (thekeyboard!=NULL) { 
515     KEYBOARD_DEBUG_PRINT("keyboard: creating >1 keyboard device.  This will probably fail!\n");
516   }
517   
518   struct keyboard_internal * keyboard_state = (struct keyboard_internal *)V3_Malloc(sizeof(struct keyboard_internal));
519
520   struct vm_device *device = create_device("KEYBOARD", &dev_ops, keyboard_state);
521
522   thekeyboard=device;
523   
524   return device;
525 }