summaryrefslogtreecommitdiffstats
path: root/app/atkbd.c
blob: 0dfc9c8d69258a8fc7f2c42e7b01e35f140a1407 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
#include "project.h"

#define KBCLK		(1UL << 0)
#define KBCLK_PORT	GPIOA
#define KBCLK_IRQ	NVIC_EXTI0_IRQ

#define KBDAT		(1UL << 1)
#define KBDAT_PORT	GPIOA

/*Cope with lost sync */
/*If we've had no bits for this long, it's definately the start of a new word,  72MHz units */
#define RESYNC_GAP 	(7200000) /* 100ms */

#define ATKBD_TIMEOUT	1000 /*how long to wait for the keyboard to respond to a command in ms */


typedef enum {
	STATE_START=0,
	STATE_BIT0,
	STATE_BIT1,
	STATE_BIT2,
	STATE_BIT3,
	STATE_BIT4,
	STATE_BIT5,
	STATE_BIT6,
	STATE_BIT7,
	STATE_PARITY,
	STATE_STOP,
} atkbd_state;


#define ATKBD_CMD_SETLEDS       0xed
#define ATKBD_CMD_GSCANSET      0xf0
#define ATKBD_CMD_SSCANSET      0xf0
#define ATKBD_CMD_GETID         0xf2
#define ATKBD_CMD_SETREP        0xf3
#define ATKBD_CMD_ENABLE        0xf4
#define ATKBD_CMD_RESET_DIS     0xf5
#define ATKBD_CMD_RESET_DEF     0xf6
#define ATKBD_CMD_SETALL_MB     0xf8
#define ATKBD_CMD_SETALL_MBR    0xfa 
#define ATKBD_CMD_RESET_BAT     0xff
#define ATKBD_CMD_RESEND        0xfe
#define ATKBD_CMD_EX_ENABLE     0xea
#define ATKBD_CMD_EX_SETLEDS    0xeb
#define ATKBD_CMD_OK_GETID      0xe8

#define ATKBD_RET_ECHO          0xee
#define ATKBD_RET_ACK           0xfa
#define ATKBD_RET_NAK           0xfe
#define ATKBD_RET_BAT           0xaa
#define ATKBD_RET_EMUL0         0xe0
#define ATKBD_RET_EMUL1         0xe1
#define ATKBD_RET_RELEASE       0xf0
#define ATKBD_RET_HANJA         0xf1
#define ATKBD_RET_HANGEUL       0xf2
#define ATKBD_RET_ERR           0xff

#define ATKBD_KEY_UNKNOWN       0
#define ATKBD_KEY_NULL          255


static int atkbd_ack;
static int atkbd_nack;
static int atkbd_bat;
static int atkbd_echo;



static uint32_t cycle_diff(uint32_t a,uint32_t b)
{
return b-a;
}


static void atkbd_dispatch(int emul,int key,int updown)
{

printf("KEY> %x %x %x\r\n",emul,key,updown);


}

static void atkbd_data_dispatch(uint8_t byte)
{
static int release;
static int emul;

printf("ATKBD < 0x%02x\r\n",byte);

switch (byte) {
case ATKBD_RET_ACK:
	atkbd_ack++;
	break;
case ATKBD_RET_NAK:
	atkbd_nack++;
	break;
case ATKBD_RET_BAT:
	atkbd_bat++;
	break;
case ATKBD_RET_ECHO:
	atkbd_echo++;
	break;
case ATKBD_RET_ERR:
case ATKBD_KEY_UNKNOWN:
case ATKBD_RET_HANJA: /*Don't handle japanese or korean for the moment*/
case ATKBD_RET_HANGEUL: /*Don't handle japanese or korean for the moment*/
	/*All these need no action */
	break;
case ATKBD_RET_EMUL0:
	emul=1;
	break;
case ATKBD_RET_EMUL1:
	emul=2;
	break;
case ATKBD_RET_RELEASE:
	release=1;
	break;
default:
	atkbd_dispatch(emul,byte,release);
	emul=0;
	release=0;
}
	
}


