You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			154 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
			
		
		
	
	
			154 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
| #include "ps2controller.h"
 | |
| 
 | |
| #include "../arch/system.h"
 | |
| #include "../arch/core_interrupt.h"
 | |
| #include "../arch/ioport.h"
 | |
| #include "../compiler/fix.h"
 | |
| #include "../debug/output.h"
 | |
| #include "keydecoder.h"
 | |
| 
 | |
| namespace PS2Controller {
 | |
| 
 | |
| // I/O Ports of the PS2 Controller
 | |
| static const IOPort ctrl_port(
 | |
|     0x64);  ///< Access status- (read) and command (write) register
 | |
| static const IOPort data_port(0x60);  ///< Access PS/2 device [keyboard] output-
 | |
|                                       ///< (read) and input (write) buffer
 | |
| /* The buffers are used to communicate with the controller or the connected
 | |
|  * PS/2 devices alike:
 | |
|  *  - For the output buffer, the controller decides to which PS/2 device the
 | |
|  *    data gets forwarded to -- by default it is the primary PS/2 device
 | |
|  * (keyboard).
 | |
|  *  - The source device from which the data was gathered can be determined using
 | |
|  *    the status flag (\ref IS_MOUSE).
 | |
|  *
 | |
|  * Please also note, that the naming of the buffer may be a bit contra-intuitive
 | |
|  * since it is the perspective of the PS/2 controller due to historical reasons.
 | |
|  */
 | |
| 
 | |
| // Key decoder (stores the state of the modifier keys)
 | |
| static KeyDecoder key_decoder;
 | |
| 
 | |
| // To store the current state of the Keyboard LEDs
 | |
| static uint8_t leds = 0;
 | |
| 
 | |
| /*! \brief Flags in the PS/2 controller status register
 | |
|  */
 | |
| enum Status {
 | |
|   HAS_OUTPUT = 1 << 0,     ///< Output buffer non-empty?
 | |
|   INPUT_PENDING = 1 << 1,  ///< Is input buffer full?
 | |
|   SYSTEM_FLAG = 1 << 2,    ///< set on soft reset, cleared on power up
 | |
|   IS_COMMAND = 1 << 3,     ///< Is command Byte? (otherwise data)
 | |
|   IS_MOUSE = 1 << 5,       ///< Mouse output has data
 | |
|   TIMEOUT_ERROR = 1 << 6,  ///< Timeout error
 | |
|   PARITY_ERROR = 1 << 7    ///< Parity error
 | |
| };
 | |
| 
 | |
| /*! \brief Commands to be send to the Keyboard
 | |
|  */
 | |
| enum KeyboardCommand : uint8_t {
 | |
|   KEYBOARD_SET_LED =
 | |
|       0xed,  ///< Set the LED (according to the following parameter byte)
 | |
|   KEYBOARD_SEND_ECHO = 0xee,    ///< Send an echo packet
 | |
|   KEYBOARD_SET_SPEED = 0xf3,    ///< Set the repeat rate (according to the
 | |
|                                 ///< following parameter byte)
 | |
|   KEYBOARD_ENABLE = 0xf4,       ///< Enable Keyboard
 | |
|   KEYBOARD_DISABLE = 0xf5,      ///< Disable Keyboard
 | |
|   KEYBOARD_SET_DEFAULT = 0xf6,  ///< Load defaults
 | |
| };
 | |
| 
 | |
| /*! \brief Replies
 | |
|  */
 | |
| enum Reply {
 | |
|   ACK = 0xfa,     ///< Acknowledgement
 | |
|   RESEND = 0xfe,  ///< Request to resend (not required to implement)
 | |
|   ECHO = 0xee     ///< Echo answer
 | |
| };
 | |
| 
 | |
| /*! \brief Commands for the PS/2 Controller
 | |
|  *
 | |
|  * These commands are processed by the controller and *not* send to
 | |
|  * keyboard/mouse. They have to be written into the command register.
 | |
|  */
 | |
| enum ControllerCommand {
 | |
|   CONTROLLER_GET_COMMAND_BYTE = 0x20,  ///< Read Command Byte of PS/2 Controller
 | |
|   CONTROLLER_SET_COMMAND_BYTE =
 | |
|       0x60,                         ///< Write Command Byte of PS/2 Controller
 | |
|   CONTROLLER_MOUSE_DISABLE = 0xa7,  ///< Disable mouse interface
 | |
|   CONTROLLER_MOUSE_ENABLE = 0xa8,   ///< Enable mouse interface
 | |
|   CONTROLLER_KEYBOARD_DISABLE = 0xad,  ///< Disable keyboard interface
 | |
|   CONTROLLER_KEYBOARD_ENABLE = 0xae,   ///< Enable keyboard interface
 | |
|   CONTROLLER_SEND_TO_MOUSE = 0xd4,     ///< Send parameter to mouse device
 | |
| };
 | |
| 
 | |
| /*! \brief Send a command or data to a connected PS/2 device
 | |
|  *
 | |
|  * The value must only be written into the input buffer after the previously
 | |
|  * written values have been fetched (\ref INPUT_PENDING in the status register).
 | |
|  *
 | |
|  *  \todo(11) Implement method
 | |
|  *
 | |
|  *  \param value data to be sent
 | |
|  */
 | |
|  static void sendData(uint8_t value) {
 | |
|   
 | |
|   if (!(ctrl_port.inb()&HAS_OUTPUT)) {
 | |
|     data_port.outb(value);
 | |
|   }
 | |
|   (void)value;
 | |
| }
 | |
| 
 | |
| void init() {
 | |
|   // Switch all LEDs off (on many PCs NumLock is turned on after power up)
 | |
|   setLed(LED_CAPS_LOCK, false);
 | |
|   setLed(LED_SCROLL_LOCK, false);
 | |
|   setLed(LED_NUM_LOCK, false);
 | |
| 
 | |
|   // Set to maximum speed & minimum delay
 | |
|   setRepeatRate(SPEED_30_0CPS, DELAY_250MS);
 | |
| }
 | |
| 
 | |
| bool fetch(Key &pressed) {
 | |
|   uint8_t status_reg = ctrl_port.inb();
 | |
|   if(!(status_reg & HAS_OUTPUT) )
 | |
|     return false;
 | |
|   DBG_VERBOSE << "status: " << hex << static_cast<int>(status_reg) << "\n" << flush;
 | |
|   
 | |
|   uint8_t out_buffer = data_port.inb();
 | |
|   DBG_VERBOSE << "scancode: " << hex << static_cast<int>(out_buffer) << "\n" << flush;
 | |
|   
 | |
|   if (status_reg & IS_MOUSE)
 | |
|       return false;
 | |
| 
 | |
|   pressed = key_decoder.decode(out_buffer);
 | |
| 
 | |
|   if (pressed.ctrl() && pressed.alt() && pressed.scancode == Key::KEY_DEL)
 | |
|       System::reboot();
 | |
|   
 | |
|   if (pressed.alt() || pressed.ctrl() || !pressed.valid())
 | |
|     return false;
 | |
|   else
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void setRepeatRate(Speed speed, Delay delay) {
 | |
|   (void)speed;
 | |
|   (void)delay;
 | |
| }
 | |
| 
 | |
| void setLed(enum LED led, bool on) {
 | |
|   if (on) {
 | |
|     leds |= led;
 | |
|   } else {
 | |
|     leds &= ~led;
 | |
|   }
 | |
|   sendData(KEYBOARD_SET_LED);  // Command for the Keyboard
 | |
|   sendData(leds);              // Parameter
 | |
| }
 | |
| 
 | |
| void drainBuffer() {
 | |
|     while(ctrl_port.inb() & HAS_OUTPUT)
 | |
|         data_port.inb();
 | |
| }
 | |
| }  // namespace PS2Controller
 |