FabGL
ESP32 Display Controller and Graphics Library
i8042.cpp
1 /*
2  Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3  Copyright (c) 2019-2021 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6 
7 * Please contact fdivitto2013@gmail.com if you need a commercial license.
8 
9 
10 * This library and related software is available under GPL v3.
11 
12  FabGL is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  FabGL is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 
27 #include "i8042.h"
28 
29 
30 
31 namespace fabgl {
32 
33 
34 // Controller status bits
35 #define STATUS_OBF 0x01 // 0 : Output Buffer Full (0 = output buffer empty)
36 #define STATUS_IBF 0x02 // 1 : Input Buffer Full (0 = input buffer empty)
37 #define STATUS_SYSFLAG 0x04 // 2 : 0 = power on reset, 1 = diagnostic ok
38 #define STATUS_CMD 0x08 // 3 : Command or Data, 0 = write to port 0 (0x60), 1 = write to port 1 (0x64)
39 #define STATUS_INH 0x10 // 4 : Inhibit Switch, 0 = Keyboard inhibited, 1 = Keyboard not inhibited
40 #define STATUS_AOBF 0x20 // 5 : Auxiliary Output Buffer Full, 0 = keyboard data, 1 = mouse data
41 #define STATUS_TIMEOUT 0x40 // 6 : 1 = Timeout Error
42 #define STATUS_PARITY_ERR 0x80 // 7 : 1 = Parity Error
43 
44 
45 // Controller commands
46 #define CTRLCMD_NONE 0x00
47 #define CTRLCMD_GET_COMMAND_BYTE 0x20
48 #define CTRLCMD_READ_CONTROLLER_RAM_BEGIN 0x21
49 #define CTRLCMD_READ_CONTROLLER_RAM_END 0x3f
50 #define CTRLCMD_WRITE_COMMAND_BYTE 0x60
51 #define CTRLCMD_WRITE_CONTROLLER_RAM_BEGIN 0x61
52 #define CTRLCMD_WRITE_CONTROLLER_RAM_END 0x7f
53 #define CTRLCMD_DISABLE_MOUSE_PORT 0xa7
54 #define CTRLCMD_ENABLE_MOUSE_PORT 0xa8
55 #define CTRLCMD_TEST_MOUSE_PORT 0xa9
56 #define CTRLCMD_SELF_TEST 0xaa
57 #define CTRLCMD_TEST_KEYBOARD_PORT 0xab
58 #define CTRLCMD_DISABLE_KEYBOARD 0xad
59 #define CTRLCMD_ENABLE_KEYBOARD 0xae
60 #define CTRLCMD_READ_INPUT_PORT 0xc0
61 #define CTRLCMD_READ_OUTPUT_PORT 0xd0
62 #define CTRLCMD_WRITE_OUTPUT_PORT 0xd1
63 #define CTRLCMD_WRITE_KEYBOARD_OUTPUT_BUFFER 0xd2
64 #define CTRLCMD_WRITE_MOUSE_OUTPUT_BUFFER 0xd3
65 #define CTRLCMD_WRITE_TO_MOUSE 0xd4
66 #define CTRLCMD_SYSTEM_RESET 0xfe
67 
68 
69 // Command byte bits
70 #define CMDBYTE_ENABLE_KEYBOARD_IRQ 0x01 // 0 : 1 = Keyboard output buffer full causes interrupt (IRQ 1)
71 #define CMDBYTE_ENABLE_MOUSE_IRQ 0x02 // 1 : 1 = Mouse output buffer full causes interrupt (IRQ 12)
72 #define CMDBYTE_SYSFLAG 0x04 // 2 : 1 = System flag after successful controller self-test
73 #define CMDBYTE_UNUSED1 0x08 // 3 : unused (must be 0)
74 #define CMDBYTE_DISABLE_KEYBOARD 0x10 // 4 : 1 = Disable keyboard by forcing the keyboard clock low
75 #define CMDBYTE_DISABLE_MOUSE 0x20 // 5 : 1 = Disable mouse by forcing the mouse serial clock line low
76 #define CMDBYTE_STD_SCAN_CONVERSION 0x40 // 6 : 1 = Standard Scan conversion
77 #define CMDBYTE_UNUSED2 0x80 // 7 : unused (must be 0)
78 
79 
80 i8042::i8042()
81 {
82 }
83 
84 
85 i8042::~i8042()
86 {
87 }
88 
89 
90 void i8042::init()
91 {
92  // because mouse is optional, don't re-try if it is not found (to speed-up boot)
94 
95  // keyboard configured on port 0, and optionally mouse on port 1
96  if (!PS2Controller::initialized())
98  else
99  m_PS2Controller.keyboard()->enableVirtualKeys(false, false);
100  m_keyboard = m_PS2Controller.keyboard();
101  m_mouse = m_PS2Controller.mouse();
102 
103  reset();
104 }
105 
106 
107 void i8042::reset()
108 {
109  m_STATUS = STATUS_SYSFLAG | STATUS_INH;
110  m_DBBOUT = 0;
111  m_DBBIN = 0;
112  m_commandByte = CMDBYTE_ENABLE_KEYBOARD_IRQ | CMDBYTE_ENABLE_MOUSE_IRQ | CMDBYTE_SYSFLAG | CMDBYTE_STD_SCAN_CONVERSION | CMDBYTE_DISABLE_MOUSE;
113 
114  m_executingCommand = CTRLCMD_NONE;
115  m_writeToMouse = false;
116  m_mousePacketIdx = -1;
117 
118  m_mouseIntTrigs = 0;
119  m_keybIntTrigs = 0;
120 
121  m_sysReqTriggered = false;
122 }
123 
124 
125 uint8_t i8042::read(int address)
126 {
127  switch (address) {
128 
129  // 0 = read 8042 output register (DBBOUT) and set OBF = 0 and AOBF = 0
130  // this is port 0x60 as seen from CPU side
131  case 0:
132  m_STATUS &= ~(STATUS_OBF | STATUS_AOBF);
133  //printf("i8042.read(%02X) => %02X\n", address, m_DBBOUT);
134  return m_DBBOUT;
135 
136  // 1 = read 8042 status register (STATUS)
137  // this is port 0x64 as seen from CPU side
138  case 1:
139  //printf("i8042.read(%02X) => %02X\n", address, m_STATUS);
140  return m_STATUS;
141 
142  default:
143  return 0;
144 
145  }
146 }
147 
148 
149 void i8042::write(int address, uint8_t value)
150 {
151  switch (address) {
152 
153  // 0 = write 8042 input register (DBBIN), set STATUS_CMD = 0 and STATUS_IBF = 1
154  // this is port 0x60 as seen from CPU side
155  case 0:
156  //printf("i8042.write(%02X, %02X)\n", address, value);
157  m_DBBIN = value;
158  m_STATUS = (m_STATUS & ~STATUS_CMD) | STATUS_IBF;
159  break;
160 
161  // 1 = write 8042 input register (DBBIN), set F1 = 1 and STATUS_IBF = 1
162  // this is port 0x64 as seen from CPU side
163  case 1:
164  //printf("i8042.write(%02X, %02X)\n", address, value);
165  m_DBBIN = value;
166  m_STATUS |= STATUS_CMD | STATUS_IBF;
167  break;
168 
169  }
170 }
171 
172 
173 void i8042::tick()
174 {
175  // something to receive from keyboard?
176  if ((m_STATUS & STATUS_OBF) == 0 && m_keyboard->scancodeAvailable() && (m_commandByte & CMDBYTE_DISABLE_KEYBOARD) == 0) {
177  if (m_commandByte & CMDBYTE_STD_SCAN_CONVERSION) {
178  // transform "set 2" scancodes to "set 1"
179  int scode2 = m_keyboard->getNextScancode();
180  checkSysReq(scode2);
181  uint8_t scode = Keyboard::convScancodeSet2To1(scode2); // "set 1" code (0xf0 doesn't change!)
182  m_DBBOUT = (m_DBBOUT == 0xf0 ? (0x80 | scode) : scode);
183  if (scode != 0xf0) {
184  m_STATUS |= STATUS_OBF;
185  // IR1 (IRQ9) triggered when non break code or when code+break has been received
186  ++m_keybIntTrigs;
187  }
188  } else {
189  // no transform
190  int scode2 = m_keyboard->getNextScancode();
191  checkSysReq(scode2);
192  m_DBBOUT = scode2;
193  m_STATUS |= STATUS_OBF;
194  ++m_keybIntTrigs;
195  }
196  }
197 
198  // something to receive from mouse?
199  if ((m_STATUS & STATUS_OBF) == 0 && (m_mousePacketIdx > -1 || m_mouse->packetAvailable()) && (m_commandByte & CMDBYTE_DISABLE_MOUSE) == 0) {
200  if (m_mousePacketIdx == -1)
201  m_mouse->getNextPacket(&m_mousePacket);
202  m_DBBOUT = m_mousePacket.data[++m_mousePacketIdx];
203  if (m_mousePacketIdx == m_mouse->getPacketSize() - 1)
204  m_mousePacketIdx = -1;
205  m_STATUS |= STATUS_OBF | STATUS_AOBF;
206  ++m_mouseIntTrigs;
207  }
208 
209  // something to execute?
210  if (m_STATUS & STATUS_CMD) {
211  m_STATUS &= ~(STATUS_IBF | STATUS_CMD);
212  execCommand();
213  }
214 
215  // something to execute (with parameters)?
216  if ((m_STATUS & STATUS_IBF) && m_executingCommand != CTRLCMD_NONE) {
217  m_STATUS &= ~STATUS_IBF;
218  execCommand();
219  }
220 
221  // something to send?
222  if (m_STATUS & STATUS_IBF) {
223  m_STATUS &= ~(STATUS_IBF | STATUS_PARITY_ERR);
224  if (m_writeToMouse)
225  m_mouse->sendCommand(m_DBBIN);
226  else
227  m_keyboard->sendCommand(m_DBBIN);
228  m_writeToMouse = false;
229  m_STATUS |= STATUS_PARITY_ERR * m_keyboard->parityError();
230  }
231 
232  // are there interrupts to trig?
233  if (m_keybIntTrigs && trigKeyboardInterrupt())
234  --m_keybIntTrigs;
235  if (m_mouseIntTrigs && trigMouseInterrupt())
236  --m_mouseIntTrigs;
237 }
238 
239 
240 void i8042::execCommand()
241 {
242  uint8_t cmd = m_executingCommand == CTRLCMD_NONE ? m_DBBIN : m_executingCommand;
243 
244  switch (cmd) {
245 
246  case CTRLCMD_GET_COMMAND_BYTE:
247  m_DBBOUT = m_commandByte;
248  m_STATUS |= STATUS_OBF;
249  break;
250 
251  case CTRLCMD_WRITE_COMMAND_BYTE:
252  if (m_executingCommand) {
253  // data received
254  updateCommandByte(m_DBBIN);
255  m_executingCommand = CTRLCMD_NONE;
256  } else {
257  // wait for data
258  m_executingCommand = CTRLCMD_WRITE_COMMAND_BYTE;
259  }
260  break;
261 
262  case CTRLCMD_DISABLE_MOUSE_PORT:
263  enableMouse(false);
264  break;
265 
266  case CTRLCMD_ENABLE_MOUSE_PORT:
267  enableMouse(true);
268  break;
269 
270  case CTRLCMD_TEST_MOUSE_PORT:
271  m_DBBOUT = m_mouse->isMouseAvailable() ? 0x00 : 0x02;
272  m_STATUS |= STATUS_OBF;
273  break;
274 
275  case CTRLCMD_SELF_TEST:
276  m_DBBOUT = 0x55; // no errors!
277  m_STATUS |= STATUS_OBF;
278  break;
279 
280  case CTRLCMD_TEST_KEYBOARD_PORT:
281  m_DBBOUT = m_keyboard->isKeyboardAvailable() ? 0x00 : 0x02;
282  m_STATUS |= STATUS_OBF;
283  break;
284 
285  case CTRLCMD_DISABLE_KEYBOARD:
286  updateCommandByte(m_commandByte | CMDBYTE_DISABLE_KEYBOARD);
287  break;
288 
289  case CTRLCMD_ENABLE_KEYBOARD:
290  updateCommandByte(m_commandByte & ~CMDBYTE_DISABLE_KEYBOARD);
291  break;
292 
293  case CTRLCMD_WRITE_TO_MOUSE:
294  m_writeToMouse = 1;
295  break;
296 
297  case CTRLCMD_SYSTEM_RESET:
298  m_reset(m_context);
299  break;
300 
301  default:
302  printf("8042: unsupported controller command %02X\n", cmd);
303  break;
304  }
305 }
306 
307 
308 void i8042::enableMouse(bool value)
309 {
310  updateCommandByte(value ? (m_commandByte & ~CMDBYTE_DISABLE_MOUSE) : (m_commandByte | CMDBYTE_DISABLE_MOUSE));
311 }
312 
313 
314 void i8042::updateCommandByte(uint8_t newValue)
315 {
316  // disable keyboard bit changed?
317  if ((newValue ^ m_commandByte) & CMDBYTE_DISABLE_KEYBOARD) {
318  if (newValue & CMDBYTE_DISABLE_KEYBOARD) {
319  m_keyboard->suspendPort();
320  } else {
321  m_keyboard->resumePort();
322  }
323  }
324 
325  // disable mouse bit changed?
326  if ((newValue ^ m_commandByte) & CMDBYTE_DISABLE_MOUSE) {
327  if (newValue & CMDBYTE_DISABLE_MOUSE) {
328  m_mouse->suspendPort();
329  } else {
330  m_mouse->resumePort();
331  }
332  }
333 
334  m_commandByte = newValue;
335 
336 }
337 
338 
339 bool i8042::trigKeyboardInterrupt()
340 {
341  return m_commandByte & CMDBYTE_ENABLE_KEYBOARD_IRQ ? m_keyboardInterrupt(m_context) : true;
342 }
343 
344 
345 bool i8042::trigMouseInterrupt()
346 {
347  return m_commandByte & CMDBYTE_ENABLE_MOUSE_IRQ ? m_mouseInterrupt(m_context) : true;
348 }
349 
350 
351 // check if SysReq (ALT + PRINT SCREEN) has been released
352 void i8042::checkSysReq(int scode2)
353 {
354  if (m_DBBOUT == 0xf0) {
355  if (scode2 == 0x84) { // SysReq released?
356  m_sysReqTriggered = true;
357  } else if (m_sysReqTriggered && scode2 == 0x11) { // ALT released?
358  m_sysReqTriggered = false;
359  m_sysReq(m_context);
360  }
361  }
362 }
363 
364 
365 
366 } // namespace fabgl
static void quickCheckHardware()
Disable re-try when a mouse is not found.
Definition: mouse.h:373
Definition: canvas.cpp:36