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