The Pedigree Project 0.1
mach_pc/Keyboard.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Keyboard.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/machine/HidInputManager.h"
24#include "pedigree/kernel/machine/InputManager.h"
25#include "pedigree/kernel/machine/KeymapManager.h"
26#include "pedigree/kernel/process/Thread.h"
27#include "pedigree/kernel/processor/Processor.h"
28#include "pedigree/kernel/processor/ProcessorInformation.h"
29#include "pedigree/kernel/utilities/new"
30
31#include "Ps2Controller.h"
32
33#if DEBUGGER
34#if TRACK_PAGE_ALLOCATIONS
35#include "pedigree/kernel/debugger/commands/AllocationCommand.h"
36#endif
37
38#include "pedigree/kernel/core/SlamAllocator.h"
39#include "pedigree/kernel/debugger/commands/SlamCommand.h"
40#endif
41
42class Process;
43
44#if MEMORY_TRACING
45extern void toggleTracingAllocations();
46#endif
47
48X86Keyboard::X86Keyboard(Ps2Controller* controller)
49 : m_pPs2Controller(controller),
50 m_Escape(KeymapManager::EscapeNone),
51 m_IrqId(0),
52 m_LedState(0),
53 m_LedCommandState(LedIdle),
54 m_LedSent(0),
55 m_LedRetries(0),
56 m_LedLock(),
57 m_ReaderThread() {}
58
59X86Keyboard::~X86Keyboard() {
60 stopReaderThread();
61}
62
65
66 // enable data stream
67 uint8_t result = 0;
70 NOTICE("X86Keyboard: 'enable stream' response: " << Hex << result);
71}
72
75 // Convert the scancode into ASCII and return it
76 uint8_t scancode = m_pPs2Controller->readByte();
77 return scancodeToAscii(scancode);
78 } else {
79 ERROR("Keyboard::getChar() should not be called outside debug mode");
80 return 0;
81 }
82}
83
86 uint8_t scancode = m_pPs2Controller->readByteNonBlock();
87 if (!scancode) {
88 return 0;
89 }
90
91 // Convert the scancode into ASCII and return it
92 return scancodeToAscii(scancode);
93 } else {
94 ERROR(
95 "Keyboard::getCharNonBlock should not be called outside debug "
96 "mode");
97 return 0;
98 }
99}
100
101void X86Keyboard::setDebugState(bool enableDebugState) {
102 m_pPs2Controller->setDebugState(enableDebugState);
103}
104
105bool X86Keyboard::getDebugState() {
107}
108
109char X86Keyboard::scancodeToAscii(uint8_t scancode) {
110 // Get the HID keycode corresponding to the scancode
112 if (!keyCode)
113 return 0;
114
115 uint64_t key = 0;
116 // Let KeymapManager handle the modifiers
117 if (!KeymapManager::instance().handleHidModifier(keyCode, !(scancode & 0x80)) &&
118 !(scancode & 0x80))
119 key = KeymapManager::instance().resolveHidKeycode(keyCode); // Get the actual key
120
121 // Check for special keys
122 if (key & Keyboard::Special) {
123 char a = key & 0xFF, b = (key >> 8) & 0xFF, c = (key >> 16) & 0xFF, d = (key >> 24) & 0xFF;
124 // Up
125 if (a == 'u' && b == 'p')
126 return 'j';
127 // Down
128 if (a == 'd' && b == 'o' && c == 'w' && d == 'n')
129 return 'k';
130 // PageUp
131 if (a == 'p' && b == 'g' && c == 'u' && d == 'p')
132 return '\b';
133 // PageDown
134 if (a == 'p' && b == 'g' && c == 'd' && d == 'n')
135 return ' ';
136 }
137 // Discard non-ASCII keys
138 if ((key & Keyboard::Special) || ((key & 0xFFFFFFFF) > 0x7f))
139 return 0;
140 return static_cast<char>(key & 0x7f);
141}
142
144 LockGuard<Mutex> guard(m_LedLock);
145 return m_LedState;
146}
147
148void X86Keyboard::setLedState(char state) {
149 updateLedState(state, false);
150}
151
152void X86Keyboard::updateLedState(char state, bool toggle) {
153 LockGuard<Mutex> guard(m_LedLock);
154 m_LedState = (toggle ? m_LedState ^ state : state) & 7;
155 if (m_LedCommandState == LedIdle) {
156 m_LedCommandState = LedCommandAck;
157 m_LedRetries = 0;
159 }
160}
161
162void X86Keyboard::handleLedResponse(uint8_t response) {
163 // Command replies share the scancode FIFO. Only the reader consumes it,
164 // so queued key releases cannot be mistaken for a synchronous LED reply.
165 LockGuard<Mutex> guard(m_LedLock);
166 if (m_LedCommandState == LedIdle) {
167 return;
168 }
169
170 if (response == 0xFE) {
171 if (m_LedRetries++ == 3) {
172 m_LedCommandState = LedIdle;
173 ERROR("X86Keyboard: LED command retry limit reached");
174 return;
175 }
176 m_pPs2Controller->writeFirstPort(m_LedCommandState == LedCommandAck ? 0xED : m_LedSent);
177 return;
178 }
179
180 m_LedRetries = 0;
181 if (m_LedCommandState == LedCommandAck) {
182 m_LedSent = m_LedState;
183 m_LedCommandState = LedDataAck;
185 } else if (m_LedSent != m_LedState) {
186 m_LedCommandState = LedCommandAck;
188 } else {
189 m_LedCommandState = LedIdle;
190 }
191}
192
193void X86Keyboard::startReaderThread() {
194 if (m_ReaderThread) {
195 FATAL("X86Keyboard reader started more than once.");
196 }
197
198 Process* pProcess = Processor::information().getCurrentThread()->getParent();
199 Thread* pThread = new Thread(pProcess, readerThreadTrampoline, this);
200 pThread->setName("X86 keyboard reader");
201 m_ReaderThread.adopt(pThread);
202
203 // Now that we're listening - enable IRQs from the keyboard
204 m_pPs2Controller->setIrqEnable(true, false);
205}
206
207void X86Keyboard::stopReaderThread() {
208 m_ReaderThread.stop();
209}
210
211int X86Keyboard::readerThreadTrampoline(void* param) {
212 X86Keyboard* instance = reinterpret_cast<X86Keyboard*>(param);
213 instance->readerThread();
214 return 0;
215}
216
217void X86Keyboard::readerThread() {
218 while (true) {
219 uint8_t scancode;
220 if (!m_pPs2Controller->readFirstPort(scancode)) {
221 Thread* thread = Processor::information().getCurrentThread();
222 if (thread && thread->getUnwindState() != Thread::Continue) {
223 return;
224 }
225 continue;
226 }
227 if (scancode == 0xFA || scancode == 0xFE) {
228 handleLedResponse(scancode);
229 continue;
230 }
231
232 // Check for keys with special functions
233#if DEBUGGER
234#if CRIPPLINGLY_VIGILANT
235 if (scancode == 0x43) // F9
236 SlamAllocator::instance().setVigilance(true);
237 if (scancode == 0x44) // F10
238 SlamAllocator::instance().setVigilance(false);
239#endif
240 if (scancode == 0x57) // F11
241 {
242#if MEMORY_TRACING
243 WARNING("F11: Toggling allocation tracing.");
244 toggleTracingAllocations();
245#else
246#if TRACK_PAGE_ALLOCATIONS
247 NOTICE("F11: Triggering allocation checkpoint.");
248 g_AllocationCommand.checkpoint();
249#endif
250 NOTICE("F11: Cleaning SLAM.");
251 g_SlamCommand.clean();
252#endif
253
254 continue;
255 }
256 if (scancode == 0x58) // F12
257 {
258 FATAL("User-induced breakpoint.");
259 }
260#endif
261
262 // Check for LED manipulation
263 if (scancode & 0x80) {
264 uint8_t code = scancode & ~0x80;
265 if (code == 0x3A) {
266 DEBUG_LOG("X86Keyboard: Caps Lock toggled");
267 updateLedState(Keyboard::CapsLock, true);
268 } else if (code == 0x45) {
269 DEBUG_LOG("X86Keyboard: Num Lock toggled");
270 updateLedState(Keyboard::NumLock, true);
271 } else if (code == 0x46) {
272 DEBUG_LOG("X86Keyboard: Scroll Lock toggled");
273 updateLedState(Keyboard::ScrollLock, true);
274 }
275 }
276
277 InputManager::instance().machineKeyUpdate(scancode & 0x7F, scancode & 0x80);
278
279 // Get the HID keycode corresponding to the scancode
280 uint8_t keyCode =
282 if (!keyCode) {
283 ERROR("X86Keyboard: failed to translate scancode " << Hex << scancode);
284 continue;
285 }
286
287 // Send the keycode to the HID input manager
288 if (scancode & 0x80)
290 else
292 }
293}
void keyDown(uint8_t keyCode, const void *source=nullptr)
Called when a key transitions to the "down" state.
static HidInputManager & instance()
Singleton design.
void keyUp(uint8_t keyCode, const void *source=nullptr)
Called when a key transitions to the "up" state.
static InputManager & instance()
Singleton design.
void machineKeyUpdate(uint8_t scancode, bool bKeyUp)
Called whenever a machine-specific key scancode comes in.
static KeymapManager & instance()
Singleton design.
uint8_t convertPc102ScancodeToHidKeycode(uint8_t scancode, EscapeState &escape)
Converts a pc102 scancode into a HID keycode.
uint64_t resolveHidKeycode(uint8_t keyCode)
Process * getParent()
Definition Process.h:620
static ProcessorInformation & information()
uint8_t readByte()
Reads a single byte from the PS/2 controller by polling.
void writeFirstPort(uint8_t byte)
Send a byte to the first port, dropping it if the controller times out.
bool getDebugState() const
Gets the debug state.
bool readFirstPort(uint8_t &byte, bool block=true)
Reads a single byte from the first port.
EXPORTED_PUBLIC void setIrqEnable(bool firstEnabled, bool secondEnabled)
Enables/disables IRQs for the first or second ports.
void setDebugState(bool debugState)
Selects trap-safe polling without changing controller IRQ state.
@ Continue
No unwind necessary, carry on as normal.
Definition Thread.h:517
UnwindType getUnwindState()
Definition Thread.h:535
KeymapManager::EscapeState m_Escape
The current escape state.
char m_LedState
Current LED state.
Ps2Controller * m_pPs2Controller
Parent PS/2 controller.
virtual char getLedState()
OwnedThread m_ReaderThread
The controller buffer may block this worker until teardown wakes it.
virtual void setDebugState(bool enableDebugState)
virtual void initialise()
Initialises the device.
virtual char getCharNonBlock()
virtual void setLedState(char state)
char scancodeToAscii(uint8_t scancode)
Converts a scancode into an ASCII character (for use in debug state)
virtual char getChar()
@ Hex
Definition Log.h:124