-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathkk_ps2.c
485 lines (386 loc) · 11.4 KB
/
kk_ps2.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
/**
* kk_ps2.c: A PS/2 host library for AVR
*
* The PS/2 CLK pin must be connected to a hardware interrupt pin. The
* default is PD2/INT0.
*
* Copyright (c) 2020 Kimmo Kulovesi, https://arkku.dev/
* Provided with absolutely no warranty, use at your own risk only.
* Use and distribute freely, mark modified copies as such.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "kk_ps2.h"
#define PASTE_(a, b) a##b
#define PASTE(a, b) PASTE_(a, b)
#ifndef PS2_PORT
#define PS2_PORT D
#define PS2_DATA_PIN 4
#define PS2_CLK_PIN 2
#define PS2_CLK_INT_NUM 0
#endif
#define PS2_DDR PASTE(DDR, PS2_PORT)
#define PS2_PORT_REG PASTE(PORT, PS2_PORT)
#define PS2_PIN_REG PASTE(PIN, PS2_PORT)
#define PS2_CLK_BIT ((uint8_t) (1U << (PS2_CLK_PIN)))
#define PS2_DATA_BIT ((uint8_t) (1U << (PS2_DATA_PIN)))
#define PS2_CLK_INT PASTE(INT, PS2_CLK_INT_NUM)
#define PS2_CLK_INT_VECTOR PASTE(INT, PASTE(PS2_CLK_INT_NUM, _vect))
#define PS2_CLK_ISC0 PASTE(PASTE(ISC, PS2_CLK_INT_NUM), 0)
#define PS2_CLK_ISC1 PASTE(PASTE(ISC, PS2_CLK_INT_NUM), 1)
#define PS2_CLK_ISC0_BIT ((uint8_t) (1U << (PS2_CLK_ISC0)))
#define PS2_CLK_ISC1_BIT ((uint8_t) (1U << (PS2_CLK_ISC1)))
#define PS2_BIT_MASK (PS2_CLK_BIT | PS2_DATA_BIT)
#define are_ps2_lines_high() ((PS2_PIN_REG & PS2_BIT_MASK) == PS2_BIT_MASK)
#define ps2_data_bit7() ((uint8_t) (((PS2_PIN_REG & PS2_DATA_BIT) ? 0x80U : 0U)))
#define ps2_clk_state() ((uint8_t) (PS2_PIN_REG & PS2_CLK_BIT) ? 1U : 0U)
#define ps2_set_pin_state(pin, state) do { \
if ((state)) { \
PS2_PORT_REG |= _BV(pin); \
} else { \
PS2_PORT_REG &= ~(_BV(pin)); \
} \
} while (0)
#define ps2_clk_set(state) ps2_set_pin_state(PS2_CLK_PIN, (state))
#define ps2_data_set(state) ps2_set_pin_state(PS2_DATA_PIN, (state))
#define ps2_clk_int_clear_flag() do { EIFR = _BV(PS2_CLK_INT); } while (0)
#define ps2_clk_int_enable() do { EIMSK |= _BV(PS2_CLK_INT); } while (0)
#define ps2_clk_int_disable() do { EIMSK &= ~(_BV(PS2_CLK_INT)); } while (0)
#define ps2_clk_int_on_falling() do { EICRA = (EICRA & ~PS2_CLK_ISC0_BIT) | PS2_CLK_ISC1_BIT; } while (0)
#define ps2_clk_int_on_change() do { EICRA = (EICRA & ~PS2_CLK_ISC1_BIT) | PS2_CLK_ISC0_BIT; } while (0)
#define ps2_clk_set_input() do { PS2_DDR &= ~PS2_CLK_BIT; } while (0)
#define ps2_clk_set_output() do { PS2_DDR |= PS2_CLK_BIT; } while (0)
#define ps2_data_set_input() do { PS2_DDR &= ~PS2_DATA_BIT; } while (0)
#define ps2_data_set_output() do { PS2_DDR |= PS2_DATA_BIT; } while (0)
#define INTERNAL_PULL_UP 1
enum {
PS2_STATE_ERROR = 0,
PS2_STATE_IDLE,
PS2_STATE_READ_DATA,
PS2_STATE_READ_PARITY,
PS2_STATE_READ_STOP,
PS2_STATE_WRITE_BEGIN,
PS2_STATE_WRITE_DATA,
PS2_STATE_WRITE_PARITY,
PS2_STATE_WRITE_STOP,
PS2_STATE_WRITE_ACK,
PS2_STATE_END
};
/// The state of the (bidirectional) PS/2 bus.
static volatile uint8_t ps2_state = PS2_STATE_ERROR;
/// Number of bits left to read/write.
static volatile uint8_t ps2_bits_left = 0;
/// The parity of the current byte being read or written.
static volatile uint8_t ps2_parity = 0;
/// The current data byte being read or written.
static volatile uint8_t ps2_data_byte = 0;
/// The last error that has occurred.
static volatile char ps2_error = 0;
#ifndef KK_PS2_BUFFER_SIZE
#define KK_PS2_BUFFER_SIZE 128
#endif
static uint8_t ps2_buffer[KK_PS2_BUFFER_SIZE];
static volatile uint8_t ps2_buffer_tail = 0;
static volatile uint8_t ps2_buffer_head = 0;
#define buffer_size ((unsigned int) (sizeof ps2_buffer))
#if KK_PS2_BUFFER_SIZE == 256
#define modulo_buffer_size(x) ((uint8_t) (x))
#elif KK_PS2_BUFFER_SIZE > 256
#error KK_PS2_BUFFER_SIZE too large (max. 256)!
#else
#define modulo_buffer_size(x) ((uint8_t) ((x) % buffer_size))
#endif
static inline void
ps2_data_out (const uint8_t state) {
if (state) {
ps2_data_set_input();
ps2_data_set(INTERNAL_PULL_UP);
} else {
ps2_data_set_output();
ps2_data_set(0);
}
}
static inline void
ps2_clk_set_low (void) {
ps2_clk_set_output();
ps2_clk_set(0);
}
static inline void
ps2_clk_release (void) {
ps2_clk_set_input();
ps2_clk_set(INTERNAL_PULL_UP);
}
static inline void
ps2_data_release (void) {
ps2_data_set_input();
ps2_data_set(INTERNAL_PULL_UP);
}
static inline void
ps2_enable_interrupt (void) {
ps2_clk_int_clear_flag();
ps2_clk_int_enable();
}
static inline void
ps2_disable_interrupt (void) {
ps2_clk_int_disable();
}
static inline void
ps2_set_error (const char err) {
ps2_state = PS2_STATE_ERROR;
ps2_error = err;
}
char
ps2_last_error (void) {
char msg = ps2_error;
if (msg == 0 && !ps2_is_ok()) {
msg = '?';
}
return msg;
}
static inline bool
ps2_is_active (void) {
return ps2_state > PS2_STATE_IDLE;
}
bool
ps2_is_ok (void) {
return ps2_state != PS2_STATE_ERROR;
}
static bool
ps2_write (const uint8_t data, const bool flush_input) {
unsigned attempts_remaining = 25000U;
while (ps2_is_active() && attempts_remaining--) {
_delay_us(4);
}
ps2_disable_interrupt();
if (!ps2_is_ok()) {
return false;
}
if (ps2_is_active()) {
ps2_error = PS2_ERROR_BUSY;
return false;
}
ps2_data_release();
// Pull down CLK to request write access
ps2_clk_set_low();
// The specified time is 100 µs, but let's err on the side of caution
// since some KVMs seem to introduce an additional delay
_delay_us(160);
ps2_data_out(0);
ps2_error = 0;
ps2_state = PS2_STATE_WRITE_BEGIN;
ps2_data_byte = data;
ps2_parity = 0;
ps2_bits_left = 8;
if (flush_input) {
ps2_flush_input();
}
ps2_clk_int_on_falling();
ps2_enable_interrupt();
// Release the CLK to begin writing
ps2_clk_release();
// Wait for the write to begin (note that we use a longer timeout
// here since the very first write on power-up may take a while)
attempts_remaining = 62500U;
while (ps2_state == PS2_STATE_WRITE_BEGIN && attempts_remaining--) {
_delay_us(8);
}
if (ps2_state != PS2_STATE_WRITE_BEGIN) {
return true;
} else {
ps2_set_error(PS2_ERROR_WRITE_BEGIN);
return false;
}
}
uint8_t
ps2_bytes_available (void) {
return modulo_buffer_size(buffer_size + ps2_buffer_tail - ps2_buffer_head);
}
int
ps2_get_byte (void) {
uint8_t data;
uint8_t pos = ps2_buffer_head;
if (pos == ps2_buffer_tail) {
return EOF;
}
data = ps2_buffer[pos];
ps2_buffer_head = modulo_buffer_size(pos + 1);
return data;
}
int
ps2_recv (void) {
while (ps2_is_ok() && !ps2_bytes_available());
return ps2_get_byte();
}
int
ps2_recv_timeout (const uint8_t milliseconds) {
if (ps2_bytes_available()) {
return ps2_get_byte();
}
if (milliseconds == 0U) {
return ps2_recv();
}
unsigned attempts_remaining = 100U * (unsigned) milliseconds;
while (ps2_is_ok() && !ps2_bytes_available() && attempts_remaining--) {
_delay_us(10);
}
return ps2_get_byte();
}
bool
ps2_send (const uint8_t data, const bool flush_input) {
if (ps2_write(data, flush_input)) {
while (ps2_is_active());
return ps2_is_ok();
} else {
return false;
}
}
uint8_t ps2_reply_timeout_ms = 100U;
int
ps2_command (const uint8_t command) {
int_fast8_t retries_remaining = 2;
int reply = EOF;
do {
if (!ps2_send(command, true)) {
return reply;
}
reply = ps2_recv_timeout(ps2_reply_timeout_ms);
} while (reply == PS2_REPLY_RESEND && retries_remaining);
return reply;
}
bool
ps2_command_ack (const uint8_t command) {
if (ps2_command(command) == PS2_REPLY_ACK) {
return true;
} else {
ps2_error = PS2_ERROR_COMMAND;
return false;
}
}
int
ps2_command_arg (const uint8_t command, const uint8_t arg) {
const int reply = ps2_command(command);
if (reply != PS2_REPLY_ACK) {
return reply;
}
return ps2_command(arg);
}
bool
ps2_command_arg_ack (const uint8_t command, const uint8_t arg) {
if (ps2_command_arg(command, arg) == PS2_REPLY_ACK) {
return true;
} else {
ps2_error = PS2_ERROR_COMMAND;
return false;
}
}
/// The interrupt fires on the PS/2 CLK pulse.
ISR (PS2_CLK_INT_VECTOR) {
// Read the incoming bit from the data line at msb
uint8_t bit = ps2_data_bit7();
uint8_t state = ps2_state;
switch (state++) {
case PS2_STATE_ERROR:
return;
case PS2_STATE_END:
if (bit || ps2_clk_state()) {
ps2_clk_int_on_falling();
ps2_state = PS2_STATE_IDLE;
return;
}
// fallthrough
case PS2_STATE_IDLE:
if (bit == 0) {
ps2_state = state;
ps2_data_byte = 0;
ps2_parity = 0;
ps2_bits_left = 8;
} else {
ps2_set_error(PS2_ERROR_START_BIT);
}
break;
case PS2_STATE_READ_DATA:
ps2_parity ^= bit;
ps2_data_byte >>= 1;
ps2_data_byte |= bit;
if (--ps2_bits_left == 0) {
ps2_state = state;
}
break;
case PS2_STATE_READ_PARITY:
ps2_parity ^= bit;
if (ps2_parity) {
ps2_state = state;
} else {
ps2_set_error(PS2_ERROR_PARITY);
}
break;
case PS2_STATE_READ_STOP:
ps2_state = PS2_STATE_END;
ps2_clk_int_on_change();
if (bit) {
const uint8_t next_pos = modulo_buffer_size(ps2_buffer_tail + 1);
if (next_pos != ps2_buffer_head) {
ps2_buffer[ps2_buffer_tail] = ps2_data_byte;
ps2_buffer_tail = next_pos;
}
} else {
ps2_set_error(PS2_ERROR_STOP_BIT);
}
break;
case PS2_STATE_WRITE_BEGIN:
ps2_state = state;
// fallthrough
case PS2_STATE_WRITE_DATA:
bit = ps2_data_byte & 1;
ps2_data_out(bit);
ps2_parity ^= bit;
ps2_data_byte >>= 1;
if (--ps2_bits_left == 0) {
ps2_state = state;
}
break;
case PS2_STATE_WRITE_PARITY:
ps2_data_out(ps2_parity ^ 1);
ps2_state = state;
break;
case PS2_STATE_WRITE_STOP:
ps2_data_release();
ps2_state = state;
break;
case PS2_STATE_WRITE_ACK:
if (bit == 0) {
ps2_state = PS2_STATE_END;
ps2_clk_int_on_change();
} else {
ps2_set_error(PS2_ERROR_WRITE_END);
}
break;
}
}
void
ps2_flush_input (void) {
ps2_buffer_tail = 0;
ps2_buffer_head = 0;
}
void
ps2_enable (void) {
int attempts_remaining = 12000;
while (ps2_is_active() && attempts_remaining--) {
_delay_us(4);
}
// Note: The timeout is to avoid interrupting an in-progress byte, but
// since we cannot recognize a broken state, we must proceed regardless
ps2_disable_interrupt();
ps2_data_release();
ps2_clk_set_low();
ps2_flush_input();
ps2_error = 0;
ps2_state = PS2_STATE_IDLE;
ps2_clk_int_on_falling();
ps2_enable_interrupt();
ps2_clk_release();
}