]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - tmk_core/protocol/ibmpc.c
core: Add IBM PC Keyboard protocol support
[max/tmk_keyboard.git] / tmk_core / protocol / ibmpc.c
1 /*
2 Copyright 2010,2011,2012,2013,2019 Jun WAKO <wakojun@gmail.com>
3
4 This software is licensed with a Modified BSD License.
5 All of this is supposed to be Free Software, Open Source, DFSG-free,
6 GPL-compatible, and OK to use in both free and proprietary applications.
7 Additions and corrections to this file are welcome.
8
9
10 Redistribution and use in source and binary forms, with or without
11 modification, are permitted provided that the following conditions are met:
12
13 * Redistributions of source code must retain the above copyright
14   notice, this list of conditions and the following disclaimer.
15
16 * Redistributions in binary form must reproduce the above copyright
17   notice, this list of conditions and the following disclaimer in
18   the documentation and/or other materials provided with the
19   distribution.
20
21 * Neither the name of the copyright holders nor the names of
22   contributors may be used to endorse or promote products derived
23   from this software without specific prior written permission.
24
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 /*
39  * IBM PC keyboard protocol
40  */
41
42 #include <stdbool.h>
43 #include <avr/interrupt.h>
44 #include "ringbuf.h"
45 #include "ibmpc.h"
46 #include "debug.h"
47 #include "timer.h"
48 #include "wait.h"
49
50
51 #define WAIT(stat, us, err) do { \
52     if (!wait_##stat(us)) { \
53         ibmpc_error = err; \
54         goto ERROR; \
55     } \
56 } while (0)
57
58
59 #define BUF_SIZE 16
60 static uint8_t buf[BUF_SIZE];
61 static ringbuf_t rb = {
62     .buffer = buf,
63     .head = 0,
64     .tail = 0,
65     .size_mask = BUF_SIZE - 1
66 };
67
68 volatile uint8_t ibmpc_protocol = IBMPC_PROTOCOL_AT;
69 volatile uint8_t ibmpc_error = IBMPC_ERR_NONE;
70
71 void ibmpc_host_init(void)
72 {
73     clock_init();
74     data_init();
75     idle();
76     IBMPC_INT_INIT();
77     IBMPC_INT_ON();
78     // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
79     //wait_ms(2500);
80 }
81
82 int16_t ibmpc_host_send(uint8_t data)
83 {
84     bool parity = true;
85     ibmpc_error = IBMPC_ERR_NONE;
86
87     if (ibmpc_protocol == IBMPC_PROTOCOL_XT) return -1;
88
89     dprintf("w%02X ", data);
90
91     IBMPC_INT_OFF();
92
93     /* terminate a transmission if we have */
94     inhibit();
95     wait_us(100); // 100us [4]p.13, [5]p.50
96
97     /* 'Request to Send' and Start bit */
98     data_lo();
99     clock_hi();
100     WAIT(clock_lo, 10000, 1);   // 10ms [5]p.50
101
102     /* Data bit[2-9] */
103     for (uint8_t i = 0; i < 8; i++) {
104         wait_us(15);
105         if (data&(1<<i)) {
106             parity = !parity;
107             data_hi();
108         } else {
109             data_lo();
110         }
111         WAIT(clock_hi, 50, 2);
112         WAIT(clock_lo, 50, 3);
113     }
114
115     /* Parity bit */
116     wait_us(15);
117     if (parity) { data_hi(); } else { data_lo(); }
118     WAIT(clock_hi, 50, 4);
119     WAIT(clock_lo, 50, 5);
120
121     /* Stop bit */
122     wait_us(15);
123     data_hi();
124
125     /* Ack */
126     WAIT(data_lo, 50, 6);
127     WAIT(clock_lo, 50, 7);
128
129     /* wait for idle state */
130     WAIT(clock_hi, 50, 8);
131     WAIT(data_hi, 50, 9);
132
133     idle();
134     IBMPC_INT_ON();
135     return ibmpc_host_recv_response();
136 ERROR:
137     ibmpc_error |= IBMPC_ERR_SEND;
138     idle();
139     IBMPC_INT_ON();
140     return -1;
141 }
142
143 int16_t ibmpc_host_recv_response(void)
144 {
145     // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
146     uint8_t retry = 25;
147     while (retry-- && ringbuf_is_empty(&rb)) {
148         wait_ms(1);
149     }
150     int16_t data = ringbuf_get(&rb);
151     if (data != -1) dprintf("r%02X ", data);
152     return data;
153 }
154
155 /* get data received by interrupt */
156 int16_t ibmpc_host_recv(void)
157 {
158     int16_t data = ringbuf_get(&rb);
159     if (data != -1) dprintf("r%02X ", data);
160     return data;
161 }
162
163 ISR(IBMPC_INT_VECT)
164 {
165     static uint16_t last_time = 0;
166     static enum {
167         START,
168         BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
169         PARITY,
170         STOP,
171     } state = START;
172     static uint8_t data = 0;
173     static uint8_t parity = 1;
174
175     // return unless falling edge
176     if (clock_in()) {
177         return;
178     }
179
180     // Reset state when taking more than 1ms
181     if (last_time && timer_elapsed(last_time) > 10) {
182         ibmpc_error = IBMPC_ERR_TIMEOUT | IBMPC_ERR_RECV | state;
183         state = START;
184         data = 0;
185         parity = 1;
186     }
187     last_time = timer_read();
188
189     switch (state) {
190         case START:
191             if (ibmpc_protocol == IBMPC_PROTOCOL_XT) {
192                 // ignore start(0) bit
193                 if (!data_in()) return;
194             } else {
195                 if (data_in())
196                     goto ERROR;
197             }
198         case BIT0:
199         case BIT1:
200         case BIT2:
201         case BIT3:
202         case BIT4:
203         case BIT5:
204         case BIT6:
205         case BIT7:
206             data >>= 1;
207             if (data_in()) {
208                 data |= 0x80;
209                 parity++;
210             }
211             if (state == BIT7 && ibmpc_protocol == IBMPC_PROTOCOL_XT) {
212                 if (!ringbuf_put(&rb, data)) {
213                     ibmpc_error = IBMPC_ERR_FULL;
214                     goto ERROR;
215                 }
216                 ibmpc_error = IBMPC_ERR_NONE;
217                 goto DONE;
218             }
219             break;
220         case PARITY:
221             if (data_in()) {
222                 if (!(parity & 0x01))
223                     goto ERROR;
224             } else {
225                 if (parity & 0x01)
226                     goto ERROR;
227             }
228             break;
229         case STOP:
230             if (!data_in())
231                 goto ERROR;
232             if (!ringbuf_put(&rb, data)) {
233                 ibmpc_error = IBMPC_ERR_FULL;
234                 goto ERROR;
235             }
236             ibmpc_error = IBMPC_ERR_NONE;
237             goto DONE;
238             break;
239         default:
240             goto ERROR;
241     }
242     goto NEXT;
243
244 ERROR:
245     ibmpc_error |= state;
246     ibmpc_error |= IBMPC_ERR_RECV;
247     ringbuf_reset(&rb);
248 DONE:
249     last_time = 0;
250     state = START;
251     data = 0;
252     parity = 1;
253     return;
254 NEXT:
255     state++;
256     return;
257 }
258
259 /* send LED state to keyboard */
260 void ibmpc_host_set_led(uint8_t led)
261 {
262     ibmpc_host_send(0xED);
263     ibmpc_host_send(led);
264 }