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)
Peter Dinda [Thu, 29 May 2008 22:42:55 +0000 (22:42 +0000)]
palacios/src/devices/keyboard.c

index ddccc89..1f098bb 100644 (file)
 #endif
 
 
-#define KEYBOARD_DATA_REG          0x60
-#define KEYBOARD_CONTROL_REG       0x64
+extern struct vmm_os_hooks *os_hooks;
+
+extern void SerialPrint(const char *format, ...);
+
+
+
+#define KEYBOARD_60H          0x60  // keyboard microcontroller
+#define KEYBOARD_64H          0x64  // onboard microcontroller
 
 #define KEYBOARD_IRQ    0x1
 
 
+// extract bits for status byte
+#define STATUS_OUTPUT_BUFFER_FULL   0x01  // 1=full
+#define STATUS_INPUT_BUFFER_FULL    0x02  // 1=full
+#define STATUS_SYSTEM               0x04  // 1=self-test-passed
+#define STATUS_COMMAND_DATA_AVAIL   0x08  // internal: 0=data on 60h, 0=cmd on 64h
+#define STATUS_ENABLED              0x10  // 1=keyboard is enabled
+#define STATUS_MOUSE_BUFFER_FULL    0x20  // 1= mouse output buffer full
+#define STATUS_TIMEOUT              0x40  // 1=timeout of keybd
+#define STATUS_PARITY               0x80  // 1=parity error
+
+// bits for cmd byte
+
+#define CMD_INTR                0x01  // 1=interrupts enabled
+#define CMD_MOUSE_INTR          0x02  // 1=interrupts enabled for mouse
+#define CMD_SYSTEM              0x04  // 1= self test passed
+#define CMD_OVERRIDE            0x08  // FORCE 0 for  PS2
+#define CMD_DISABLE             0x10  // 1=disabled keyboard
+#define CMD_MOUSE_DISABLE       0x20  // 1=disabled mouse
+#define CMD_SCANCODE_XLATE      0x40  // 1=translate to set 1 scancodes
+#define CMD_RESERVED            0x80  // should be zero
 
 // The currently targetted keyboard
 static struct vm_device *thekeyboard=NULL;
 
 
 struct keyboard_internal {
-  // read* is what is seen when reads are done from the VM
-  uchar_t read_status;
-  uchar_t read_scancode;
-  // write* is where we put the writes from the VM
-  uchar_t write_status;
-  uchar_t write_scancode;
-
-  uchar_t status_byte;      //  for on-board uC
+  // 
+  // 0x60 is the port for the keyboard microcontroller
+  //   writes are commands
+  //   reads from it usually return scancodes
+  //   however, it can also return other data 
+  //   depending on the state of the onboard microcontroller
+  //
+  // 0x64 is the port for the onboard microcontroller
+  //   writes are commands
+  //   reads are status
+  //
 
-  uchar_t input_queue;      //  input queue is for communication *to* the on-board uC
+  // state of the onboard microcontroller
+  // this is needed because sometimes 0x60 reads come
+  // from the onboard microcontroller
+  enum {NORMAL,
+       // after receiving cmd 0x20 
+       // keyboard uC cmd byte pushed onto output queue
+       WRITING_CMD_BYTE,  
+       // after receiving cmd 0x60
+       // keybaord uC cmd will subsequently arive on data port
+       TRANSMIT_PASSWD,
+       // after recieving 0xa5
+       // password arrives on data port, null terminated
+  } state;
+       
+
+
+  uchar_t cmd_byte;         //  for keyboard uC - read/written 
+                            //     via read/write cmd byte command
+  uchar_t status_byte;      //  for on-board uC - read via 64h
+
+  uchar_t input_queue;      //  Read via 60h
   uint_t  input_queue_len;  //  num items queued
-  uchar_t output_queue;     //  output queue is for communcation *from* the on-board uC
+  uchar_t output_queue;     //  Written by 60h
   uint_t  output_queue_len; //  num items queued 
 };
 
 
