Files
retrobsd/sys/pic32/maximite/kbd.c
2014-04-09 14:27:18 +01:00

264 lines
8.4 KiB
C

/*
*
* RetroBSD - PS2 keyboard driver for the Maximite PIC32 board
*
* Copyright (C) 2011 Rob Judd <judd@ob-wan.com>
* All rights reserved. The three clause ("New" or "Modified")
* Berkeley software License Agreement specifies the terms and
* conditions for redistribution.
*
*/
/*
../kbd.c: In function 'initKBD':
../kbd.c:155:8: error: request for member 'ON' in something not a structure or union
../kbd.c:156:8: error: request for member 'CNPUE15' in something not a structure or union
../kbd.c:157:8: error: request for member 'CNPUE16' in something not a structure or union
../kbd.c: In function 'readKBD':
../kbd.c:173:9: error: request for member 'RD7' in something not a structure or union
../kbd.c:174:9: error: request for member 'RD6' in something not a structure or union
*
* This driver uses a 20uS timer, probably too fast
*/
#include "sys/types.h"
#include "machine/io.h"
#include "kbd.h"
//#define TRACE printf
#ifndef TRACE
#define TRACE(...)
#endif
// I2C registers
struct kbdreg {
//
};
#define USASCII 1
//#define RUSSIAN 1
#define true 1
#define false 0
#define PS2CLOCK 1//PORTD.RD6
#define PS2DATA 2//PORTD.RD7
#define QUEUE_SIZE 256
extern volatile char in_queue[QUEUE_SIZE];
extern volatile int in_queue_head, in_queue_tail;
volatile int abort;
#define POLL 20*(CPU_KHZ/1000) // # clock cycles for 20uS between keyboard reads
#define TIMEOUT 500*(CPU_KHZ/1000) // # clock cycles for 500uS timeout
enum {PS2START, PS2BIT, PS2PARITY, PS2STOP};
// Keyboard state machine and buffer
int state;
unsigned char key_buff;
int key_state, key_count, key_parity, key_timer;
// IBM keyboard scancode set 2 - Special Keys
#define F1 0x0e
#define F2 0x0f
#define F3 0x10
#define F4 0x11
#define F5 0x12
#define F6 0x13
#define F7 0x14 // maps to F5
#define F8 0x15
#define F9 0x16
#define F10 0x17
#define F11 0x18
#define F12 0x19
#define NUM 0x00
#define BKSP 0x08
#define TAB 0x09
#define L_ALT 0x11
#define L_SHF 0x12
#define L_CTL 0x14
#define CAPS 0x58
#define R_SHF 0x59
#define ENTER 0x0d
#define ESC 0x1b
#define SCRL 0x7e
#ifdef USASCII
#include "usascii.inc"
#elif defined RUSSIAN
#include "russian.inc"
#endif
/*
Standard PC init sequence:
Keyboard: AA Self-test passed ;Keyboard controller init
Host: ED Set/Reset Status Indicators
Keyboard: FA Acknowledge
Host: 00 Turn off all LEDs
Keyboard: FA Acknowledge
Host: F2 Read ID
Keyboard: FA Acknowledge
Keyboard: AB First byte of ID
Host: ED Set/Reset Status Indicators ;BIOS init
Keyboard: FA Acknowledge
Host: 02 Turn on Num Lock LED
Keyboard: FA Acknowledge
Host: F3 Set Typematic Rate/Delay ;Windows init
Keyboard: FA Acknowledge
Host: 20 500 ms / 30.0 reports/sec
Keyboard: FA Acknowledge
Host: F4 Enable
Keyboard: FA Acknowledge
Host: F3 Set Typematic Rate/delay
Keyboard: FA Acknowledge
Host: 00 250 ms / 30.0 reports/sec
Keyboard: FA Acknowledge
*/
char init_kbd(void)
{
// enable pullups on the clock and data lines.
// This stops them from floating and generating random chars when no keyboard is attached
// CNCON.ON = 1; // turn on Change Notice for interrupt
// CNPUE.CNPUE15 = 1; // turn on the pullup for pin D6 also called CN15
// CNPUE.CNPUE16 = 1; // turn on the pullup for pin D7 also called CN16
return false;
}
void read_kbd(void)
{
int data = PS2DATA;
int clock = PS2CLOCK;
static char key_up = false;
static unsigned char code = 0;
static unsigned then = 0;
unsigned now = mips_read_c0_register (C0_COUNT, 0);
// Is it time to poll the keyboard yet?
if ((int) (now - then) < POLL)
return;
else
then = now;
if (key_state) { // if clock was high, key_state = 1
if (!clock) { // PS2CLOCK == 0, falling edge detected
key_state = 0; // transition to state 0
key_timer = TIMEOUT; // restart the counter
switch(state){
default:
case PS2START:
if(!data) { // PS2DATA == 0
key_count = 8; // init bit counter
key_parity = 0; // init parity check
code = 0;
state = PS2BIT;
}
break;
case PS2BIT:
code >>= 1; // shift in data bit
if(data) // PS2DATA == 1
code |= 0x80;
key_parity ^= code;
if (--key_count == 0)
state = PS2PARITY; // all bits read
break;
case PS2PARITY:
if(data)
key_parity ^= 0x80;
if(key_parity & 0x80) // parity odd, continue
state = PS2STOP;
else
state = PS2START;
break;
case PS2STOP:
if(data) {
if(code == 0xf0)
key_up = true;
else {
char chr;
static char LShift = 0;
static char RShift = 0;
static char LCtrl = 0;
static char LAlt = 0;
static char CapsLock = 0;
if(key_up) { // check for special key release
key_up = false;
switch(code) {
case L_SHF: LShift = 0;
case R_SHF: RShift = 0;
case L_CTL: LCtrl = 0;
case L_ALT: LAlt = 0;
}
goto exit;
} else { // check for special key press
switch(code) {
case L_SHF: LShift = 1;
case R_SHF: RShift = 1;
case L_CTL: LCtrl = 1;
case L_ALT: LAlt = 1;
case CAPS: CapsLock = !CapsLock;
default: break;
goto exit;
}
}
if(LShift || RShift) // get the ASCII code
chr = lowerKey[code%128];
else
chr = upperKey[code%128];
if(!chr) // it was an unmapped key
break;
if(CapsLock && chr >= 'a' && chr <= 'z') // check for altered keys
chr -= 32;
if(LCtrl)
chr &= 0x1F;
in_queue[in_queue_head] = chr;
in_queue_head = (in_queue_head + 1) % QUEUE_SIZE;
if(chr == 3) { // check for CTL-C
in_queue_head = in_queue_tail = 0;
abort = true;
}
// PrintSignonToUSB = false; // show that the keyboard is in use
LAlt = LAlt; // not used yet
} // if key_up
exit:
code = 0;
} // if(data)
state = PS2START;
} // switch(state)
} // if(!clock)
} else // if(key_state)
key_state = 1; // PS2CLOCK == 1, rising edge detected
if ((key_timer -= POLL) <= 0)
state = PS2START; // timeout, reset state machine
return;
}
char write_kbd(u_char data)
{
// do something here
return false;
}