]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - tmk_core/protocol/ibmpc.c
ibmpc: Read data line earlier in ISR as possible
[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     uint8_t dbit = IBMPC_DATA_PIN&(1<<IBMPC_DATA_BIT);
176
177     // Reset state when taking more than 1ms
178     if (last_time && timer_elapsed(last_time) > 10) {
179         ibmpc_error = IBMPC_ERR_TIMEOUT | IBMPC_ERR_RECV | state;
180         state = START;
181         data = 0;
182         parity = 1;
183     }
184     last_time = timer_read();
185
186     switch (state) {
187         case START:
188             if (ibmpc_protocol == IBMPC_PROTOCOL_XT) {
189                 // ignore start(0) bit
190                 if (!dbit) return;
191             } else {
192                 if (dbit)
193                     goto ERROR;
194             }
195         case BIT0:
196         case BIT1:
197         case BIT2:
198         case BIT3:
199         case BIT4:
200         case BIT5:
201         case BIT6:
202         case BIT7:
203             data >>= 1;
204             if (dbit) {
205                 data |= 0x80;
206                 parity++;
207             }
208             if (state == BIT7 && ibmpc_protocol == IBMPC_PROTOCOL_XT) {
209                 if (!ringbuf_put(&rb, data)) {
210                     ibmpc_error = IBMPC_ERR_FULL;
211                     goto ERROR;
212                 }
213                 ibmpc_error = IBMPC_ERR_NONE;
214                 goto DONE;
215             }
216             break;
217         case PARITY:
218             if (dbit) {
219                 if (!(parity & 0x01))
220                     goto ERROR;
221             } else {
222                 if (parity & 0x01)
223                     goto ERROR;
224             }
225             break;
226         case STOP:
227             if (!dbit)
228                 goto ERROR;
229             if (!ringbuf_put(&rb, data)) {
230                 ibmpc_error = IBMPC_ERR_FULL;
231                 goto ERROR;
232             }
233             ibmpc_error = IBMPC_ERR_NONE;
234             goto DONE;
235             break;
236         default:
237             goto ERROR;
238     }
239     goto NEXT;
240
241 ERROR:
242     ibmpc_error |= state;
243     ibmpc_error |= IBMPC_ERR_RECV;
244     ringbuf_reset(&rb);
245 DONE:
246     last_time = 0;
247     state = START;
248     data = 0;
249     parity = 1;
250     return;
251 NEXT:
252     state++;
253     return;
254 }
255
256 /* send LED state to keyboard */
257 void ibmpc_host_set_led(uint8_t led)
258 {
259     ibmpc_host_send(0xED);
260     ibmpc_host_send(led);
261 }