void exti0_isr(void)
{
 static uint32_t last_interrupt=0;
 static atkbd_state state=STATE_START;
 static uint8_t atkbd_byte;
 static int parity;

 uint32_t now,diff;
 int d;

 d=!!GET(KBDAT);

 exti_reset_request(KBCLK);

 now=dwt_read_cycle_counter();
 diff=cycle_diff(last_interrupt,now);
 last_interrupt=now;


 if (diff>RESYNC_GAP) state=STATE_START;


 switch (state) {
	case STATE_START:
		atkbd_byte=0;
		parity=0;
		if (!d) state++;
		break;
	case STATE_BIT0:
	case STATE_BIT1:
	case STATE_BIT2:
	case STATE_BIT3:
	case STATE_BIT4:
	case STATE_BIT5:
	case STATE_BIT6:
	case STATE_BIT7:
		atkbd_byte|=d<<(state-STATE_BIT0);
		/* fall through*/
	case STATE_PARITY:
		parity ^= d;
		state++;
		break;
	case STATE_STOP:
		if (d && parity) atkbd_data_dispatch(atkbd_byte);
		state=STATE_START;
		break;
	}
}



void atkbd_set(int clk,int dat)
{
if (clk) {
	MAP_INPUT_PU(KBCLK);
} else {
	CLEAR(KBCLK);
	MAP_OUTPUT_PP(KBCLK);
}


if (dat) {
	MAP_INPUT_PU(KBDAT);
} else {
	CLEAR(KBDAT);
	MAP_OUTPUT_PP(KBDAT);
}

}


int atkbd_send(uint8_t d)
{
uint32_t then=ticks;
int parity=1;
uint32_t c;


nvic_disable_irq(KBCLK_IRQ);

#define PS2_CLOCK_SEIZE_DELAY 200
#define PS2_BIT_DELAY	10

atkbd_set(0,1);
delay_us(PS2_CLOCK_SEIZE_DELAY);
atkbd_set(0,0);
delay_us(PS2_BIT_DELAY);
atkbd_set(1,0);
delay_us(PS2_BIT_DELAY);

for (c=1;c<0x100;c<<=1)  {
while (GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;
atkbd_set(1,c&d);
parity^=!!(c&d);
while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;
}
while (GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;
atkbd_set(1,parity);
while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;

while (GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;
while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;

atkbd_set(1,1);
while (GET(KBDAT)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;
while (GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;



while (!GET(KBDAT)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;
while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;

exti_reset_request(KBCLK);
nvic_enable_irq(KBCLK_IRQ);

printf("ATKBD > 0x%02x\r\n",d);

return 0;

err:
atkbd_set(1,1);
while (!GET(KBDAT)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;
while (!GET(KBCLK)) if (timed_out(then,ATKBD_TIMEOUT)) goto err;

exti_reset_request(KBCLK);
nvic_enable_irq(KBCLK_IRQ);

printf("ATKBD >! 0x%02x\r\n",d);
return -1;
}

static int atkbd_reset(void)
{
uint32_t then=ticks;
atkbd_bat=0;
atkbd_send(ATKBD_CMD_RESET_BAT);
while (!atkbd_bat) if (timed_out(then,ATKBD_TIMEOUT)) return -1;

then=ticks;
atkbd_ack=0;
atkbd_send(ATKBD_CMD_SETALL_MBR);
while (!atkbd_ack) if (timed_out(then,ATKBD_TIMEOUT)) return -1;


return 0;
}

int atkbd_request_echo(void)
{
uint32_t then=ticks;
atkbd_ack=0;
atkbd_send(ATKBD_CMD_ECHO);
while (!atkbd_echo) if (timed_out(then,ATKBD_TIMEOUT)) return -1;

return 0;
}


int atkbd_set_leds(uint8_t leds)
{
uint32_t then=ticks;
atkbd_ack=0;
atkbd_send(ATKBD_CMD_SETLEDS);
while (!atkbd_ack) if (timed_out(then,ATKBD_TIMEOUT)) return -1;

then=ticks;
atkbd_ack=0;
atkbd_send(leds);
while (!atkbd_ack) if (timed_out(then,ATKBD_TIMEOUT)) return -1;

return 0;
}


void atkbd_init(void)
{
atkbd_set(1,1);
delay_ms(200);

nvic_enable_irq(NVIC_EXTI0_IRQ);


exti_select_source(KBCLK, KBCLK_PORT);
exti_set_trigger(KBCLK, EXTI_TRIGGER_FALLING);
exti_enable_request(KBCLK);

exti_reset_request(KBCLK);
nvic_enable_irq(KBCLK_IRQ);


atkbd_reset();
atkbd_request_echo();
atkbd_set_leds(0x07);


}