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-2020 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6  This file is part of FabGL Library.
7 
8  FabGL is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  FabGL is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 
23 #include "freertos/FreeRTOS.h"
24 
25 #include "ps2device.h"
26 #include "fabutils.h"
27 
28 
29 
30 namespace fabgl {
31 
32 
33 #define PS2_CMD_SETLEDS 0xED
34 #define PS2_CMD_ECHO 0xEE
35 #define PS2_CMD_GETSET_CURRENT_SCANCODE_SET 0xF0 // keyboard specific
36 #define PS2_CMD_SET_REMOTE_MODE 0xF0 // mouse specific
37 #define PS2_CMD_IDENTIFY 0xF2
38 #define PS2_CMD_SET_TYPEMATIC_RATE_AND_DELAY 0xF3 // keyboard specific
39 #define PS2_CMD_SET_SAMPLE_RATE 0xF3 // mouse specific
40 #define PS2_CMD_ENABLE_SCANNING 0xF4
41 #define PS2_CMD_DISABLE_SCANNING 0xF5
42 #define PS2_CMD_SET_DEFAULT_PARAMS 0xF6
43 #define PS2_CMD_RESEND_LAST_BYTE 0xFE
44 #define PS2_CMD_RESET 0xFF
45 #define PS2_CMD_SET_STREAM_MODE 0xEA // mouse specific
46 #define PS2_CMD_STATUS_REQUEST 0xE9 // mouse specific
47 #define PS2_CMD_SET_RESOLUTION 0xE8 // mouse specific
48 #define PS2_CMD_SET_SCALING 0xE6 // mouse specific
49 
50 #define PS2_REPLY_ERROR1 0x00
51 #define PS2_REPLY_ERROR2 0xFF
52 #define PS2_REPLY_SELFTEST_OK 0xAA
53 #define PS2_REPLY_ECHO 0xEE
54 #define PS2_REPLY_ACK 0xFA
55 #define PS2_REPLY_SELFTEST_FAILED1 0xFC
56 #define PS2_REPLY_SELFTEST_FAILED2 0xFD
57 #define PS2_REPLY_RESEND 0xFE
58 
59 #define PS2_DEFAULT_CMD_RETRY_COUNT 3
60 #define PS2_DEFAULT_CMD_TIMEOUT 400
61 #define PS2_DEFAULT_CMD_SUBTIMEOUT (PS2_DEFAULT_CMD_TIMEOUT / 2)
62 
63 #define PS2_QUICK_CMD_RETRY_COUNT 1
64 #define PS2_QUICK_CMD_TIMEOUT 50
65 #define PS2_QUICK_CMD_SUBTIMEOUT (PS2_QUICK_CMD_TIMEOUT / 2)
66 
67 
68 PS2Device::PS2Device()
69 {
70  m_retryCount = PS2_DEFAULT_CMD_RETRY_COUNT;
71  m_cmdTimeOut = PS2_DEFAULT_CMD_TIMEOUT;
72  m_cmdSubTimeOut = PS2_DEFAULT_CMD_SUBTIMEOUT;
73  m_deviceLock = xSemaphoreCreateRecursiveMutex();
74 }
75 
76 
77 PS2Device::~PS2Device()
78 {
79  vSemaphoreDelete(m_deviceLock);
80 }
81 
82 
83 void PS2Device::quickCheckHardware()
84 {
85  m_retryCount = PS2_QUICK_CMD_RETRY_COUNT;
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 xSemaphoreTakeRecursive(m_deviceLock, msToTicks(timeOutMS));
94 }
95 
96 
98 {
99  xSemaphoreGiveRecursive(m_deviceLock);
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::instance()->dataAvailable(m_PS2Port);
112 }
113 
114 
115 bool PS2Device::parityError()
116 {
117  return PS2Controller::instance()->parityError(m_PS2Port);
118 }
119 
120 
121 int PS2Device::getData(int timeOutMS)
122 {
123  TimeOut timeout;
124  int ret = -1;
125  while (!timeout.expired(timeOutMS)) {
126  lock(-1);
127  ret = PS2Controller::instance()->getData(m_PS2Port);
128  unlock();
129  if (ret > -1 || parityError())
130  break;
131  lock(-1);
132  PS2Controller::instance()->waitData((timeOutMS > -1 ? timeOutMS : m_cmdSubTimeOut), m_PS2Port);
133  unlock();
134  vTaskDelay(10 / portTICK_PERIOD_MS);
135  }
136  return ret;
137 }
138 
139 
140 bool PS2Device::sendCommand(uint8_t cmd, uint8_t expectedReply)
141 {
142  for (int i = 0; i < m_retryCount; ++i) {
143  PS2Controller::instance()->sendData(cmd, m_PS2Port);
144  if (getData(m_cmdTimeOut) != expectedReply)
145  continue;
146  return true;
147  }
148  return false;
149 }
150 
151 
152 void PS2Device::requestToResendLastByte()
153 {
154  PS2Controller::instance()->sendData(PS2_CMD_RESEND_LAST_BYTE, m_PS2Port);
155 }
156 
157 
158 bool PS2Device::send_cmdLEDs(bool numLock, bool capsLock, bool scrollLock)
159 {
160  PS2DeviceLock deviceLock(this);
161  if (!sendCommand(PS2_CMD_SETLEDS, PS2_REPLY_ACK))
162  return false;
163  bool ret = sendCommand((scrollLock << 0) | (numLock << 1) | (capsLock << 2), PS2_REPLY_ACK);
164  return ret;
165 }
166 
167 
168 bool PS2Device::send_cmdEcho()
169 {
170  PS2DeviceLock deviceLock(this);
171  return sendCommand(PS2_CMD_ECHO, PS2_REPLY_ECHO);
172 }
173 
174 
175 bool PS2Device::send_cmdGetScancodeSet(uint8_t * result)
176 {
177  PS2DeviceLock deviceLock(this);
178  if (!sendCommand(PS2_CMD_GETSET_CURRENT_SCANCODE_SET, PS2_REPLY_ACK))
179  return false;
180  if (!sendCommand(0, PS2_REPLY_ACK))
181  return false;
182  *result = getData(m_cmdTimeOut);
183  return (*result >= 1 || *result <= 3);
184 }
185 
186 
187 bool PS2Device::send_cmdSetScancodeSet(uint8_t scancodeSet)
188 {
189  PS2DeviceLock deviceLock(this);
190  if (!sendCommand(PS2_CMD_GETSET_CURRENT_SCANCODE_SET, PS2_REPLY_ACK))
191  return false;
192  return sendCommand(scancodeSet, PS2_REPLY_ACK);
193 }
194 
195 
196 // return value is always valid
197 bool PS2Device::send_cmdIdentify(PS2DeviceType * result)
198 {
199  PS2DeviceLock deviceLock(this);
201  if (!send_cmdDisableScanning())
202  return false;
203  if (!sendCommand(PS2_CMD_IDENTIFY, PS2_REPLY_ACK))
204  return false;
205  int b1 = getData(m_cmdTimeOut);
206  int b2 = getData(m_cmdTimeOut);
207  if (b1 == -1 && b2 == -1)
209  else if (b1 == 0x00 && b2 == -1)
211  else if (b1 == 0x03 && b2 == -1)
213  else if (b1 == 0x04 && b2 == -1)
215  else if ((b1 == 0xAB && b2 == 0x41) || (b1 == 0xAB && b2 == 0xC1))
217  else if (b1 == 0xAB && b2 == 0x83)
218  *result = PS2DeviceType::M2Keyboard;
219  return send_cmdEnableScanning();
220 }
221 
222 
223 bool PS2Device::send_cmdDisableScanning()
224 {
225  PS2DeviceLock deviceLock(this);
226  return sendCommand(PS2_CMD_DISABLE_SCANNING, PS2_REPLY_ACK);
227 }
228 
229 
230 bool PS2Device::send_cmdEnableScanning()
231 {
232  PS2DeviceLock deviceLock(this);
233  return sendCommand(PS2_CMD_ENABLE_SCANNING, PS2_REPLY_ACK);
234 }
235 
236 
237 const int16_t REPEATRATES[32] = { 33, 37, 41, 45, 50, 54, 58, 62, 66, 75, 83, 91,
238  100, 108, 125, 125, 133, 149, 166, 181, 200, 217, 232, 250,
239  270, 303, 333, 370, 400, 434, 476, 500};
240 
241 
242 // repeatRateMS : 33 ms ... 500 ms (in steps as above REPEATRATES table)
243 // repeatDelayMS : 250 ms ... 1000 ms (in steps of 250 ms)
244 bool PS2Device::send_cmdTypematicRateAndDelay(int repeatRateMS, int repeatDelayMS)
245 {
246  PS2DeviceLock deviceLock(this);
247  if (!sendCommand(PS2_CMD_SET_TYPEMATIC_RATE_AND_DELAY, PS2_REPLY_ACK))
248  return false;
249  uint8_t byteToSend = 0b01011; // default repeat rate 10.9 characters per seconds (91ms)
250  for (int i = 0; i < 32; ++i)
251  if (REPEATRATES[i] >= repeatRateMS) {
252  byteToSend = i;
253  break;
254  }
255  byteToSend |= (repeatDelayMS / 250 - 1) << 5;
256  return sendCommand(byteToSend, PS2_REPLY_ACK);
257 }
258 
259 
260 // sampleRate: valid values are 10, 20, 40, 60, 80, 100, and 200 (samples/sec)
261 bool PS2Device::send_cmdSetSampleRate(int sampleRate)
262 {
263  PS2DeviceLock deviceLock(this);
264  if (!sendCommand(PS2_CMD_SET_SAMPLE_RATE, PS2_REPLY_ACK))
265  return false;
266  return sendCommand(sampleRate, PS2_REPLY_ACK);
267 }
268 
269 
270 // resolution:
271 // 0 = 1 count/mm
272 // 1 = 2 count/mm
273 // 2 = 4 count/mm
274 // 3 = 8 count/mm
275 bool PS2Device::send_cmdSetResolution(int resolution)
276 {
277  PS2DeviceLock deviceLock(this);
278  if (!sendCommand(PS2_CMD_SET_RESOLUTION, PS2_REPLY_ACK))
279  return false;
280  return sendCommand(resolution, PS2_REPLY_ACK);
281 }
282 
283 
284 // scaling:
285 // 1 -> 1:1
286 // 2 -> 1:2
287 bool PS2Device::send_cmdSetScaling(int scaling)
288 {
289  PS2DeviceLock deviceLock(this);
290  if (!sendCommand(PS2_CMD_SET_SCALING, PS2_REPLY_ACK))
291  return false;
292  return sendCommand(scaling, PS2_REPLY_ACK);
293 }
294 
295 
296 bool PS2Device::send_cmdSetDefaultParams()
297 {
298  PS2DeviceLock deviceLock(this);
299  return sendCommand(PS2_CMD_SET_DEFAULT_PARAMS, PS2_REPLY_ACK);
300 }
301 
302 
303 bool PS2Device::send_cmdReset()
304 {
305  PS2DeviceLock deviceLock(this);
306  if (!sendCommand(PS2_CMD_RESET, PS2_REPLY_ACK))
307  return false;
308  return getData(500) == PS2_REPLY_SELFTEST_OK; // timout 500ms should be enough for PS2 device to reset and do self test
309 }
310 
311 
312 } // end of namespace
bool lock(int timeOutMS)
Gets exclusive access to the device.
Definition: ps2device.cpp:91
int dataAvailable(int PS2Port)
Gets the number of scancodes available in the controller buffer.
PS2DeviceType
Represents the type of device attached to PS/2 port.
Definition: ps2device.h:48
void sendData(uint8_t data, int PS2Port)
Sends a command to the device.
This file contains some utility classes and functions.
static PS2Controller * instance()
Returns the singleton instance of PS2Controller class.
Definition: canvas.cpp:31
int getData(int PS2Port)
Gets a scancode from the queue.
void unlock()
Releases device from exclusive access.
Definition: ps2device.cpp:97
This file contains fabgl::PS2Device definition.