]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - tmk_core/protocol/ibmpc.c
ibmpc: Fix START case in ISR
[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             break;
196         case BIT0:
197         case BIT1:
198         case BIT2:
199         case BIT3:
200         case BIT4:
201         case BIT5:
202         case BIT6:
203         case BIT7:
204             data >>= 1;
205             if (dbit) {
206                 data |= 0x80;
207                 parity++;
208             }
209             if (state == BIT7 && ibmpc_protocol == IBMPC_PROTOCOL_XT) {
210                 if (!ringbuf_put(&rb, data)) {
211                     ibmpc_error = IBMPC_ERR_FULL;
212                     goto ERROR;
213                 }
214                 ibmpc_error = IBMPC_ERR_NONE;
215                 goto DONE;
216             }
217             break;
218         case PARITY:
219             if (dbit) {
220                 if (!(parity & 0x01))
221                     goto ERROR;
222             } else {
223                 if (parity & 0x01)
224                     goto ERROR;
225             }
226             break;
227         case STOP:
228             if (!dbit)
229                 goto ERROR;
230             if (!ringbuf_put(&rb, data)) {
231                 ibmpc_error = IBMPC_ERR_FULL;
232                 goto ERROR;
233             }
234             ibmpc_error = IBMPC_ERR_NONE;
235             goto DONE;
236             break;
237         default:
238             goto ERROR;
239     }
240     goto NEXT;
241
242 ERROR:
243     ibmpc_error |= state;
244     ibmpc_error |= IBMPC_ERR_RECV;
245     ringbuf_reset(&rb);
246 DONE:
247     last_time = 0;
248     state = START;
249     data = 0;
250     parity = 1;
251     return;
252 NEXT:
253     state++;
254     return;
255 }
256
257 /* send LED state to keyboard */
258 void ibmpc_host_set_led(uint8_t led)
259 {
260     ibmpc_host_send(0xED);
261     ibmpc_host_send(led);
262 }