+// 
+// push item onto outputqueue, optionally overwriting if there is no room
+// returns 0 if successful
+//
+static int PushToOutputQueue(struct vm_device *dev, uchar_t value, uchar_t overwrite) 
+{
+  struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
+  
+  if (state->output_queue_len==0 || overwrite) { 
+    state->output_queue=value;
+    state->output_queue_len=1;
+    state->status_byte |= STATUS_OUTPUT_BUFFER_FULL;
+    return 0;
+  } else {
+    KEYBOARD_DEBUG_PRINT("keyboard: PushToOutputQueue Failed - Queue Full\n");
+    return -1;
+  }
+}
+
+#if 0
+// 
+// pull item from outputqueue 
+// returns 0 if successful
+//
+static int PullFromOutputQueue(struct vm_device *dev,uchar_t *value) 
+{
+  struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
+  if (state->output_queue_len==1) { 
+    *value=state->input_queue;
+    state->output_queue_len=0;
+    state->status_byte &= ~STATUS_OUTPUT_BUFFER_FULL;
+    return 0;
+  } else {
+    KEYBOARD_DEBUG_PRINT("keyboard: PullFromOutputQueue Failed - Queue Empty\n");
+    return -1;
+  }
+}
+#endif
+
+// 
+// push item onto inputqueue, optionally overwriting if there is no room
+// returns 0 if successful
+//
+static int PushToInputQueue(struct vm_device *dev, uchar_t value, uchar_t overwrite) 
+{
+  struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
+  if (state->input_queue_len==0 || overwrite) { 
+    state->input_queue=value;
+    state->input_queue_len=1;
+    state->status_byte |= STATUS_INPUT_BUFFER_FULL;
+    return 0;
+  } else {
+    KEYBOARD_DEBUG_PRINT("keyboard: PushToOutputQueue Failed - Queue Full\n");
+    return -1;
+  }
+}
+
+// 
+// pull item from inputqueue 
+// returns 0 if successful
+//
+static int PullFromInputQueue(struct vm_device *dev, uchar_t *value) 
+{
+  struct keyboard_internal *state = (struct keyboard_internal *)dev->private_data;
+  if (state->input_queue_len==1) { 
+    *value=state->input_queue;
+    state->input_queue_len=0;
+    state->status_byte &=~STATUS_INPUT_BUFFER_FULL;
+    return 0;
+  } else {
+    KEYBOARD_DEBUG_PRINT("keyboard: PullFromInputQueue Failed - Queue Empty\n");
+    return -1;
+  }
+}
+
 static struct vm_device *demultiplex_injected_key(uchar_t status, uchar_t scancode)
 {
   // this currently does nothing
@@ -56,13 +180,16 @@ void deliver_key_to_vmm(uchar_t status, uchar_t scancode)
 
   KEYBOARD_DEBUG_PRINT("keyboard: injected status 0x%x, and scancode 0x%x\n", status,scancode);
   
-  // This is wrong - the read status should be some combination of the 
-  // status/scancode within the VM, and that of the actual device
-  state->read_status=status;
-  state->read_scancode=scancode;
+  if (    (state->status_byte & STATUS_ENABLED)      // onboard is enabled
+         && (!(state->cmd_byte & CMD_DISABLE)))  {   // keyboard is enabled
 
-  keyboard_interrupt(KEYBOARD_IRQ,dev);
+    PushToInputQueue(dev,scancode,1);
 
+    if (state->cmd_byte & CMD_INTR) { 
+      keyboard_interrupt(KEYBOARD_IRQ,dev);
+    }
+       
+  }
 }
 
 int keyboard_reset_device(struct vm_device * dev)
@@ -70,7 +197,18 @@ int keyboard_reset_device(struct vm_device * dev)
   struct keyboard_internal *data = (struct keyboard_internal *) dev->private_data;
   
   memset(data,0,sizeof(struct keyboard_internal));
+
+  data->cmd_byte =   
+      CMD_INTR        // interrupts on
+    | CMD_MOUSE_INTR  // mouse interupts on
+    | CMD_SYSTEM ;    // self test passed
+                      // PS2, keyboard+mouse enabled, generic translation    
   
+  data->status_byte = 
+      STATUS_SYSTEM     // self-tests passed
+    | STATUS_ENABLED ;  // keyboard ready
+                        // buffers empty, no errors
+
   KEYBOARD_DEBUG_PRINT("keyboard: reset device\n");
  
   return 0;
@@ -79,8 +217,6 @@ int keyboard_reset_device(struct vm_device * dev)
 
 
 
