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 "Ps2Controller.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 #ifdef DEBUGGER
32 #ifdef TRACK_PAGE_ALLOCATIONS
33 #include "pedigree/kernel/debugger/commands/AllocationCommand.h"
34 #endif
35 
36 #include "pedigree/kernel/core/SlamAllocator.h"
37 #include "pedigree/kernel/debugger/commands/SlamCommand.h"
38 #endif
39 
40 class Process;
41 
42 #ifdef MEMORY_TRACING
43 extern void toggleTracingAllocations();
44 #endif
45 
46 X86Keyboard::X86Keyboard(Ps2Controller *controller)
47  : m_pPs2Controller(controller), m_Escape(KeymapManager::EscapeNone),
48  m_IrqId(0), m_LedState(0)
49 {
50 }
51 
52 X86Keyboard::~X86Keyboard()
53 {
54 }
55 
57 {
59 
60  // enable data stream
61  uint8_t result = 0;
62  m_pPs2Controller->writeFirstPort(0xF4);
63  m_pPs2Controller->readFirstPort(result);
64  NOTICE("X86Keyboard: 'enable stream' response: " << Hex << result);
65 }
66 
68 {
69  if (m_pPs2Controller->getDebugState())
70  {
71  // Convert the scancode into ASCII and return it
72  uint8_t scancode = m_pPs2Controller->readByte();
73  return scancodeToAscii(scancode);
74  }
75  else
76  {
77  ERROR("Keyboard::getChar() should not be called outside debug mode");
78  return 0;
79  }
80 }
81 
83 {
84  if (m_pPs2Controller->getDebugState())
85  {
86  uint8_t scancode = m_pPs2Controller->readByteNonBlock();
87  if (!scancode)
88  {
89  return 0;
90  }
91 
92  // Convert the scancode into ASCII and return it
93  return scancodeToAscii(scancode);
94  }
95  else
96  {
97  ERROR("Keyboard::getCharNonBlock should not be called outside debug "
98  "mode");
99  return 0;
100  }
101 }
102 
103 void X86Keyboard::setDebugState(bool enableDebugState)
104 {
105  m_pPs2Controller->setDebugState(enableDebugState);
106 }
107 
108 bool X86Keyboard::getDebugState()
109 {
110  return m_pPs2Controller->getDebugState();
111 }
112 
113 char X86Keyboard::scancodeToAscii(uint8_t scancode)
114 {
115  // Get the HID keycode corresponding to the scancode
116  uint8_t keyCode =
118  scancode, m_Escape);
119  if (!keyCode)
120  return 0;
121 
122  uint64_t key = 0;
123  // Let KeymapManager handle the modifiers
124  if (!KeymapManager::instance().handleHidModifier(
125  keyCode, !(scancode & 0x80)) &&
126  !(scancode & 0x80))
128  keyCode); // Get the actual key
129 
130  // Check for special keys
131  if (key & Keyboard::Special)
132  {
133  char a = key & 0xFF, b = (key >> 8) & 0xFF, c = (key >> 16) & 0xFF,
134  d = (key >> 24) & 0xFF;
135  // Up
136  if (a == 'u' && b == 'p')
137  return 'j';
138  // Down
139  if (a == 'd' && b == 'o' && c == 'w' && d == 'n')
140  return 'k';
141  // PageUp
142  if (a == 'p' && b == 'g' && c == 'u' && d == 'p')
143  return '\b';
144  // PageDown
145  if (a == 'p' && b == 'g' && c == 'd' && d == 'n')
146  return ' ';
147  }
148  // Discard non-ASCII keys
149  if ((key & Keyboard::Special) || ((key & 0xFFFFFFFF) > 0x7f))
150  return 0;
151  return static_cast<char>(key & 0x7f);
152 }
153 
155 {
156  return m_LedState;
157 }
158 
159 void X86Keyboard::setLedState(char state)
160 {
161  m_LedState = state;
162 
163  m_pPs2Controller->writeFirstPort(0xED);
164  m_pPs2Controller->writeFirstPort(state);
165 
166  uint8_t response = 0;
167  if (m_pPs2Controller->readFirstPort(response))
168  {
169  NOTICE("X86Keyboard: setLedState response: " << Hex << response);
170  }
171  else
172  {
173  ERROR("X86Keyboard: failed to read response in setLedState");
174  }
175 }
176 
177 void X86Keyboard::startReaderThread()
178 {
179  Process *pProcess =
180  Processor::information().getCurrentThread()->getParent();
181  Thread *pThread = new Thread(pProcess, readerThreadTrampoline, this);
182  pThread->detach();
183 
184  // Now that we're listening - enable IRQs from the keyboard
185  m_pPs2Controller->setIrqEnable(true, false);
186 }
187 
188 int X86Keyboard::readerThreadTrampoline(void *param)
189 {
190  X86Keyboard *instance = reinterpret_cast<X86Keyboard *>(param);
191  instance->readerThread();
192 }
193 
194 void X86Keyboard::readerThread()
195 {
196  while (true)
197  {
198  uint8_t scancode;
199  if (!m_pPs2Controller->readFirstPort(scancode))
200  {
201  continue;
202  }
203  if (scancode == 0xFA || scancode == 0xFE)
204  {
205  // ignore for now
206  continue;
207  }
208 
209  // Check for keys with special functions
210 #ifdef DEBUGGER
211 #if CRIPPLINGLY_VIGILANT
212  if (scancode == 0x43) // F9
213  SlamAllocator::instance().setVigilance(true);
214  if (scancode == 0x44) // F10
215  SlamAllocator::instance().setVigilance(false);
216 #endif
217  if (scancode == 0x57) // F11
218  {
219 #ifdef MEMORY_TRACING
220  WARNING("Toggling allocation tracing.");
221  toggleTracingAllocations();
222 #else
223 #ifdef TRACK_PAGE_ALLOCATIONS
224  g_AllocationCommand.checkpoint();
225 #endif
226  g_SlamCommand.clean();
227 #endif
228 
229  continue;
230  }
231  if (scancode == 0x58) // F12
232  {
233  FATAL("User-induced breakpoint.");
234  }
235 #endif
236 
237  // Check for LED manipulation
238  if (scancode & 0x80)
239  {
240  uint8_t code = scancode & ~0x80;
241  if (code == 0x3A)
242  {
243  DEBUG_LOG("X86Keyboard: Caps Lock toggled");
244  m_LedState ^= Keyboard::CapsLock;
245  setLedState(m_LedState);
246  }
247  else if (code == 0x45)
248  {
249  DEBUG_LOG("X86Keyboard: Num Lock toggled");
250  m_LedState ^= Keyboard::NumLock;
251  setLedState(m_LedState);
252  }
253  else if (code == 0x46)
254  {
255  DEBUG_LOG("X86Keyboard: Scroll Lock toggled");
256  m_LedState ^= Keyboard::ScrollLock;
257  setLedState(m_LedState);
258  }
259  }
260 
262  scancode & 0x7F, scancode & 0x80);
263 
264  // Get the HID keycode corresponding to the scancode
265  uint8_t keyCode =
267  scancode, m_Escape);
268  if (!keyCode)
269  {
270  ERROR(
271  "X86Keyboard: failed to translate scancode " << Hex
272  << scancode);
273  continue;
274  }
275 
276  // Send the keycode to the HID input manager
277  if (scancode & 0x80)
278  HidInputManager::instance().keyUp(keyCode);
279  else
281  }
282 }
char scancodeToAscii(uint8_t scancode)
Converts a scancode into an ASCII character (for use in debug state)
virtual void setLedState(char state)
virtual char getCharNonBlock()
void machineKeyUpdate(uint8_t scancode, bool bKeyUp)
Called whenever a machine-specific key scancode comes in.
virtual char getLedState()
virtual void setLedState(char state)
Definition: Keyboard.cc:30
uint64_t resolveHidKeycode(uint8_t keyCode)
void keyUp(uint8_t keyCode)
Called when a key transitions to the "up" state.
static KeymapManager & instance()
Singleton design.
Definition: KeymapManager.h:41
static ProcessorInformation & information()
Definition: Processor.cc:45
static HidInputManager & instance()
Singleton design.
void keyDown(uint8_t keyCode)
Called when a key transitions to the "down" state.
uint8_t convertPc102ScancodeToHidKeycode(uint8_t scancode, EscapeState &escape)
Converts a pc102 scancode into a HID keycode.
#define WARNING(text)
Definition: Log.h:78
virtual char getChar()
#define NOTICE(text)
Definition: Log.h:74
Definition: Log.h:136
virtual void setDebugState(bool enableDebugState)
static InputManager & instance()
Singleton design.
Definition: InputManager.h:107
Definition: Thread.h:54
#define ERROR(text)
Definition: Log.h:82
bool detach()
Definition: Thread.cc:885
#define FATAL(text)
Definition: Log.h:89
virtual void initialise()
Initialises the device.
#define DEBUG_LOG(text)
Definition: Log.h:69