Advertisement
Geometrian

controller_ps2.cpp

Jul 10th, 2013
401
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.12 KB | None | 0 0
  1. #include "controller_ps2.h"
  2.  
  3. namespace MOSS { namespace Input { namespace Devices {
  4.  
  5.  
  6. ControllerPS2::ControllerPS2(void) {
  7.     ASSERT(sizeof(StatusByte)==1,"Status byte was the wrong size!");
  8.     ASSERT(sizeof(ConfigurationByte)==1,"Configuration byte was the wrong size!");
  9.  
  10.     //keyboard = new DevicePS2Keyboard(this);
  11.     mouse    = new DevicePS2Mouse(this);
  12.  
  13.     //http://wiki.osdev.org/%228042%22_PS/2_Controller#Initialising_the_PS.2F2_Controller (these steps):
  14.  
  15.     //Step 1: Initialize USB controllers (disabling emulation)
  16.     //    If the system is using (typically limited/dodgy) USB Legacy Support it will emulate USB devices as PS/2 devices.  This must be
  17.     //    disabled or we'll just be initializing the emulated PS/2 controller.
  18.     //TODO: this
  19.  
  20.     //Step 2: Determine if the PS/2 controller exists
  21.     //    Before touching the PS/2 controller at all, should determine if it actually exists.  On some systems (e.g. 80x86 Apple machines)
  22.     //    it doesn't exist and any attempt to touch it can result in a system crash.  The correct way to do this is with ACPI.  Check bit
  23.     //    1 (value=2, the "8042" flag) in the "IA PC Boot Architecture Flags" field at offset 109 in the Fixed ACPI Description Table
  24.     //    (FADT).  If this bit is clear then there is no PS/2 Controller to configure (and MOSS exits, since we need one of these).
  25.     //    Otherwise, if the bit is set or the system doesn't support ACPI (no ACPI tables and no FADT) then there is a PS/2 Controller.
  26.     //TODO: this
  27.  
  28.     //Step 3: Disable devices
  29.     {
  30.         //Disable both ports (so that any PS/2 devices can't send data at the wrong time and mess up the initialization)
  31.         send_command(0xAD); //No response expected
  32.         send_command(0xA7); //No response expected (Note: if the controller is a "single channel" device, it will ignore this)
  33.     }
  34.  
  35.     //Step 4: Flush the output buffer
  36.     {
  37.         //Sometimes (e.g. due to interrupt controller initialization causing a lost IRQ) data can be stuck in the PS/2 controller's output
  38.         //buffer.  Now that the devices are disabled (and can't send more data to it), flush the output buffer.
  39.         if (is_outputbuffer_full()) {
  40.             //Read from IO port 0x60 and discard the result.
  41.             recv_data(NULL,0);
  42.         }
  43.     }
  44.  
  45.     //Step 5: Disable all of the controller's IRQs and disable translation of keyboard scancodes
  46.     {
  47.         ConfigurationByte config_byte = _get_configuration_byte();
  48.         config_byte. first_port_interrupts_enabled = false;
  49.         config_byte.second_port_interrupts_enabled = false;
  50.         config_byte.        first_port_translation = false;
  51.         _set_configuration_byte(config_byte);
  52.     }
  53.  
  54.     //Step 6: Perform controller self-test
  55.     {
  56.         send_command(0xAA);
  57.         uint8_t result; recv_data(&result);
  58.         ASSERT(result==0x55,"PS/2 Controller self-test failed!"); //0xFC is failure
  59.     }
  60.  
  61.     //Step 7: Determine if there are two channels
  62.     {
  63.         //Enable second PS/2 port
  64.         send_command(0xA8); //No response expected
  65.  
  66.         ConfigurationByte config_byte = _get_configuration_byte();
  67.         //If the following fails, then can't be a two-port PS/2 controller, since just enabled the second port.
  68.         ASSERT(!config_byte.second_port_clock_disabled,"Not a two-port PS/2 controller!");
  69.  
  70.         //Disable second PS/2 port again
  71.         send_command(0xA7); //No response expected
  72.     }
  73.  
  74.     //Step 8: Perform interface tests (test the PS/2 ports)
  75.     {
  76.         #define CHECK_PORT(PORT)\
  77.             recv_data(&result);\
  78.             switch (result) {\
  79.                 case 0x00: break;\
  80.                 case 0x01: ASSERT(false,"Port "#PORT" interface test failed (clock line stuck low)!");\
  81.                 case 0x02: ASSERT(false,"Port "#PORT" interface test failed (clock line stuck high)!");\
  82.                 case 0x03: ASSERT(false,"Port "#PORT" interface test failed (data line stuck low)!");\
  83.                 case 0x04: ASSERT(false,"Port "#PORT" interface test failed (data line stuck high)!");\
  84.                 default: ASSERT(false,"Port "#PORT" interface test failed (unknown)!");\
  85.             }
  86.  
  87.         uint8_t result;
  88.         //Test first port
  89.         send_command(0xAB);
  90.         CHECK_PORT(1)
  91.  
  92.         //Test second port
  93.         send_command(0xA9);
  94.         CHECK_PORT(2)
  95.  
  96.         #undef CHECK_PORT
  97.     }
  98.  
  99.     //Step 9: Enable devices
  100.     {
  101.         //Enable both ports
  102.         send_command(0xAE); //No response expected
  103.         send_command(0xA8); //No response expected
  104.  
  105.         ConfigurationByte config_byte = _get_configuration_byte();
  106.         config_byte. first_port_interrupts_enabled = false; //TODO: when adding back keyboard, enable this.
  107.         config_byte.second_port_interrupts_enabled =  true;
  108.         _set_configuration_byte(config_byte);
  109.     }
  110.  
  111.     //Step 10: Reset devices
  112.     //    All PS/2 devices should support the "reset" command (which is a command for the device, and not a command for the PS/2 Controller).
  113.     //    To send the reset, just send the byte 0xFF to each (usable) device.  The device/s will respond with 0xFA (success) or 0xFC
  114.     //    (failure), or won't respond at all (no device present). If your code supports "hot-plug PS/2 devices" (see later), then you can
  115.     //    assume each device is "not present" and let the hot-plug code figure out that the device is present if/when 0xFA or 0xFC is
  116.     //    received on a PS/2 port.
  117.     {
  118.         Kernel::terminal->write("Resetting PS/2 devices\n");
  119.         //keyboard->reset();
  120.         mouse->reset();
  121.     }
  122.  
  123.     //Step 11: Enable interrupts
  124.     //    OSDev suggests this as part of step 9, but I think it should come after the devices have been set up properly.
  125.     {
  126.         ConfigurationByte config_byte = _get_configuration_byte();
  127.         config_byte. first_port_interrupts_enabled = false; //TODO: when adding back keyboard, enable this.
  128.         config_byte.second_port_interrupts_enabled =  true;
  129.         _set_configuration_byte(config_byte);
  130.     }
  131.  
  132.     //Kernel::terminal->write("Keyboard using scancode "); Kernel::terminal->write((int)(keyboard->get_scancode())); Kernel::terminal->write("!\n");
  133.     //ASSERT(keyboard->set_scancode(2)==2,"Could not set keyboard scancode to 2!");
  134.     //ASSERT(test(),"PS/2 Controller test failed!");
  135.  
  136.     ASSERT(false,"PS/2 Success!"); //Hang the OS, just to show that it worked.
  137. }
  138. ControllerPS2::~ControllerPS2(void) {
  139.     delete mouse;
  140.     //delete keyboard;
  141. }
  142.  
  143. void ControllerPS2::send_command(uint8_t command) {
  144.     wait_for_inputbuffer_clear();
  145.     IO::send<uint8_t>(CommandRegister,command);
  146. }
  147. void ControllerPS2::send_data(uint8_t data) {
  148.     wait_for_inputbuffer_clear();
  149.     IO::send<uint8_t>(DataPort,data);
  150. }
  151. bool ControllerPS2::recv_data(uint8_t* data, int timeout_counter/*=-1*/) {
  152.     if (timeout_counter==-1) {
  153.         wait_for_outputbuffer_full();
  154.     } else {
  155.         while (timeout_counter>0) {
  156.             if (is_outputbuffer_full()) goto RECV;
  157.             --timeout_counter;
  158.         }
  159.         return false;
  160.     }
  161.     RECV:
  162.     uint8_t result = IO::recv<uint8_t>(DataPort);
  163.     if (data!=NULL) *data=result;
  164.     return true;
  165. }
  166.  
  167. bool ControllerPS2::is_inputbuffer_full(void) const {
  168.     return _get_status_byte().input_buffer_full;
  169. }
  170. bool ControllerPS2::is_outputbuffer_full(void) const {
  171.     return _get_status_byte().output_buffer_full;
  172. }
  173. void ControllerPS2::wait_for_inputbuffer_clear(void) const {
  174.     while (is_inputbuffer_full());
  175. }
  176. void ControllerPS2::wait_for_outputbuffer_full(void) const {
  177.     while (!is_outputbuffer_full());
  178. }
  179.  
  180. ControllerPS2::StatusByte ControllerPS2::_get_status_byte(void) const {
  181.     StatusByte status_byte;
  182.  
  183.     status_byte.data_byte = IO::recv<uint8_t>(StatusRegister);
  184.  
  185.     return status_byte;
  186. }
  187.  
  188. ControllerPS2::ConfigurationByte ControllerPS2::_get_configuration_byte(void) {
  189.     ConfigurationByte config_byte;
  190.  
  191.     send_command(0x20);
  192.     recv_data(&config_byte.data_byte);
  193.  
  194.     return config_byte;
  195. }
  196. void ControllerPS2::_set_configuration_byte(const ConfigurationByte& config_byte) {
  197.     send_command(0x60); //No response expected
  198.     send_data(config_byte.data_byte);
  199. }
  200.  
  201.  
  202. }}}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement