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-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 "freertos/FreeRTOS.h"
28
29#include "ps2device.h"
30#include "fabutils.h"
31
32
33#pragma GCC optimize ("O2")
34
35
36namespace 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
72PS2Device::PS2Device()
73{
74 m_cmdTimeOut = PS2_DEFAULT_CMD_TIMEOUT;
75 m_cmdSubTimeOut = PS2_DEFAULT_CMD_SUBTIMEOUT;
76}
77
78
79PS2Device::~PS2Device()
80{
81}
82
83
84void PS2Device::quickCheckHardware()
85{
86 m_cmdTimeOut = PS2_QUICK_CMD_TIMEOUT;
87 m_cmdSubTimeOut = PS2_QUICK_CMD_SUBTIMEOUT;
88}
89
90
91bool PS2Device::lock(int timeOutMS)
92{
93 return PS2Controller::lock(m_PS2Port, timeOutMS);
94}
95
96
98{
99 PS2Controller::unlock(m_PS2Port);
100}
101
102
103void PS2Device::begin(int PS2Port)
104{
105 m_PS2Port = PS2Port;
106}
107
108
109int PS2Device::dataAvailable()
110{
111 return PS2Controller::dataAvailable(m_PS2Port);
112}
113
114
115bool PS2Device::parityError()
116{
117 return PS2Controller::parityError(m_PS2Port);
118}
119
120
121bool PS2Device::syncError()
122{
123 return PS2Controller::syncError(m_PS2Port);
124}
125
126
127bool 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
145int 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
167bool 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
186void PS2Device::sendCommand(uint8_t cmd)
187{
188 PS2Controller::sendData(cmd, m_PS2Port);
189}
190
191
192void PS2Device::requestToResendLastByte()
193{
194 PS2Controller::sendData(PS2_CMD_RESEND_LAST_BYTE, m_PS2Port);
195}
196
197
198bool 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
205bool PS2Device::send_cmdEcho()
206{
207 return sendCommand(PS2_CMD_ECHO, PS2_REPLY_ECHO);
208}
209
210
211bool 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
223bool 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
233bool 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)
256 return send_cmdEnableScanning();
257}
258
259
260bool PS2Device::send_cmdDisableScanning()
261{
262 return sendCommand(PS2_CMD_DISABLE_SCANNING, PS2_REPLY_ACK);
263}
264
265
266bool PS2Device::send_cmdEnableScanning()
267{
268 return sendCommand(PS2_CMD_ENABLE_SCANNING, PS2_REPLY_ACK);
269}
270
271
272const 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)
279bool 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)
296bool 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
310bool 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
322bool 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
331bool PS2Device::send_cmdSetDefaultParams()
332{
333 return sendCommand(PS2_CMD_SET_DEFAULT_PARAMS, PS2_REPLY_ACK);
334}
335
336
337bool 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
static void sendData(uint8_t data, int PS2Port)
Sends a command to the device.
static bool lock(int PS2Port, int timeOutMS)
Gets exclusive access to the specified PS/2 port.
static void disableRX(int PS2Port)
Disables inputs from PS/2 port driving the CLK line Low.
static void unlock(int PS2Port)
Releases port from exclusive access.
static int getData(int PS2Port, int timeOutMS)
Gets a scancode from the queue.
static bool dataAvailable(int PS2Port)
Determines if one byte has been received from the specified port.
static void enableRX(int PS2Port)
Enables inputs from PS/2 port releasing CLK line.
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
void unlock()
Releases device from exclusive access.
Definition: ps2device.cpp:97
bool lock(int timeOutMS)
Gets exclusive access to the device.
Definition: ps2device.cpp:91
void suspendPort()
Suspends PS/2 port driving the CLK line Low.
Definition: ps2device.cpp:133
void resumePort()
Resumes PS/2 port releasing CLK line.
Definition: ps2device.cpp:139
This file contains some utility classes and functions.
PS2DeviceType
Represents the type of device attached to PS/2 port.
Definition: ps2device.h:52
This file contains fabgl::PS2Device definition.