/* * \brief Interface to i8042 controller * \author Norman Feske * \date 2007-09-21 */ /* * Copyright (C) 2007-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ #ifndef _I8042_H_ #define _I8042_H_ #include #include #include #include "serial_interface.h" class I8042 { /* * XXX: To be moved into the implementation file */ enum Register { REG_DATA = 0x60, REG_STATUS = 0x64, REG_COMMAND = 0x64, }; enum Flag { /* status register */ STAT_OBF = 0x01, STAT_IBF = 0x02, STAT_04 = 0x04, STAT_08 = 0x08, STAT_10 = 0x10, STAT_AUX_DATA = 0x20, STAT_40 = 0x40, STAT_80 = 0x80, /* control register */ CTRL_KBD_INT = 0x01, CTRL_AUX_INT = 0x02, CTRL_04 = 0x04, CTRL_08 = 0x08, CTRL_KBD_DISABLE = 0x10, CTRL_AUX_DISABLE = 0x20, CTRL_XLATE = 0x40, CTRL_80 = 0x80, }; enum Command { CMD_READ = 0x20, CMD_WRITE = 0x60, CMD_TEST = 0xaa, CMD_AUX_DISABLE = 0xa7, CMD_AUX_ENABLE = 0xa8, CMD_AUX_TEST = 0xa9, CMD_KBD_DISABLE = 0xad, CMD_KBD_ENABLE = 0xae, CMD_KBD_TEST = 0xab, CMD_AUX_WRITE = 0xd4, }; enum Return { RET_TEST_OK = 0x55, RET_KBD_TEST_OK = 0x00, RET_AUX_TEST_OK = 0x00, }; class _Channel : public Serial_interface, public Genode::Ring_buffer { private: I8042 &_i8042; bool _aux; public: /** * Constructor * * \param i8042 controller * \param aux channel type is aux * * The 'aux' parameter is used to direct write operations. Read * operations always block on the channel's ring buffer, which * gets fed from outside this class. */ _Channel(I8042 &i8042, bool aux) : _i8042(i8042), _aux(aux) { } /** * Read all available data from controller */ void flush_read() { Genode::Lock::Guard guard(_i8042._lock); while (_i8042._output_buffer_full()) _i8042._read_and_route(); } unsigned char read() { while (empty()) flush_read(); return get(); } void write(unsigned char value) { Genode::Lock::Guard guard(_i8042._lock); if (_aux) _i8042._command(CMD_AUX_WRITE); _i8042._data(value); } bool data_read_ready() { flush_read(); return !empty(); } }; private: Genode::Io_port_connection _data_port; /* data port */ Genode::Io_port_connection _stat_port; /* status/command port */ bool _kbd_xlate; /* translation mode to scan-code set 1 */ /** * Read controller status */ unsigned char _status() { return _stat_port.inb(REG_STATUS); } /** * Read data from controller */ unsigned char _data() { return _data_port.inb(REG_DATA); } /** * Issue command to controller */ void _command(unsigned char cmd) { while (_input_buffer_full()); _stat_port.outb(REG_STATUS, cmd); } /** * Send data to controller */ void _data(unsigned char value) { while (_input_buffer_full()); _data_port.outb(REG_DATA, value); } /** * Convenience functions for accessing the controller status */ bool _output_buffer_full() { return _status() & STAT_OBF; } bool _input_buffer_full() { return _status() & STAT_IBF; } bool _aux_data_pending() { return _status() & STAT_AUX_DATA; } /** * Wait for data and read */ unsigned char _wait_data() { while (!_output_buffer_full()); return _data(); } /** * Read character from controller and route it to serial channel */ void _read_and_route() { while (!_output_buffer_full()); if (_aux_data_pending()) _aux_interface.add(_data()); else _kbd_interface.add(_data()); } _Channel _kbd_interface; _Channel _aux_interface; /* * Both serial interfaces may be used by different * threads, e.g., interrupt handlers. Hence, controller * transactions (read/write sequences) must be protected * with a lock. */ Genode::Lock _lock; public: /** * Constructor */ I8042() : _data_port(REG_DATA, 1), _stat_port(REG_STATUS, 1), _kbd_interface(*this, false), _aux_interface(*this, true) { reset(); } /** * Test and initialize controller */ void reset() { unsigned char configuration; unsigned char ret; /* disable keyboard and mouse */ _command(CMD_KBD_DISABLE); _command(CMD_AUX_DISABLE); /* read remaining data in controller */ while (_output_buffer_full()) _data(); /* get configuration (can change during the self tests) */ _command(CMD_READ); configuration = _wait_data(); /* query xlate bit */ _kbd_xlate = !!(configuration & CTRL_XLATE); /* run self tests */ _command(CMD_TEST); if ((ret = _wait_data()) != RET_TEST_OK) { Genode::printf("i8042: self test failed (%x)\n", ret); return; } _command(CMD_KBD_TEST); if ((ret = _wait_data()) != RET_KBD_TEST_OK) { Genode::printf("i8042: kbd test failed (%x)\n", ret); return; } _command(CMD_AUX_TEST); if ((ret = _wait_data()) != RET_AUX_TEST_OK) { Genode::printf("i8042: aux test failed (%x)\n", ret); return; } /* enable interrupts for keyboard and mouse at the controller */ configuration |= CTRL_KBD_INT | CTRL_AUX_INT; _command(CMD_WRITE); _data(configuration); /* enable keyboard and mouse */ _command(CMD_KBD_ENABLE); _command(CMD_AUX_ENABLE); } /** * Return true if controller operates in translation mode * * If xlate is set, the controller keyboard events to scan-code set 1. * We just detect the setting as defined by the BIOS. If xlate is * clear, we have to decode scan-code-set 2 packets. */ bool kbd_xlate() { return _kbd_xlate; } /** * Request serial keyboard interface */ Serial_interface *kbd_interface() { return &_kbd_interface; } /** * Request serial mouse interface */ Serial_interface *aux_interface() { return &_aux_interface; } }; #endif