]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/protocol/ibm4704.c
Merge commit 'cedfbfcb1a9ad9cf93816f1952fc4bf7c55fbb61'
[qmk_firmware.git] / tmk_core / protocol / ibm4704.c
1 /*
2 Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
3 */
4 #include <stdbool.h>
5 #include <util/delay.h>
6 #include "debug.h"
7 #include "ring_buffer.h"
8 #include "ibm4704.h"
9
10
11 #define WAIT(stat, us, err) do { \
12     if (!wait_##stat(us)) { \
13         ibm4704_error = err; \
14         goto ERROR; \
15     } \
16 } while (0)
17
18
19 uint8_t ibm4704_error = 0;
20
21
22 void ibm4704_init(void)
23 {
24     inhibit();  // keep keyboard from sending
25     IBM4704_INT_INIT();
26     IBM4704_INT_ON();
27     idle();     // allow keyboard sending
28 }
29
30 /*
31 Host to Keyboard
32 ----------------
33 Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part.
34
35         ____        __   __   __   __   __   __   __   __   __   ________
36 Clock       \______/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/
37             ^   ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ___
38 Data    ____|__/    X____X____X____X____X____X____X____X____X____X   \___
39             |  Start   0    1    2    3    4    5    6    7    P   Stop
40             Request by host
41
42 Start bit:  can be long as 300-350us.
43 Request:    Host pulls Clock line down to request to send a command.
44 Timing:     After Request keyboard pull up Data and down Clock line to low for start bit.
45             After request host release Clock line once Data line becomes hi.
46             Host writes a bit while Clock is hi and Keyboard reads while low.
47 Stop bit:   Host releases or pulls up Data line to hi after 9th clock and waits for keyboard pull down the line to lo.
48 */
49 uint8_t ibm4704_send(uint8_t data)
50 {
51     bool parity = true; // odd parity
52     ibm4704_error = 0;
53
54     IBM4704_INT_OFF();
55
56     /* Request to send */
57     idle();
58     clock_lo();
59
60     /* wait for Start bit(Clock:lo/Data:hi) */
61     WAIT(data_hi, 300, 0x30);
62
63     /* Data bit */
64     for (uint8_t i = 0; i < 8; i++) {
65         WAIT(clock_hi, 100, 0x40+i);
66         if (data&(1<<i)) {
67             parity = !parity;
68             data_hi();
69         } else {
70             data_lo();
71         }
72         WAIT(clock_lo, 100, 0x48+i);
73     }
74
75     /* Parity bit */
76     WAIT(clock_hi, 100, 0x34);
77     if (parity) { data_hi(); } else { data_lo(); }
78     WAIT(clock_lo, 100, 0x35);
79
80     /* Stop bit */
81     WAIT(clock_hi, 100, 0x34);
82     data_hi();
83
84     /* End */
85     WAIT(data_lo, 100, 0x36);
86
87     idle();
88     IBM4704_INT_ON();
89     return 0;
90 ERROR:
91     idle();
92     if (ibm4704_error > 0x30) {
93         xprintf("S:%02X ", ibm4704_error);
94     }
95     IBM4704_INT_ON();
96     return -1;
97 }
98
99 /* wait forever to receive data */
100 uint8_t ibm4704_recv_response(void)
101 {
102     while (!rbuf_has_data()) {
103         _delay_ms(1);
104     }
105     return rbuf_dequeue();
106 }
107
108 uint8_t ibm4704_recv(void)
109 {
110     if (rbuf_has_data()) {
111         return rbuf_dequeue();
112     } else {
113         return -1;
114     }
115 }
116
117 /*
118 Keyboard to Host
119 ----------------
120 Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part.
121
122         ____       __   __   __   __   __   __   __   __   __   _______
123 Clock       \_____/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/
124              ____ ____ ____ ____ ____ ____ ____ ____ ____ ____    
125 Data    ____/    X____X____X____X____X____X____X____X____X____X________
126             Start   0    1    2    3    4    5    6    7    P  Stop
127
128 Start bit:  can be long as 300-350us.
129 Inhibit:    Pull Data line down to inhibit keyboard to send.
130 Timing:     Host reads bit while Clock is hi.(rising edge)
131 Stop bit:   Keyboard pulls down Data line to lo after 9th clock.
132 */
133 ISR(IBM4704_INT_VECT)
134 {
135     static enum {
136         BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, PARITY, STOP
137     } state = BIT0;
138     // LSB first
139     static uint8_t data = 0;
140     // Odd parity
141     static uint8_t parity = false;
142
143     ibm4704_error = 0;
144
145     switch (state) {
146         case BIT0:
147         case BIT1:
148         case BIT2:
149         case BIT3:
150         case BIT4:
151         case BIT5:
152         case BIT6:
153         case BIT7:
154             data >>= 1;
155             if (data_in()) {
156                 data |= 0x80;
157                 parity = !parity;
158             }
159             break;
160         case PARITY:
161             if (data_in()) {
162                 parity = !parity;
163             }
164             if (!parity)
165                 goto ERROR;
166             break;
167         case STOP:
168             // Data:Low
169             WAIT(data_lo, 100, state);
170             rbuf_enqueue(data);
171             ibm4704_error = IBM4704_ERR_NONE;
172             goto DONE;
173             break;
174         default:
175             goto ERROR;
176     }
177     state++;
178     goto RETURN;
179 ERROR:
180     ibm4704_error = state;
181     while (ibm4704_send(0xFE)) _delay_ms(1); // resend
182     xprintf("R:%02X%02X\n", state, data);
183 DONE:
184     state = BIT0;
185     data = 0;
186     parity = false;
187 RETURN:
188     return;
189 }