FabGL
ESP32 Display Controller and Graphics Library
ps2device.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 "freertos/FreeRTOS.h"
28 
29 #include "ps2device.h"
30 #include "fabutils.h"
31 
32 
33 #pragma GCC optimize ("O2")
34 
35 
36 namespace fabgl {
37 
38 
39 #define PS2_CMD_SETLEDS 0xED
40 #define PS2_CMD_ECHO 0xEE
41 #define PS2_CMD_GETSET_CURRENT_SCANCODE_SET 0xF0 // keyboard specific
42 #define PS2_CMD_SET_REMOTE_MODE 0xF0 // mouse specific
43 #define PS2_CMD_IDENTIFY 0xF2
44 #define PS2_CMD_SET_TYPEMATIC_RATE_AND_DELAY 0xF3 // keyboard specific
45 #define PS2_CMD_SET_SAMPLE_RATE 0xF3 // mouse specific
46 #define PS2_CMD_ENABLE_SCANNING 0xF4
47 #define PS2_CMD_DISABLE_SCANNING 0xF5
48 #define PS2_CMD_SET_DEFAULT_PARAMS 0xF6
49 #define PS2_CMD_RESEND_LAST_BYTE 0xFE
50 #define PS2_CMD_RESET 0xFF
51 #define PS2_CMD_SET_STREAM_MODE 0xEA // mouse specific
52 #define PS2_CMD_STATUS_REQUEST 0xE9 // mouse specific
53 #define PS2_CMD_SET_RESOLUTION 0xE8 // mouse specific
54 #define PS2_CMD_SET_SCALING 0xE6 // mouse specific
55 
56 #define PS2_REPLY_ERROR1 0x00
57 #define PS2_REPLY_ERROR2 0xFF
58 #define PS2_REPLY_SELFTEST_OK 0xAA
59 #define PS2_REPLY_ECHO 0xEE
60 #define PS2_REPLY_ACK 0xFA
61 #define PS2_REPLY_SELFTEST_FAILED1 0xFC
62 #define PS2_REPLY_SELFTEST_FAILED2 0xFD
63 #define PS2_REPLY_RESEND 0xFE
64 
65 #define PS2_DEFAULT_CMD_TIMEOUT 500
66 #define PS2_DEFAULT_CMD_SUBTIMEOUT (PS2_DEFAULT_CMD_TIMEOUT / 2)
67 
68 #define PS2_QUICK_CMD_TIMEOUT 50
69 #define PS2_QUICK_CMD_SUBTIMEOUT (PS2_QUICK_CMD_TIMEOUT / 2)
70 
71 
72 PS2Device::PS2Device()
73 {
74  m_cmdTimeOut = PS2_DEFAULT_CMD_TIMEOUT;
75  m_cmdSubTimeOut = PS2_DEFAULT_CMD_SUBTIMEOUT;
76 }
77 
78 
79 PS2Device::~PS2Device()
80 {
81 }
82 
83 
84 void PS2Device::quickCheckHardware()
85 {
86  m_cmdTimeOut = PS2_QUICK_CMD_TIMEOUT;
87  m_cmdSubTimeOut = PS2_QUICK_CMD_SUBTIMEOUT;
88 }
89 
90 
91 bool PS2Device::lock(int timeOutMS)
92 {
93  return PS2Controller::lock(m_PS2Port, timeOutMS);
94 }
95 
96 
98 {
99  PS2Controller::unlock(m_PS2Port);
100 }
101 
102 
103 void PS2Device::begin(int PS2Port)
104 {
105  m_PS2Port = PS2Port;
106 }
107 
108 
109 int PS2Device::dataAvailable()
110 {
111  return PS2Controller::dataAvailable(m_PS2Port);
112 }
113 
114 
115 bool PS2Device::parityError()
116 {
117  return PS2Controller::parityError(m_PS2Port);
118 }
119 
120 
121 bool PS2Device::syncError()
122 {
123  return PS2Controller::syncError(m_PS2Port);
124 }
125 
126 
127 bool PS2Device::CLKTimeOutError()
128 {
129  return PS2Controller::CLKTimeOutError(m_PS2Port);
130 }
131 
132 
134 {
135  PS2Controller::disableRX(m_PS2Port);
136 }
137 
138 
140 {
141  PS2Controller::enableRX(m_PS2Port);
142 }
143 
144 
145 int PS2Device::getData(int timeOutMS)
146 {
147  constexpr int INTER_GETDATA_TIMEOUT_MS = 100;
148  constexpr int INTER_GETDATA_PAUSE_MS = 10;
149 
150  int interTimeOut = timeOutMS > -1 ? imin(timeOutMS, INTER_GETDATA_TIMEOUT_MS) : INTER_GETDATA_TIMEOUT_MS;
151 
152  int ret = -1;
153  TimeOut timeout;
154  while (true) {
155  lock(-1);
156  ret = PS2Controller::getData(m_PS2Port, interTimeOut);
157  unlock();
158  if (ret > -1 || parityError() || syncError() || CLKTimeOutError() || timeout.expired(timeOutMS))
159  break;
160  // give the opportunity for other sends
161  vTaskDelay(INTER_GETDATA_PAUSE_MS / portTICK_PERIOD_MS);
162  }
163  return ret;
164 }
165 
166 
167 bool PS2Device::sendCommand(uint8_t cmd, uint8_t expectedReply)
168 {
169  constexpr int INTER_WAITREPLY_TIMEOUT_MS = 10;
170 
171  PS2DeviceLock deviceLock(this);
172 
173  // temporary disable RX for the other port
174  PS2PortAutoDisableRX autoDisableRX(!m_PS2Port);
175 
176  PS2Controller::sendData(cmd, m_PS2Port);
177  TimeOut timeout;
178  do {
179  if (PS2Controller::getData(m_PS2Port, INTER_WAITREPLY_TIMEOUT_MS) == expectedReply)
180  return true;
181  } while (!timeout.expired(m_cmdTimeOut));
182  return false;
183 }
184 
185 
186 void PS2Device::sendCommand(uint8_t cmd)
187 {
188  PS2Controller::sendData(cmd, m_PS2Port);
189 }
190 
191 
192 void PS2Device::requestToResendLastByte()
193 {
194  PS2Controller::sendData(PS2_CMD_RESEND_LAST_BYTE, m_PS2Port);
195 }
196 
197 
198 bool PS2Device::send_cmdLEDs(bool numLock, bool capsLock, bool scrollLock)
199 {
200  PS2DeviceLock deviceLock(this);
201  return sendCommand(PS2_CMD_SETLEDS, PS2_REPLY_ACK) && sendCommand((scrollLock << 0) | (numLock << 1) | (capsLock << 2), PS2_REPLY_ACK);
202 }
203 
204 
205 bool PS2Device::send_cmdEcho()
206 {
207  return sendCommand(PS2_CMD_ECHO, PS2_REPLY_ECHO);
208 }
209 
210 
211 bool PS2Device::send_cmdGetScancodeSet(uint8_t * result)
212 {
213  PS2DeviceLock deviceLock(this);
214  if (!sendCommand(PS2_CMD_GETSET_CURRENT_SCANCODE_SET, PS2_REPLY_ACK))
215  return false;
216  if (!sendCommand(0, PS2_REPLY_ACK))
217  return false;
218  *result = getData(m_cmdTimeOut);
219  return (*result >= 1 || *result <= 3);
220 }
221 
222 
223 bool PS2Device::send_cmdSetScancodeSet(uint8_t scancodeSet)
224 {
225  PS2DeviceLock deviceLock(this);
226  if (!sendCommand(PS2_CMD_GETSET_CURRENT_SCANCODE_SET, PS2_REPLY_ACK))
227  return false;
228  return sendCommand(scancodeSet, PS2_REPLY_ACK);
229 }
230 
231 
232 // return value is always valid
233 bool PS2Device::send_cmdIdentify(PS2DeviceType * result)
234 {
235  PS2DeviceLock deviceLock(this);
237  if (!send_cmdDisableScanning())
238  return false;
239  if (!sendCommand(PS2_CMD_IDENTIFY, PS2_REPLY_ACK))
240  return false;
241  int b1 = getData(m_cmdTimeOut);
242  int b2 = getData(m_cmdTimeOut);
243  m_deviceID = (uint8_t)b1 | ((uint8_t)b2 << 8);
244  if (b1 == -1 && b2 == -1)
246  else if (b1 == 0x00 && b2 == -1)
248  else if (b1 == 0x03 && b2 == -1)
250  else if (b1 == 0x04 && b2 == -1)
252  else if ((b1 == 0xAB && b2 == 0x41) || (b1 == 0xAB && b2 == 0xC1))
254  else if (b1 == 0xAB && b2 == 0x83)
255  *result = PS2DeviceType::M2Keyboard;
256  return send_cmdEnableScanning();
257 }
258 
259 
260 bool PS2Device::send_cmdDisableScanning()
261 {
262  return sendCommand(PS2_CMD_DISABLE_SCANNING, PS2_REPLY_ACK);
263 }
264 
265 
266 bool PS2Device::send_cmdEnableScanning()
267 {
268  return sendCommand(PS2_CMD_ENABLE_SCANNING, PS2_REPLY_ACK);
269 }
270 
271 
272 const int16_t REPEATRATES[32] = { 33, 37, 41, 45, 50, 54, 58, 62, 66, 75, 83, 91,
273  100, 108, 125, 125, 133, 149, 166, 181, 200, 217, 232, 250,
274  270, 303, 333, 370, 400, 434, 476, 500};
275 
276 
277 // repeatRateMS : 33 ms ... 500 ms (in steps as above REPEATRATES table)
278 // repeatDelayMS : 250 ms ... 1000 ms (in steps of 250 ms)
279 bool PS2Device::send_cmdTypematicRateAndDelay(int repeatRateMS, int repeatDelayMS)
280 {
281  PS2DeviceLock deviceLock(this);
282  if (!sendCommand(PS2_CMD_SET_TYPEMATIC_RATE_AND_DELAY, PS2_REPLY_ACK))
283  return false;
284  uint8_t byteToSend = 0b01011; // default repeat rate 10.9 characters per seconds (91ms)
285  for (int i = 0; i < 32; ++i)
286  if (REPEATRATES[i] >= repeatRateMS) {
287  byteToSend = i;
288  break;
289  }
290  byteToSend |= (repeatDelayMS / 250 - 1) << 5;
291  return sendCommand(byteToSend, PS2_REPLY_ACK);
292 }
293 
294 
295 // sampleRate: valid values are 10, 20, 40, 60, 80, 100, and 200 (samples/sec)
296 bool PS2Device::send_cmdSetSampleRate(int sampleRate)
297 {
298  PS2DeviceLock deviceLock(this);
299  if (!sendCommand(PS2_CMD_SET_SAMPLE_RATE, PS2_REPLY_ACK))
300  return false;
301  return sendCommand(sampleRate, PS2_REPLY_ACK);
302 }
303 
304 
305 // resolution:
306 // 0 = 1 count/mm
307 // 1 = 2 count/mm
308 // 2 = 4 count/mm
309 // 3 = 8 count/mm
310 bool PS2Device::send_cmdSetResolution(int resolution)
311 {
312  PS2DeviceLock deviceLock(this);
313  if (!sendCommand(PS2_CMD_SET_RESOLUTION, PS2_REPLY_ACK))
314  return false;
315  return sendCommand(resolution, PS2_REPLY_ACK);
316 }
317 
318 
319 // scaling:
320 // 1 -> 1:1
321 // 2 -> 1:2
322 bool PS2Device::send_cmdSetScaling(int scaling)
323 {
324  PS2DeviceLock deviceLock(this);
325  if (!sendCommand(PS2_CMD_SET_SCALING, PS2_REPLY_ACK))
326  return false;
327  return sendCommand(scaling, PS2_REPLY_ACK);
328 }
329 
330 
331 bool PS2Device::send_cmdSetDefaultParams()
332 {
333  return sendCommand(PS2_CMD_SET_DEFAULT_PARAMS, PS2_REPLY_ACK);
334 }
335 
336 
337 bool PS2Device::send_cmdReset()
338 {
339  PS2DeviceLock deviceLock(this);
340  if (!sendCommand(PS2_CMD_RESET, PS2_REPLY_ACK))
341  return false;
342  return getData(500) == PS2_REPLY_SELFTEST_OK; // timout 500ms should be enough for PS2 device to reset and do self test
343 }
344 
345 
346 } // end of namespace
void suspendPort()
Suspends PS/2 port driving the CLK line Low.
Definition: ps2device.cpp:133
bool lock(int timeOutMS)
Gets exclusive access to the device.
Definition: ps2device.cpp:91
bool sendCommand(uint8_t cmd, uint8_t expectedReply)
Sends a raw command to the PS/2 device and wait for reply.
Definition: ps2device.cpp:167
static bool lock(int PS2Port, int timeOutMS)
Gets exclusive access to the specified PS/2 port.
static bool dataAvailable(int PS2Port)
Determines if one byte has been received from the specified port.
PS2DeviceType
Represents the type of device attached to PS/2 port.
Definition: ps2device.h:52
static void sendData(uint8_t data, int PS2Port)
Sends a command to the device.
static int getData(int PS2Port, int timeOutMS)
Gets a scancode from the queue.
static void unlock(int PS2Port)
Releases port from exclusive access.
This file contains some utility classes and functions.
Definition: canvas.cpp:36
static void disableRX(int PS2Port)
Disables inputs from PS/2 port driving the CLK line Low.
void resumePort()
Resumes PS/2 port releasing CLK line.
Definition: ps2device.cpp:139
static void enableRX(int PS2Port)
Enables inputs from PS/2 port releasing CLK line.
void unlock()
Releases device from exclusive access.
Definition: ps2device.cpp:97
This file contains fabgl::PS2Device definition.