|  |  |  | /*
 | 
					
						
							|  |  |  | Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com> | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | #include <stdbool.h>
 | 
					
						
							|  |  |  | #include <util/delay.h>
 | 
					
						
							|  |  |  | #include "debug.h"
 | 
					
						
							|  |  |  | #include "ring_buffer.h"
 | 
					
						
							|  |  |  | #include "ibm4704.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define WAIT(stat, us, err) do { \
 | 
					
						
							|  |  |  |     if (!wait_##stat(us)) { \ | 
					
						
							|  |  |  |         ibm4704_error = err; \ | 
					
						
							|  |  |  |         goto ERROR; \ | 
					
						
							|  |  |  |     } \ | 
					
						
							|  |  |  | } while (0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint8_t ibm4704_error = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ibm4704_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     inhibit();  // keep keyboard from sending
 | 
					
						
							|  |  |  |     IBM4704_INT_INIT(); | 
					
						
							|  |  |  |     IBM4704_INT_ON(); | 
					
						
							|  |  |  |     idle();     // allow keyboard sending
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  | Host to Keyboard | 
					
						
							|  |  |  | ---------------- | 
					
						
							|  |  |  | Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ____        __   __   __   __   __   __   __   __   __   ________ | 
					
						
							|  |  |  | Clock       \______/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/ | 
					
						
							|  |  |  |             ^   ____ ____ ____ ____ ____ ____ ____ ____ ____ ____ ___ | 
					
						
							|  |  |  | Data    ____|__/    X____X____X____X____X____X____X____X____X____X   \___ | 
					
						
							|  |  |  |             |  Start   0    1    2    3    4    5    6    7    P   Stop | 
					
						
							|  |  |  |             Request by host | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Start bit:  can be long as 300-350us. | 
					
						
							|  |  |  | Request:    Host pulls Clock line down to request to send a command. | 
					
						
							|  |  |  | Timing:     After Request keyboard pull up Data and down Clock line to low for start bit. | 
					
						
							|  |  |  |             After request host release Clock line once Data line becomes hi. | 
					
						
							|  |  |  |             Host writes a bit while Clock is hi and Keyboard reads while low. | 
					
						
							|  |  |  | Stop bit:   Host releases or pulls up Data line to hi after 9th clock and waits for keyboard pull down the line to lo. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | uint8_t ibm4704_send(uint8_t data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool parity = true; // odd parity
 | 
					
						
							|  |  |  |     ibm4704_error = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     IBM4704_INT_OFF(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Request to send */ | 
					
						
							|  |  |  |     idle(); | 
					
						
							|  |  |  |     clock_lo(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* wait for Start bit(Clock:lo/Data:hi) */ | 
					
						
							|  |  |  |     WAIT(data_hi, 300, 0x30); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Data bit */ | 
					
						
							|  |  |  |     for (uint8_t i = 0; i < 8; i++) { | 
					
						
							|  |  |  |         WAIT(clock_hi, 100, 0x40+i); | 
					
						
							|  |  |  |         if (data&(1<<i)) { | 
					
						
							|  |  |  |             parity = !parity; | 
					
						
							|  |  |  |             data_hi(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             data_lo(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         WAIT(clock_lo, 100, 0x48+i); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Parity bit */ | 
					
						
							|  |  |  |     WAIT(clock_hi, 100, 0x34); | 
					
						
							|  |  |  |     if (parity) { data_hi(); } else { data_lo(); } | 
					
						
							|  |  |  |     WAIT(clock_lo, 100, 0x35); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Stop bit */ | 
					
						
							|  |  |  |     WAIT(clock_hi, 100, 0x34); | 
					
						
							|  |  |  |     data_hi(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* End */ | 
					
						
							|  |  |  |     WAIT(data_lo, 100, 0x36); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     idle(); | 
					
						
							|  |  |  |     IBM4704_INT_ON(); | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | ERROR: | 
					
						
							|  |  |  |     idle(); | 
					
						
							|  |  |  |     if (ibm4704_error > 0x30) { | 
					
						
							|  |  |  |         xprintf("S:%02X ", ibm4704_error); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     IBM4704_INT_ON(); | 
					
						
							|  |  |  |     return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* wait forever to receive data */ | 
					
						
							|  |  |  | uint8_t ibm4704_recv_response(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     while (!rbuf_has_data()) { | 
					
						
							|  |  |  |         _delay_ms(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return rbuf_dequeue(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint8_t ibm4704_recv(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (rbuf_has_data()) { | 
					
						
							|  |  |  |         return rbuf_dequeue(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  | Keyboard to Host | 
					
						
							|  |  |  | ---------------- | 
					
						
							|  |  |  | Data bits are LSB first and Parity is odd. Clock has around 60us high and 30us low part. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ____       __   __   __   __   __   __   __   __   __   _______ | 
					
						
							|  |  |  | Clock       \_____/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/  \_/ | 
					
						
							|  |  |  |              ____ ____ ____ ____ ____ ____ ____ ____ ____ ____     | 
					
						
							|  |  |  | Data    ____/    X____X____X____X____X____X____X____X____X____X________ | 
					
						
							|  |  |  |             Start   0    1    2    3    4    5    6    7    P  Stop | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Start bit:  can be long as 300-350us. | 
					
						
							|  |  |  | Inhibit:    Pull Data line down to inhibit keyboard to send. | 
					
						
							|  |  |  | Timing:     Host reads bit while Clock is hi.(rising edge) | 
					
						
							|  |  |  | Stop bit:   Keyboard pulls down Data line to lo after 9th clock. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | ISR(IBM4704_INT_VECT) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     static enum { | 
					
						
							|  |  |  |         BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, PARITY, STOP | 
					
						
							|  |  |  |     } state = BIT0; | 
					
						
							|  |  |  |     // LSB first
 | 
					
						
							|  |  |  |     static uint8_t data = 0; | 
					
						
							|  |  |  |     // Odd parity
 | 
					
						
							|  |  |  |     static uint8_t parity = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ibm4704_error = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (state) { | 
					
						
							|  |  |  |         case BIT0: | 
					
						
							|  |  |  |         case BIT1: | 
					
						
							|  |  |  |         case BIT2: | 
					
						
							|  |  |  |         case BIT3: | 
					
						
							|  |  |  |         case BIT4: | 
					
						
							|  |  |  |         case BIT5: | 
					
						
							|  |  |  |         case BIT6: | 
					
						
							|  |  |  |         case BIT7: | 
					
						
							|  |  |  |             data >>= 1; | 
					
						
							|  |  |  |             if (data_in()) { | 
					
						
							|  |  |  |                 data |= 0x80; | 
					
						
							|  |  |  |                 parity = !parity; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case PARITY: | 
					
						
							|  |  |  |             if (data_in()) { | 
					
						
							|  |  |  |                 parity = !parity; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (!parity) | 
					
						
							|  |  |  |                 goto ERROR; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case STOP: | 
					
						
							|  |  |  |             // Data:Low
 | 
					
						
							|  |  |  |             WAIT(data_lo, 100, state); | 
					
						
							|  |  |  |             rbuf_enqueue(data); | 
					
						
							|  |  |  |             ibm4704_error = IBM4704_ERR_NONE; | 
					
						
							|  |  |  |             goto DONE; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             goto ERROR; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     state++; | 
					
						
							|  |  |  |     goto RETURN; | 
					
						
							|  |  |  | ERROR: | 
					
						
							|  |  |  |     ibm4704_error = state; | 
					
						
							|  |  |  |     while (ibm4704_send(0xFE)) _delay_ms(1); // resend
 | 
					
						
							|  |  |  |     xprintf("R:%02X%02X\n", state, data); | 
					
						
							|  |  |  | DONE: | 
					
						
							|  |  |  |     state = BIT0; | 
					
						
							|  |  |  |     data = 0; | 
					
						
							|  |  |  |     parity = false; | 
					
						
							|  |  |  | RETURN: | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  | } |