sgikbd initial commit (based on a500kbd: https://github.com/jtsiomb/a500kbd)
[sgikbd] / fw / src / ps2kbd.c
1 #include <avr/io.h>
2 #include <avr/interrupt.h>
3 #include <util/delay.h>
4 #include "ps2kbd.h"
5 #include "defs.h"
6 #include "timer.h"
7
8 #define TIMEOUT 100
9
10 #define BUF_SZ  32
11 #define BUF_IDX_MASK    (BUF_SZ - 1)
12 #define NEXT_IDX(x) (((x) + 1) & BUF_IDX_MASK)
13 static volatile unsigned char keybuf[BUF_SZ];
14 static volatile unsigned char key_rd, key_wr;
15
16 static volatile int send_nbits, wait_ack;
17 static volatile unsigned char send_val, send_par;
18
19 static void abort_send(void);
20
21 int ps2write(unsigned char c)
22 {
23         cli();
24         send_nbits = 10;
25         send_val = c;
26         send_par = 0;   /* init to 0, will be calculated while sending */
27
28         /* inhibit transmission, hold at least 100us */
29         PORTD &= ~PCLK_BIT;
30         DDRD |= PCLK_BIT;
31         EIFR |= (1 << INTF0);   /* clear pending interrupt due to clock transition */
32         sei();
33         _delay_us(100);
34
35         /* RTS by data low & release clock (this counts as start bit?) */
36         PORTD &= ~PDATA_BIT;
37         DDRD |= PDATA_BIT;
38         DDRD &= ~PCLK_BIT;
39
40         reset_timer();
41         while(send_nbits > 0) {
42                 if(get_msec() > TIMEOUT) {
43                         abort_send();
44                         return -1;
45                 }
46         }
47         _delay_us(5);
48
49         /* release data line and wait for ack */
50         cli();
51         wait_ack = 1;
52         sei();
53         DDRD &= ~PDATA_BIT;
54         reset_timer();
55         while(wait_ack) {
56                 if(get_msec() > TIMEOUT) {
57                         abort_send();
58                         return -1;
59                 }
60         }
61         return 0;
62 }
63
64 static void abort_send(void)
65 {
66         cli();
67         send_nbits = 0;
68         wait_ack = 0;
69         /* hold clock low for 100us */
70         PORTD &= ~PCLK_BIT;
71         DDRD |= PCLK_BIT;
72         _delay_us(100);
73         DDRD &= ~(PCLK_BIT | PDATA_BIT);
74         EIFR |= (1 << INTF0);   /* clear pending interrupt */
75         sei();
76 }
77
78 unsigned char ps2read(void)
79 {
80         unsigned char key;
81
82         while(key_rd == key_wr) {
83         }
84
85         cli();
86         key = keybuf[key_rd];
87         key_rd = NEXT_IDX(key_rd);
88         sei();
89
90         return key;
91 }
92
93 int ps2pending(void)
94 {
95         return key_rd != key_wr;
96 }
97
98 int ps2wait(unsigned int timeout)
99 {
100         reset_timer();
101         while(key_rd == key_wr) {
102                 if(get_msec() >= timeout) return -1;
103         }
104         return 0;
105 }
106
107 #define PS2_ACK         0xfa
108 #define PS2_RESEND      0xfe
109 #define PS2_ECHO        0xee
110
111 int ps2setled(unsigned char state)
112 {
113         unsigned char c;
114
115         ps2write(0xed);
116         reset_timer();
117         while(!ps2pending()) {
118                 if(get_msec() >= TIMEOUT) return -1;
119         }
120         c = ps2read();
121         /*printf("ps2setled 1st response: %x\r\n", (unsigned int)c);*/
122         if(c != PS2_ACK) return -1;
123
124         ps2write(state);
125         reset_timer();
126         while(!ps2pending()) {
127                 if(get_msec() >= TIMEOUT) return -1;
128         }
129         c = ps2read();
130         /*printf("ps2setled 2nd response: %x\r\n", (unsigned int)c);*/
131         if(c != PS2_ACK) return -1;
132
133         return 0;
134 }
135
136 void ps2clearbuf(void)
137 {
138         key_rd = key_wr;
139 }
140
141 ISR(INT0_vect)
142 {
143         static unsigned char value, parity;
144         /*static unsigned char valp;*/
145         static int nbits;
146
147         if(wait_ack) {
148                 if(!(PIND & PDATA_BIT)) {
149                         wait_ack = 0;
150                 }
151                 return;
152         }
153
154         if(send_nbits > 0) {
155                 --send_nbits;
156                 switch(send_nbits) {
157                 case 1:         /* parity bit */
158                         if(send_par & 1) {      /* odd number of ones: parity 0 */
159                                 PORTD &= ~PDATA_BIT;
160                         } else {        /* even number of ones: parity 1 */
161                                 PORTD |= PDATA_BIT;
162                         }
163                         break;
164                 case 0: /* stop bit: 1 */
165                         PORTD |= PDATA_BIT;
166                         break;
167                 default:
168                         if(send_val & 1) {
169                                 PORTD |= PDATA_BIT;
170                                 ++send_par;
171                         } else {
172                                 PORTD &= ~PDATA_BIT;
173                         }
174                         send_val >>= 1;
175                 }
176
177         } else {
178                 if(nbits > 0 && nbits < 9) {
179                         value >>= 1;
180                         if(PIND & PDATA_BIT) {
181                                 value |= 0x80;
182                                 parity ^= 1;
183                         }
184                 }/* else if(nbits == 9) {
185                         valp = (PIND >> PDATA) & 1;
186                 }*/
187                 if(++nbits >= 11) {
188                         nbits = 0;
189
190                         /* check parity */
191                         /*if((parity & 1) == (valp & 1)) {}*/
192                         keybuf[key_wr] = (unsigned char)value;
193                         key_wr = NEXT_IDX(key_wr);
194
195                         value = 0;
196                         parity = 0;
197                 }
198         }
199 }