]> git.friedersdorff.com Git - max/tmk_keyboard.git/blob - tmk_core/protocol/ibmpc.c
ibmpc: Add ibmpc_host_clear_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     // initialize reset pin
74     IBMPC_RST_PORT |=  (1<<IBMPC_RST_BIT1);
75     IBMPC_RST_DDR  |=  (1<<IBMPC_RST_BIT1);
76     IBMPC_RST_PORT |=  (1<<IBMPC_RST_BIT2);
77     IBMPC_RST_DDR  |=  (1<<IBMPC_RST_BIT2);
78     inhibit();
79     IBMPC_INT_INIT();
80     IBMPC_INT_OFF();
81 }
82
83 void ibmpc_host_enable(void)
84 {
85     IBMPC_INT_ON();
86     idle();
87 }
88
89 void ibmpc_host_disable(void)
90 {
91     // TODO: test order? uneeded interrupt happens by making clock lo
92     inhibit();
93     IBMPC_INT_OFF();
94 }
95
96 int16_t ibmpc_host_send(uint8_t data)
97 {
98     bool parity = true;
99     ibmpc_error = IBMPC_ERR_NONE;
100
101     if (ibmpc_protocol == IBMPC_PROTOCOL_XT) return -1;
102
103     dprintf("w%02X ", data);
104
105     IBMPC_INT_OFF();
106
107     /* terminate a transmission if we have */
108     inhibit();
109     wait_us(100); // 100us [4]p.13, [5]p.50
110
111     /* 'Request to Send' and Start bit */
112     data_lo();
113     clock_hi();
114     WAIT(clock_lo, 10000, 1);   // 10ms [5]p.50
115
116     /* Data bit[2-9] */
117     for (uint8_t i = 0; i < 8; i++) {
118         wait_us(15);
119         if (data&(1<<i)) {
120             parity = !parity;
121             data_hi();
122         } else {
123             data_lo();
124         }
125         WAIT(clock_hi, 50, 2);
126         WAIT(clock_lo, 50, 3);
127     }
128
129     /* Parity bit */
130     wait_us(15);
131     if (parity) { data_hi(); } else { data_lo(); }
132     WAIT(clock_hi, 50, 4);
133     WAIT(clock_lo, 50, 5);
134
135     /* Stop bit */
136     wait_us(15);
137     data_hi();
138
139     /* Ack */
140     WAIT(data_lo, 50, 6);
141     WAIT(clock_lo, 50, 7);
142
143     /* wait for idle state */
144     WAIT(clock_hi, 50, 8);
145     WAIT(data_hi, 50, 9);
146
147     // clear buffer to get response correctly
148     ringbuf_reset(&rb);
149     ibmpc_host_isr_clear();
150
151     idle();
152     IBMPC_INT_ON();
153     return ibmpc_host_recv_response();
154 ERROR:
155     ibmpc_error |= IBMPC_ERR_SEND;
156     idle();
157     IBMPC_INT_ON();
158     return -1;
159 }
160
161 int16_t ibmpc_host_recv_response(void)
162 {
163     // Command may take 25ms/20ms at most([5]p.46, [3]p.21)
164     uint8_t retry = 25;
165     while (retry-- && ringbuf_is_empty(&rb)) {
166         wait_ms(1);
167     }
168     int16_t data = ringbuf_get(&rb);
169     if (data != -1) dprintf("r%02X ", data);
170     return data;
171 }
172
173 /* get data received by interrupt */
174 int16_t ibmpc_host_recv(void)
175 {
176     int16_t data = ringbuf_get(&rb);
177     if (data != -1) dprintf("r%02X ", data);
178     return data;
179 }
180
181
182 /*
183  * Receive data from keyboard with ISR
184  */
185 static enum {
186     START,
187     BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
188     PARITY,
189     STOP, } isr_state = START;
190 static uint8_t isr_data = 0;
191 static uint8_t isr_parity = 1;
192 static uint16_t isr_time = 0;
193
194 void ibmpc_host_isr_clear(void)
195 {
196     isr_state = START;
197     isr_data = 0;
198     isr_parity = 1;
199     isr_time = 0;
200 }
201
202 ISR(IBMPC_INT_VECT)
203 {
204     uint8_t dbit = IBMPC_DATA_PIN&(1<<IBMPC_DATA_BIT);
205
206     // Reset state when taking more than 1ms
207     if (isr_time && timer_elapsed(isr_time) > 1) {
208         ibmpc_error = IBMPC_ERR_TIMEOUT | IBMPC_ERR_RECV | isr_state;
209         isr_state = START;
210         isr_data = 0;
211         isr_parity = 1;
212     }
213     isr_time = timer_read();
214
215     switch (isr_state) {
216         case START:
217             if (ibmpc_protocol == IBMPC_PROTOCOL_XT) {
218                 // ignore start(0) bit
219                 if (!dbit) return;
220             } else {
221                 if (dbit)
222                     goto ERROR;
223             }
224             break;
225         case BIT0:
226         case BIT1:
227         case BIT2:
228         case BIT3:
229         case BIT4:
230         case BIT5:
231         case BIT6:
232         case BIT7:
233             isr_data >>= 1;
234             if (dbit) {
235                 isr_data |= 0x80;
236                 isr_parity++;
237             }
238             if (isr_state == BIT7 && ibmpc_protocol == IBMPC_PROTOCOL_XT) {
239                 if (!ringbuf_put(&rb, isr_data)) {
240                     ibmpc_error = IBMPC_ERR_FULL;
241                     goto ERROR;
242                 }
243                 ibmpc_error = IBMPC_ERR_NONE;
244                 goto DONE;
245             }
246             break;
247         case PARITY:
248             if (dbit) {
249                 if (!(isr_parity & 0x01))
250                     goto ERROR;
251             } else {
252                 if (isr_parity & 0x01)
253                     goto ERROR;
254             }
255             break;
256         case STOP:
257             if (!dbit)
258                 goto ERROR;
259             if (!ringbuf_put(&rb, isr_data)) {
260                 ibmpc_error = IBMPC_ERR_FULL;
261                 goto ERROR;
262             }
263             ibmpc_error = IBMPC_ERR_NONE;
264             goto DONE;
265             break;
266         default:
267             goto ERROR;
268     }
269     goto NEXT;
270
271 ERROR:
272     ibmpc_error |= isr_state;
273     ibmpc_error |= IBMPC_ERR_RECV;
274     ringbuf_reset(&rb);
275 DONE:
276     isr_state = START;
277     isr_data = 0;
278     isr_parity = 1;
279     isr_time = 0;
280     return;
281 NEXT:
282     isr_state++;
283     return;
284 }
285
286 /* send LED state to keyboard */
287 void ibmpc_host_set_led(uint8_t led)
288 {
289     ibmpc_host_send(0xED);
290     ibmpc_host_send(led);
291 }