2015-04-09 16:32:04 +00:00
/*
Copyright 2010 , 2011 , 2012 , 2013 Jun WAKO < wakojun @ gmail . com >
*/
# include <stdbool.h>
# include <util/delay.h>
# include "debug.h"
2014-10-27 12:39:02 +00:00
# include "ring_buffer.h"
2015-04-09 16:32:04 +00:00
# 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 )
{
2014-10-27 12:39:02 +00:00
IBM4704_INT_INIT ( ) ;
IBM4704_INT_ON ( ) ;
idle ( ) ;
2015-04-09 16:32:04 +00:00
}
/*
Host to Keyboard
- - - - - - - - - - - - - - - -
Data bits are LSB first and Parity is odd . Clock has around 60u s high and 30u s 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 - 350u s .
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 9 th 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 ;
2014-10-27 12:39:02 +00:00
IBM4704_INT_OFF ( ) ;
2015-04-09 16:32:04 +00:00
/* 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 ) ;
2014-10-27 12:39:02 +00:00
idle ( ) ;
IBM4704_INT_ON ( ) ;
2015-04-09 16:32:04 +00:00
return 0 ;
ERROR :
2014-10-27 12:39:02 +00:00
idle ( ) ;
if ( ibm4704_error > 0x30 ) {
xprintf ( " S:%02X " , ibm4704_error ) ;
2015-04-09 16:32:04 +00:00
}
2014-10-27 12:39:02 +00:00
IBM4704_INT_ON ( ) ;
2015-04-09 16:32:04 +00:00
return - 1 ;
}
2014-10-27 12:39:02 +00:00
/* wait forever to receive data */
2015-04-09 16:32:04 +00:00
uint8_t ibm4704_recv_response ( void )
{
2014-10-27 12:39:02 +00:00
while ( ! rbuf_has_data ( ) ) {
_delay_ms ( 1 ) ;
}
return rbuf_dequeue ( ) ;
2015-04-09 16:32:04 +00:00
}
2015-05-14 06:38:15 +00:00
uint8_t ibm4704_recv ( void )
{
if ( rbuf_has_data ( ) ) {
return rbuf_dequeue ( ) ;
} else {
return - 1 ;
}
}
2015-04-09 16:32:04 +00:00
/*
Keyboard to Host
- - - - - - - - - - - - - - - -
Data bits are LSB first and Parity is odd . Clock has around 60u s high and 30u s low part .
2015-05-14 06:38:15 +00:00
____ __ __ __ __ __ __ __ __ __ _______
Clock \ _____ / \ _ / \ _ / \ _ / \ _ / \ _ / \ _ / \ _ / \ _ / \ _ /
2015-04-09 16:32:04 +00:00
____ ____ ____ ____ ____ ____ ____ ____ ____ ____
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 - 350u s .
Inhibit : Pull Data line down to inhibit keyboard to send .
2015-05-14 06:38:15 +00:00
Timing : Host reads bit while Clock is hi . ( rising edge )
2015-04-09 16:32:04 +00:00
Stop bit : Keyboard pulls down Data line to lo after 9 th clock .
*/
2014-10-27 12:39:02 +00:00
ISR ( IBM4704_INT_VECT )
{
static enum {
2015-05-14 06:38:15 +00:00
STOP , BIT0 , BIT1 , BIT2 , BIT3 , BIT4 , BIT5 , BIT6 , BIT7 , PARITY
} state = STOP ;
2014-10-27 12:39:02 +00:00
// LSB first
static uint8_t data = 0 ;
// Odd parity
static uint8_t parity = false ;
2015-04-09 16:32:04 +00:00
2014-10-27 12:39:02 +00:00
ibm4704_error = 0 ;
2015-05-14 06:38:15 +00:00
switch ( state + + ) {
case STOP :
2014-10-27 12:39:02 +00:00
// Data:Low
2015-05-14 06:38:15 +00:00
WAIT ( data_lo , 10 , state ) ;
2014-10-27 12:39:02 +00:00
break ;
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 ;
rbuf_enqueue ( data ) ;
ibm4704_error = IBM4704_ERR_NONE ;
goto DONE ;
break ;
default :
goto ERROR ;
2015-04-09 16:32:04 +00:00
}
2014-10-27 12:39:02 +00:00
goto RETURN ;
ERROR :
ibm4704_error = state ;
while ( ibm4704_send ( 0xFE ) ) _delay_ms ( 1 ) ; // resend
2014-10-28 05:41:45 +00:00
xprintf ( " R:%02X%02X \n " , state , data ) ;
2014-10-27 12:39:02 +00:00
DONE :
2015-05-14 06:38:15 +00:00
state = STOP ;
2014-10-27 12:39:02 +00:00
data = 0 ;
parity = false ;
RETURN :
return ;
2015-04-09 16:32:04 +00:00
}