-
-
 int keyboard_start_device(struct vm_device *dev)
 {
   KEYBOARD_DEBUG_PRINT("keyboard: start device\n");
@@ -97,78 +233,218 @@ int keyboard_stop_device(struct vm_device *dev)
 
 
 
-int keyboard_write_control_port(ushort_t port,
-                               void * src, 
-                               uint_t length,
-                               struct vm_device * dev)
+int keyboard_write_command(ushort_t port,
+                          void * src, 
+                          uint_t length,
+                          struct vm_device * dev)
 {
   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
+  uchar_t cmd;
 
-  if (length==1) { 
-    uchar_t data = *((uchar_t*)src); 
-    KEYBOARD_DEBUG_PRINT("keyboard: write of 0x%x to control port\n",data);
-    state->write_status=data;
-    return 1;
-  } else {
-    KEYBOARD_DEBUG_PRINT("keyboard: unknown size write to control port!\n");
+  // Should always be single byte write
+
+  if (length!=1) { 
+    KEYBOARD_DEBUG_PRINT("keyboard: write of >1 bytes to 64h");
     return -1;
   }
+
+  cmd =  *((uchar_t*)src); 
+
+  if (state->state!=NORMAL) { 
+    KEYBOARD_DEBUG_PRINT("keyboard: warning - receiving command on 64h but state!=NORMAL\n");
+  }
+  
+  KEYBOARD_DEBUG_PRINT("keyboard: command 0x%x on 64h\n",cmd);
+
+  switch (cmd) { 
+
+  case 0x20:  // READ COMMAND BYTE (returned in 60h)
+    PushToInputQueue(dev,state->cmd_byte,1);
+    state->state=NORMAL;  // the next read on 0x60 will get the right data
+    break;
+
+  case 0x60:  // WRITE COMMAND BYTE (read from 60h)
+    state->state=WRITING_CMD_BYTE; // we need to make sure we send the next 0x60 byte appropriately
+    break;
+
+  // case 0x90-9f - write to output port  (?)
+
+  case 0xa1: // Get version number
+    PushToInputQueue(dev,0,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xa4:  // is password installed?  send result to 0x60
+    // we don't support passwords
+    PushToOutputQueue(dev,0xf1,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xa5:  // new password will arrive on 0x60
+    state->state=TRANSMIT_PASSWD;
+    break;
+
+  case 0xa6:  // check passwd;
+    // since we do not support passwords, we will simply ignore this
+    // the implication is that any password check immediately succeeds 
+    // with a blank password
+    state->state=NORMAL;
+    break;
+
+  case 0xa7:  // disable mouse
+    state->cmd_byte |= CMD_MOUSE_DISABLE;
+    state->state=NORMAL;
+    break;
+
+  case 0xa8:  // enable mouse
+    state->cmd_byte &= ~CMD_MOUSE_DISABLE;
+    state->state=NORMAL;
+    break;
+
+  case 0xa9:  // mouse interface test  (always succeeds)
+    PushToInputQueue(dev,0,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xaa:  // controller self test (always succeeds)
+    PushToInputQueue(dev,0x55,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xab:  // keyboard interface test (always succeeds)
+    PushToInputQueue(dev,0,1);
+    state->state=NORMAL;
+    break;
+
+  case 0xad:  // disable keyboard
+    state->cmd_byte |= CMD_DISABLE;
+    state->state=NORMAL;
+    break;
+
+  case 0xae:  // enable keyboard
+    state->cmd_byte &= ~CMD_DISABLE;
+    state->state=NORMAL;
+    break;
+
+  case 0xaf:  // get version
+    PushToInputQueue(dev,0x00,1);
+    state->state=NORMAL;
+    break;
+
+  // case c0  read input port ?
+  // case c1  copy input port lsn to status
+  // case c2  copy input port msn to status
+  
+  // case d0 read output port
+  // case d1 write output port
+  // case d2 write keyboard buffer (inject key)
+  // case d3 write mouse buffer (inject mouse)
+  // case d4 write mouse device (command to mouse?)
+
+  // case e0 read test port
+  
+  // case f0..ff pulse output port ?
+
+   
+  default:
+    KEYBOARD_DEBUG_PRINT("keyboard: ignoring command (unimplemented)\n");
+    state->state=NORMAL;
+    break;
+  }
+
+  return 1;
+
 }
 
-int keyboard_read_control_port(ushort_t port,
-                              void * dest, 
-                              uint_t length,
-                              struct vm_device * dev)
+int keyboard_read_status(ushort_t port,
+                        void * dest, 
+                        uint_t length,
+                        struct vm_device * dev)
 {
   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
 
   if (length==1) { 
-    uchar_t data;
-    KEYBOARD_DEBUG_PRINT("keyboard: read from control port: ");
-    data=state->read_status;
-    KEYBOARD_DEBUG_PRINT("0x%x\n",data);
-    memcpy(dest,&data,length);
+    KEYBOARD_DEBUG_PRINT("keyboard: read status (64h): ");
+    *((uchar_t*)dest)=state->status_byte;
+    KEYBOARD_DEBUG_PRINT("0x%x\n",*((uchar_t*)dest));
     return 1;
   } else {
-    KEYBOARD_DEBUG_PRINT("keyboard: unknown size read from control port!\n");
+    KEYBOARD_DEBUG_PRINT("keyboard: >1 byte read for status (64h)\n");
     return -1;
   }
 }
 
-int keyboard_write_data_port(ushort_t port,
-                            void * src, 
-                            uint_t length,
-                            struct vm_device * dev)
+int keyboard_write_output(ushort_t port,
+                         void * src, 
+                         uint_t length,
+                         struct vm_device * dev)
 {
   struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
 
-  if (length==1) { 
-    uchar_t data = *((uchar_t*)src); 
-    KEYBOARD_DEBUG_PRINT("keyboard: write of 0x%x to data port\n",data);
-    state->write_scancode=data;
-    return 1;
-  } else {
-    KEYBOARD_DEBUG_PRINT("keyboard: unknown size write to data port!\n");
+  if (length!=1) { 
+    KEYBOARD_DEBUG_PRINT("keyboard: write of 60h with >1 byte\n");
     return -1;
   }
+
+  uchar_t data=*((uchar_t*)src);
+  
+  switch (state->state) {
+  case WRITING_CMD_BYTE:
+    state->cmd_byte=data;
+    state->state=NORMAL;
+    break;
+  case TRANSMIT_PASSWD:
+    if (data) {
+      //ignore passwd
+    } else {
+      // end of password
+      state->state=NORMAL;
+    }
+    break;
+  case NORMAL:
+    // command is being sent to keyboard controller
+    switch (data) { 
+    case 0xff: // reset
+      PushToInputQueue(dev,0xfa,1); // ack
+      state->state=NORMAL;
+      break;
+    case 0xfe: // resend
+    case 0xfd: // set key type make
+    case 0xfc: // set key typ make/break
+    case 0xfb: // set key type typematic
+    case 0xfa: // set all typematic make/break/typematic
+    case 0xf9: // set all make
+    case 0xf8: // set all make/break
+    case 0xf7: // set all typemaktic
+    case 0xf6: // set defaults
+    case 0xf5: // disable scanning
+    case 0xf4: // enable scanning
+    case 0xf3: // set typematic delay/rate
+    default:
+      KEYBOARD_DEBUG_PRINT("keyboard: unhandled command 0x%x on output buffer (60h)\n");
+      break;
+    }
+  default:
+    KEYBOARD_DEBUG_PRINT("keyboard: unknown state %x on command 0x%x on output buffer (60h)\n",state->state, data);
+  }
+  
+  return 1;
 }
 
-int keyboard_read_data_port(ushort_t port,
-                           void * dest, 
-                           uint_t length,
-                           struct vm_device * dev)
+int keyboard_read_input(ushort_t port,
+                       void * dest, 
+                       uint_t length,
+                       struct vm_device * dev)
 {
-  struct keyboard_internal *state = (struct keyboard_internal *) dev->private_data;
-
   if (length==1) { 
     uchar_t data;
-    KEYBOARD_DEBUG_PRINT("keyboard: read from data port: ");
-    data=state->read_scancode;
+    KEYBOARD_DEBUG_PRINT("keyboard: read from input (60h): ");
+    PullFromInputQueue(dev,&data);
     KEYBOARD_DEBUG_PRINT("0x%x\n",data);
-    memcpy(dest,&data,1);
+    *((uchar_t*)dest)=data;
     return 1;
   } else {
-    KEYBOARD_DEBUG_PRINT("keyboard: unknown size read from data port!\n");
+    KEYBOARD_DEBUG_PRINT("keyboard: unknown size read from input (60h)\n");
     return -1;
   }
 }
@@ -198,8 +474,8 @@ int keyboard_init_device(struct vm_device * dev)
   keyboard_reset_device(dev);
 
   // hook ports
-  dev_hook_io(dev, KEYBOARD_DATA_REG, &keyboard_read_data_port, &keyboard_write_data_port);
-  dev_hook_io(dev, KEYBOARD_CONTROL_REG, &keyboard_read_control_port, &keyboard_write_control_port);
+  dev_hook_io(dev, KEYBOARD_64H, &keyboard_read_status, &keyboard_write_command);
+  dev_hook_io(dev, KEYBOARD_60H, &keyboard_read_input, &keyboard_write_output);
   
   //
   // We do not hook the IRQ here.  Instead, the underlying device driver
@@ -212,9 +488,8 @@ int keyboard_init_device(struct vm_device * dev)
 int keyboard_deinit_device(struct vm_device *dev)
 {
 
-  dev_unhook_io(dev, KEYBOARD_DATA_REG);
-  dev_unhook_io(dev, KEYBOARD_CONTROL_REG);
-
+  dev_unhook_io(dev, KEYBOARD_60H);
+  dev_unhook_io(dev, KEYBOARD_64H);
   keyboard_reset_device(dev);
   return 0;
 }