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. Feel free to use FabGL in free software and hardware:
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  m_mutex = xSemaphoreCreateMutex();
83 }
84 
85 
86 i8042::~i8042()
87 {
88  vSemaphoreDelete(m_mutex);
89 }
90 
91 
92 void i8042::init()
93 {
94  // because mouse is optional, don't re-try if it is not found (to speed-up boot)
96 
97  // keyboard configured on port 0, and optionally mouse on port 1
99  m_keyboard = m_PS2Controller.keyboard();
100  m_mouse = m_PS2Controller.mouse();
101 
102  m_STATUS = STATUS_SYSFLAG | STATUS_INH;
103  m_DBBOUT = 0;
104  m_DBBIN = 0;
105  m_commandByte = CMDBYTE_ENABLE_KEYBOARD_IRQ | CMDBYTE_ENABLE_MOUSE_IRQ | CMDBYTE_SYSFLAG | CMDBYTE_STD_SCAN_CONVERSION | CMDBYTE_DISABLE_MOUSE;
106 
107  m_executingCommand = CTRLCMD_NONE;
108  m_writeToMouse = false;
109  m_mousePacketIdx = -1;
110 
111  m_mouseIntTrigs = 0;
112  m_keybIntTrigs = 0;
113 }
114 
115 
116 uint8_t i8042::read(int address)
117 {
118  AutoSemaphore autoSemaphore(m_mutex);
119  switch (address) {
120 
121  // 0 = read 8042 output register (DBBOUT) and set OBF = 0 and AOBF = 0
122  // this is port 0x60 as seen from CPU side
123  case 0:
124  m_STATUS &= ~(STATUS_OBF | STATUS_AOBF);
125  //printf("i8042.read(%02X) => %02X\n", address, m_DBBOUT);
126  return m_DBBOUT;
127 
128  // 1 = read 8042 status register (STATUS)
129  // this is port 0x64 as seen from CPU side
130  case 1:
131  //printf("i8042.read(%02X) => %02X\n", address, m_STATUS);
132  return m_STATUS;
133 
134  default:
135  return 0;
136 
137  }
138 }
139 
140 
141 void i8042::write(int address, uint8_t value)
142 {
143  AutoSemaphore autoSemaphore(m_mutex);
144 
145  switch (address) {
146 
147  // 0 = write 8042 input register (DBBIN), set STATUS_CMD = 0 and STATUS_IBF = 1
148  // this is port 0x60 as seen from CPU side
149  case 0:
150  //printf("i8042.write(%02X, %02X)\n", address, value);
151  m_DBBIN = value;
152  m_STATUS = (m_STATUS & ~STATUS_CMD) | STATUS_IBF;
153  break;
154 
155  // 1 = write 8042 input register (DBBIN), set F1 = 1 and STATUS_IBF = 1
156  // this is port 0x64 as seen from CPU side
157  case 1:
158  //printf("i8042.write(%02X, %02X)\n", address, value);
159  m_DBBIN = value;
160  m_STATUS |= STATUS_CMD | STATUS_IBF;
161  break;
162 
163  }
164 }
165 
166 
167 void i8042::tick()
168 {
169  AutoSemaphore autoSemaphore(m_mutex);
170 
171  // something to receive from keyboard?
172  if ((m_STATUS & STATUS_OBF) == 0 && m_keyboard->scancodeAvailable()) {
173  if (m_commandByte & CMDBYTE_STD_SCAN_CONVERSION) {
174  // transform "set 2" scancodes to "set 1"
175  uint8_t scode = Keyboard::convScancodeSet2To1(m_keyboard->getNextScancode()); // "set 1" code (0xf0 doesn't change!)
176  m_DBBOUT = (m_DBBOUT == 0xf0 ? (0x80 | scode) : scode);
177  if (scode != 0xf0) {
178  m_STATUS |= STATUS_OBF;
179  // IR1 (IRQ9) triggered when non break code or when code+break has been received
180  ++m_keybIntTrigs;
181  }
182  } else {
183  // no transform
184  m_DBBOUT = m_keyboard->getNextScancode();
185  m_STATUS |= STATUS_OBF;
186  ++m_keybIntTrigs;
187  }
188  }
189 
190  // something to receive from mouse?
191  if ((m_STATUS & STATUS_OBF) == 0 && (m_mousePacketIdx > -1 || m_mouse->packetAvailable())) {
192  if (m_mousePacketIdx == -1)
193  m_mouse->getNextPacket(&m_mousePacket);
194  m_DBBOUT = m_mousePacket.data[++m_mousePacketIdx];
195  if (m_mousePacketIdx == m_mouse->getPacketSize() - 1)
196  m_mousePacketIdx = -1;
197  m_STATUS |= STATUS_OBF | STATUS_AOBF;
198  ++m_mouseIntTrigs;
199  }
200 
201  // something to execute?
202  if (m_STATUS & STATUS_CMD) {
203  m_STATUS &= ~(STATUS_IBF | STATUS_CMD);
204  execCommand();
205  }
206 
207  // something to execute (with parameters)?
208  if ((m_STATUS & STATUS_IBF) && m_executingCommand != CTRLCMD_NONE) {
209  m_STATUS &= ~STATUS_IBF;
210  execCommand();
211  }
212 
213  // something to send?
214  if (m_STATUS & STATUS_IBF) {
215  m_STATUS &= ~(STATUS_IBF | STATUS_PARITY_ERR);
216  if (m_writeToMouse)
217  m_mouse->sendCommand(m_DBBIN);
218  else
219  m_keyboard->sendCommand(m_DBBIN);
220  m_writeToMouse = false;
221  m_STATUS |= STATUS_PARITY_ERR * m_keyboard->parityError();
222  }
223 
224  // are there interrupts to trig?
225  if (m_keybIntTrigs && trigKeyboardInterrupt())
226  --m_keybIntTrigs;
227  if (m_mouseIntTrigs && trigMouseInterrupt())
228  --m_mouseIntTrigs;
229 }
230 
231 
232 void i8042::execCommand()
233 {
234  uint8_t cmd = m_executingCommand == CTRLCMD_NONE ? m_DBBIN : m_executingCommand;
235 
236  switch (cmd) {
237 
238  case CTRLCMD_GET_COMMAND_BYTE:
239  m_DBBOUT = m_commandByte;
240  m_STATUS |= STATUS_OBF;
241  break;
242 
243  case CTRLCMD_WRITE_COMMAND_BYTE:
244  if (m_executingCommand) {
245  // data received
246  updateCommandByte(m_DBBIN);
247  m_executingCommand = CTRLCMD_NONE;
248  } else {
249  // wait for data
250  m_executingCommand = CTRLCMD_WRITE_COMMAND_BYTE;
251  }
252  break;
253 
254  case CTRLCMD_DISABLE_MOUSE_PORT:
255  enableMouse(false);
256  break;
257 
258  case CTRLCMD_ENABLE_MOUSE_PORT:
259  enableMouse(true);
260  break;
261 
262  case CTRLCMD_TEST_MOUSE_PORT:
263  m_DBBOUT = m_mouse->isMouseAvailable() ? 0x00 : 0x02;
264  m_STATUS |= STATUS_OBF;
265  break;
266 
267  case CTRLCMD_SELF_TEST:
268  m_DBBOUT = 0x55; // no errors!
269  m_STATUS |= STATUS_OBF;
270  break;
271 
272  case CTRLCMD_TEST_KEYBOARD_PORT:
273  m_DBBOUT = m_keyboard->isKeyboardAvailable() ? 0x00 : 0x02;
274  m_STATUS |= STATUS_OBF;
275  break;
276 
277  case CTRLCMD_DISABLE_KEYBOARD:
278  updateCommandByte(m_commandByte | CMDBYTE_DISABLE_KEYBOARD);
279  break;
280 
281  case CTRLCMD_ENABLE_KEYBOARD:
282  updateCommandByte(m_commandByte & ~CMDBYTE_DISABLE_KEYBOARD);
283  break;
284 
285  case CTRLCMD_WRITE_TO_MOUSE:
286  m_writeToMouse = 1;
287  break;
288 
289  case CTRLCMD_SYSTEM_RESET:
290  esp_restart();
291  break;
292 
293  default:
294  printf("8042: unsupported controller command %02X\n", cmd);
295  break;
296  }
297 }
298 
299 
300 void i8042::enableMouse(bool value)
301 {
302  AutoSemaphore autoSemaphore(m_mutex);
303  updateCommandByte(value ? (m_commandByte & ~CMDBYTE_DISABLE_MOUSE) : (m_commandByte | CMDBYTE_DISABLE_MOUSE));
304 }
305 
306 
307 void i8042::updateCommandByte(uint8_t newValue)
308 {
309  // disable keyboard bit changed?
310  if ((newValue ^ m_commandByte) & CMDBYTE_DISABLE_KEYBOARD) {
311  if (newValue & CMDBYTE_DISABLE_KEYBOARD) {
312  m_keyboard->suspendPort();
313  } else {
314  m_keyboard->resumePort();
315  }
316  }
317 
318  // disable mouse bit changed?
319  if ((newValue ^ m_commandByte) & CMDBYTE_DISABLE_MOUSE) {
320  if (newValue & CMDBYTE_DISABLE_MOUSE) {
321  m_mouse->suspendPort();
322  } else {
323  m_mouse->resumePort();
324  }
325  }
326 
327  m_commandByte = newValue;
328 
329 }
330 
331 
332 bool i8042::trigKeyboardInterrupt()
333 {
334  return m_commandByte & CMDBYTE_ENABLE_KEYBOARD_IRQ ? m_keyboardInterrupt(m_context) : true;
335 }
336 
337 
338 bool i8042::trigMouseInterrupt()
339 {
340  return m_commandByte & CMDBYTE_ENABLE_MOUSE_IRQ ? m_mouseInterrupt(m_context) : true;
341 }
342 
343 
344 
345 
346 
347 } // namespace fabgl
static void quickCheckHardware()
Disable re-try when a mouse is not found.
Definition: mouse.h:373
Definition: canvas.cpp:36