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