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-2022 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
31namespace 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
80i8042::i8042()
81{
82}
83
84
85i8042::~i8042()
86{
87}
88
89
90void 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
107void 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
125uint8_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
149void 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
173void 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
240void 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
308void i8042::enableMouse(bool value)
309{
310 updateCommandByte(value ? (m_commandByte & ~CMDBYTE_DISABLE_MOUSE) : (m_commandByte | CMDBYTE_DISABLE_MOUSE));
311}
312
313
314void 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
339bool i8042::trigKeyboardInterrupt()
340{
341 return m_commandByte & CMDBYTE_ENABLE_KEYBOARD_IRQ ? m_keyboardInterrupt(m_context) : true;
342}
343
344
345bool 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
352void 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