text: /*
   * This file is part of the Bus Pirate project (http://code.google.com/p/the-bus-pirate/).
   *
   * Written and maintained by the Bus Pirate project.
   *
   * To the extent possible under law, the project has
   * waived all copyright and related or neighboring rights to Bus Pirate. This
   * work is published from United States.
   *
   * For details see: http://creativecommons.org/publicdomain/zero/1.0/.
   *
   *    This program is distributed in the hope that it will be useful,
   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   */
  
  #include "base.h"
  //#include "pc_at_keyboard.h"
  
  #define KBCLK_TRIS 	BP_CLK_DIR
  #define KBCLK 			BP_CLK
  
  #define KBDIO_TRIS 	BP_MOSI_DIR
  #define KBDIO 			BP_MOSI
  
  extern struct _modeConfig modeConfig;
  extern struct _command bpCommand;
  
  #ifdef BP_USE_PCATKB
  
  void kbSetup(void);
  void KEYBsetup_exc(void);
  unsigned char kbReadBit(void);
  unsigned char kbReadCode(void);
  unsigned char kbReadByte(void);
  unsigned char kbWriteByte(unsigned char c);
  unsigned char kbWriteBit(unsigned char c);
  void kbScancodeResults(unsigned char c);
  unsigned char kbWaitClock(unsigned char c);
  
  struct _kbframe{
  	unsigned char startbit:1;
  	unsigned char code;
  	unsigned char paritybit:1;
  	unsigned char stopbit:1;
  	unsigned char parityerror:1;
  } kbScancode;
  
  
  void KEYBsetup(void)
  {	modeConfig.HiZ=1;//yes, always HiZ
  }
  
  void KEYBsetup_exc(void)
  {
      kbSetup();
  }    
  
  unsigned int KEYBread(void)
  {	kbScancodeResults(kbReadByte());
  	return kbScancode.code;
  }
  
  unsigned int KEYBwrite(unsigned int c)
  {	unsigned char temp;
  
  	temp=kbWriteByte(c);//send to bus
  	if(temp==0) //ack bit
  	{	//bpWmessage(MSG_ACK);
  		BPMSG1060;
  	}
  	else if (temp==1)
  	{	//bpWmessage(MSG_NACK);
  		BPMSG1061;
  	}
  	else
  	{	//bpWstring(OUMSG_KB_TIMEOUT);
  		BPMSG1237;
  	}
  	return 0x100;
  }
  
  void KEYBmacro(unsigned char c)
  {	switch(c)
  	{	case 0:	//bpWline(OUMSG_KB_MACRO_MENU);
  				BPMSG1238;
  				break;
  		case 1:	//bpWline(OUMSG_KB__MACRO_LIVE);
  				BPMSG1239;
  				BPMSG1250;
  				while(1)
  				{	if(kbReadByte()==0)
  					{	bpWbyte(kbScancode.code);
  						bpSP;
  					}
  					if(UART1RXRdy() == 1) //any key pressed, exit
  					{	c=UART1RX(); /* JTR usb port; */
  						bpBR;
  						break;
  					}						
  				}
  				break;
  		default: BPMSG1016;
  	}
  }
  
  void kbScancodeResults(unsigned char c)
  {	switch(c)
  	{	case 0:		// all ok
  			//bpWbyte(kbScancode.code);
  			//bpSP;		
  			break;
  		case 1:
  			//bpWstring(OUMSG_KB_ERROR_STARTBIT);
  			BPMSG1240;				
  			break;
  		case 2:
  			//bpWbyte(kbScancode.code);
  			//bpWstring(OUMSG_KB_ERROR_PARITY);
  			BPMSG1241;
  			break;
  		case 3:
  			//bpWbyte(kbScancode.code);
  			//bpWstring(OUMSG_KB_ERROR_STOPBIT);
  			BPMSG1242;
  			break;
  		case 4:
  			//bpWstring(OUMSG_KB_ERROR_TIMEOUT);
  			bpSP;
  			BPMSG1237;
  			break;
  		case 0xff://no data
  			//bpWstring(OUMSG_KB_ERROR_NONE);				
  			BPMSG1243;
  			break;
  		default:
  			//bpWstring(OUMSG_KB_ERROR_UNK);				
  			BPMSG1244;
  			break;
  	}
  }
  
  void kbSetup(void){
  	//writes to the PORTs write to the LATCH
  	KBDIO_TRIS=1;//data input
  	KBCLK_TRIS=0;//clock output/low
  	KBCLK=0;			//B8 scl 
  	KBDIO=0;			//B9 sda
  }
  
  
  unsigned char kbReadBit(void){
  	unsigned char j;
  
  	if(kbWaitClock(0)!=0) return 2; //wait for clock High->LOW (ACTIVE)
  
  	j=KBDIO; //read the data pin
  
  	if(kbWaitClock(1)!=0) return 2; //wait for clock low->HIGH (IDLE)
  
  	return j;
  }
  
  //reads scancode into kbScancode structure
  //returns 0 - success
  //1 - startbit error
  //2 - parity error
  //3 - stopbit error
  //4 - read bit timeout error
  unsigned char kbReadCode(void){
  	unsigned char i, par=0,c=0, b;
  
  	//get startbit
  	kbScancode.startbit=KBDIO;
  	if(kbScancode.startbit!=0) return 1;//startbit should be 0
  
  	//while(KBCLK==0);//wait for clock HIGH (IDLE)
  	if(kbWaitClock(1)!=0) return 4;
  
  	//grab the eight databits LSB
  	for(i=0;i<8;i++){
  		c=c>>1;
  		b=kbReadBit();
  		if(b==2) return 4; //bit timeout
  		if(b==1){
  			c+=0b10000000;
  			par^=1;
  		}
  	}
  	kbScancode.code=c;
  
  	//get parity bit and check
  	b=kbReadBit();
  	if(b==2) return 4; //bit timeout
  	kbScancode.paritybit=b;
  	//odd parity:if these are the same, there is an error
  	//condense to bitwise op...
  	if(kbScancode.paritybit==par){
  		kbScancode.parityerror=1;
  		return 2; //error
   	}else{
  		kbScancode.parityerror=0;//ok
  	}
  
  	//get stopbit
  	b=kbReadBit();
  	if(b==2) return 4; //bit timeout
  	kbScancode.stopbit=b;//stopbit should be 1
  	KBCLK_TRIS=0;//set clock low again to idle keyboard
  	if(kbScancode.stopbit!=1) return 3;
  	
  	//success!
  	return 0;
  }
  
  unsigned char kbReadByte(void){
  	unsigned char i,c=0xff;
  
  	//let clk float, clk and data will drop if there is data	
  	KBDIO_TRIS=1;//data input
  	KBCLK_TRIS=1;//clock input/high (keyboard will go low if scan code ready)
  
  	for(i=0;i<255;i++){//do a loop, wait for change
  	//while(1){//for debugging
  		if(KBCLK==0){//check clock status
  			c=kbReadCode();//read the scancode
  			if(c==0 || c==4)break;//success or error, break the loop
  		}
  		bpDelayUS(5);//delay
  	}
  
  	//disable any further data by dropping clock line
  	KBCLK_TRIS=0;//set clock low again
  
  	return c;
  }
  
  //writes a bit using the KB clock signal.
  // 0=success, 2=timeout
  unsigned char kbWriteBit(unsigned char c){
  
  	if(kbWaitClock(1)!=0) return 2; //wait for low-high transition
  	KBDIO_TRIS=c;//set data direction
  	if(kbWaitClock(0)!=0) return 2; //wait for high-low transition
  	return 0;	//data now entered, you have a tiny bit of time before next bit
  }
  
  //@c - byte to send
  //@returns - ack bit
  unsigned char kbWriteByte(unsigned char c){
  	unsigned char par=0;
  	unsigned int i;
  
  	//take clk low to own the bus
  	//clock should already be low
  	KBCLK_TRIS=0;//clock low
  	KBCLK=0;
  
  	bpDelayUS(60);//delay at least 1 bit period (60us), interrupt any other data transfer
  
  	//data low to signal host->kb transfer
  	KBDIO=0;
  	KBDIO_TRIS=0;//data low
  
  	//take clock high again, KB will start own clock
  	KBCLK_TRIS=1;//clock high/input
  	bpDelayUS(1);
  
  	//while(KBCLK==1);//wait for first falling edge
  	if(kbWaitClock(0)!=0) return 4; //timeout
  
  	//bang out the bits LSB first out using the kb clock signal
  	for(i=0;i<8;i++){
  		if((c&1)==1){
  			if(kbWriteBit(1)!=0)return 4;//send LSB first
  			par^=1;//track parity bit
  		}else{
  			if(kbWriteBit(0)!=0)return 4;//send LSB first
  		}		
  		c=c>>1;//-- Shift next bit into position		
  	}
  	
  	//write parity bit
  	par^=1;//shift parity bit once more so it is opposite
  	if(kbWriteBit(par)!=0)return 4;//send parity bit
  
  	KBDIO_TRIS=1;//configure data for input
  	KBDIO=0;
  
  	//while(KBCLK==0);//wait for low->high transition to align with a readbit 
  	if(kbWaitClock(1)!=0) return 4; //timeout
  
  	if(kbReadBit()==2) return 4;//junk bit (stopbit/high)
  	i=kbReadBit();//ack bit
  	if(i==2) return 4;
  
  	KBCLK_TRIS=0;//set clock low again to halt data
  	KBCLK=0;
  
  	return i;//return ack bit
  }
  
  
  unsigned char kbWaitClock(unsigned char c){
  	unsigned int i=0xffff;
  	while(1){
  		if(KBCLK==c)return 0; //clock changed
  		i--;
  		if(i==0)return 1;//timeout
  	}
  }
  
  #endif
  