plan 9 kernel history: overview | file list | diff list

2000/0401/alphapc/kbd.c (diff list | history)

alphapc/kbd.c on 1999/0415
1999/0415    
#include	"u.h" 
#include	"../port/lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
#include	"../port/error.h" 
 
enum { 
	Data=		0x60,		/* data port */ 
 
	Status=		0x64,		/* status port */ 
	 Inready=	0x01,		/*  input character ready */ 
	 Outbusy=	0x02,		/*  output busy */ 
	 Sysflag=	0x04,		/*  system flag */ 
	 Cmddata=	0x08,		/*  cmd==0, data==1 */ 
	 Inhibit=	0x10,		/*  keyboard/mouse inhibited */ 
	 Minready=	0x20,		/*  mouse character ready */ 
	 Rtimeout=	0x40,		/*  general timeout */ 
	 Parity=	0x80, 
 
	Cmd=		0x64,		/* command port (write only) */ 
 
	Spec=		0x80, 
 
	PF=		Spec|0x20,	/* num pad function key */ 
	View=		Spec|0x00,	/* view (shift window up) */ 
2000/0108    
	KF=		0xF000,	/* function key (begin Unicode private space) */ 
1999/0415    
	Shift=		Spec|0x60, 
	Break=		Spec|0x61, 
	Ctrl=		Spec|0x62, 
	Latin=		Spec|0x63, 
	Caps=		Spec|0x64, 
	Num=		Spec|0x65, 
	Middle=		Spec|0x66, 
	No=		0x00,		/* peter */ 
 
	Home=		KF|13, 
	Up=		KF|14, 
	Pgup=		KF|15, 
	Print=		KF|16, 
2000/0108    
	Left=		KF|17, 
	Right=		KF|18, 
1999/0415    
	End=		'\r', 
	Down=		View, 
2000/0108    
	Pgdown=		KF|19, 
1999/0415    
	Ins=		KF|20, 
2000/0108    
	Scroll=	KF|21, 
1999/0415    
	Del=		0x7F, 
}; 
 
2000/0108    
/* 
 * The codes at 0x79 and 0x81 are produed by the PFU Happy Hacking keyboard. 
 * A 'standard' keyboard doesn't produce anything above 0x58. 
 */ 
Rune kbtab[] =  
1999/0415    
{ 
[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6', 
[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t', 
[0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i', 
[0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's', 
[0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';', 
[0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v', 
[0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*', 
[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5, 
2000/0108    
[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7', 
1999/0415    
[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1', 
[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11, 
[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No, 
1999/0514    
[0x60]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x68]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x70]	No,	No,	No,	No,	No,	No,	No,	No, 
2000/0108    
[0x78]	No,	View,	No,	Up,	No,	No,	No,	No, 
1999/0415    
}; 
 
2000/0108    
Rune kbtabshift[] = 
1999/0415    
{ 
[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^', 
[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t', 
[0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I', 
[0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S', 
[0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':', 
[0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V', 
[0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*', 
[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5, 
2000/0108    
[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7', 
1999/0415    
[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1', 
[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11, 
[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No, 
1999/0514    
[0x60]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x68]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x70]	No,	No,	No,	No,	No,	No,	No,	No, 
2000/0108    
[0x78]	No,	Up,	No,	Up,	No,	No,	No,	No, 
1999/0415    
}; 
 
2000/0108    
Rune kbtabesc1[] = 
1999/0415    
{ 
[0x00]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x08]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x10]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No, 
[0x20]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No, 
[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print, 
[0x38]	Latin,	No,	No,	No,	No,	No,	No,	No, 
[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home, 
[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End, 
[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No, 
[0x58]	No,	No,	No,	No,	No,	No,	No,	No, 
1999/0514    
[0x60]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x68]	No,	No,	No,	No,	No,	No,	No,	No, 
[0x70]	No,	No,	No,	No,	No,	No,	No,	No, 
2000/0108    
[0x78]	No,	Up,	No,	No,	No,	No,	No,	No, 
1999/0415    
}; 
 
enum 
{ 
	/* controller command byte */ 
	Cscs1=		(1<<6),		/* scan code set 1 */ 
	Cauxdis=	(1<<5),		/* mouse disable */ 
	Ckbddis=	(1<<4),		/* kbd disable */ 
	Csf=		(1<<2),		/* system flag */ 
	Cauxint=	(1<<1),		/* mouse interrupt enable */ 
	Ckbdint=	(1<<0),		/* kbd interrupt enable */ 
}; 
 
static Lock i8042lock; 
static uchar ccc; 
static void (*auxputc)(int, int); 
 
/* 
 *  wait for output no longer busy 
 */ 
static int 
outready(void) 
{ 
	int tries; 
 
	for(tries = 0; (inb(Status) & Outbusy); tries++){ 
		if(tries > 500) 
			return -1; 
		delay(2); 
	} 
	return 0; 
} 
 
/* 
 *  wait for input 
 */ 
static int 
inready(void) 
{ 
	int tries; 
 
	for(tries = 0; !(inb(Status) & Inready); tries++){ 
		if(tries > 500) 
			return -1; 
		delay(2); 
	} 
	return 0; 
} 
 
/* 
 *  ask 8042 to reset the machine 
 */ 
void 
i8042reset(void) 
{ 
	ushort *s = KADDR(0x472); 
	int i, x; 
 
	*s = 0x1234;		/* BIOS warm-boot flag */ 
 
	/* 
	 *  newer reset the machine command 
	 */ 
	outready(); 
	outb(Cmd, 0xFE); 
	outready(); 
 
	/* 
	 *  Pulse it by hand (old somewhat reliable) 
	 */ 
	x = 0xDF; 
	for(i = 0; i < 5; i++){ 
		x ^= 1; 
		outready(); 
		outb(Cmd, 0xD1); 
		outready(); 
		outb(Data, x);	/* toggle reset */ 
		delay(100); 
	} 
} 
 
int 
i8042auxcmd(int cmd) 
{ 
	unsigned int c; 
	int tries; 
 
	c = 0; 
	tries = 0; 
 
	ilock(&i8042lock); 
	do{ 
		if(tries++ > 2) 
			break; 
		if(outready() < 0) 
			break; 
		outb(Cmd, 0xD4); 
		if(outready() < 0) 
			break; 
		outb(Data, cmd); 
		if(outready() < 0) 
			break; 
		if(inready() < 0) 
			break; 
		c = inb(Data); 
	} while(c == 0xFE || c == 0); 
	iunlock(&i8042lock); 
 
	if(c != 0xFA){ 
		print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd); 
		return -1; 
	} 
	return 0; 
} 
 
/* 
 *  keyboard interrupt 
 */ 
static void 
i8042intr(Ureg*, void*) 
{ 
	int s, c, i; 
	static int esc1, esc2; 
	static int alt, caps, ctl, num, shift; 
	static int collecting, nk; 
2000/0108    
	static Rune kc[5]; 
1999/0415    
	int keyup; 
 
	/* 
	 *  get status 
	 */ 
	lock(&i8042lock); 
	s = inb(Status); 
	if(!(s&Inready)){ 
		unlock(&i8042lock); 
		return; 
	} 
 
	/* 
	 *  get the character 
	 */ 
	c = inb(Data); 
	unlock(&i8042lock); 
 
	/* 
	 *  if it's the aux port... 
	 */ 
	if(s & Minready){ 
		if(auxputc != nil) 
			auxputc(c, shift); 
		return; 
	} 
 
	/* 
	 *  e0's is the first of a 2 character sequence 
	 */ 
	if(c == 0xe0){ 
		esc1 = 1; 
		return; 
	} else if(c == 0xe1){ 
		esc2 = 2; 
		return; 
	} 
 
	keyup = c&0x80; 
	c &= 0x7f; 
	if(c > sizeof kbtab){ 
		c |= keyup; 
		if(c != 0xFF)	/* these come fairly often: CAPSLOCK U Y */ 
			print("unknown key %ux\n", c); 
		return; 
	} 
 
	if(esc1){ 
		c = kbtabesc1[c]; 
		esc1 = 0; 
	} else if(esc2){ 
		esc2--; 
		return; 
	} else if(shift) 
		c = kbtabshift[c]; 
	else 
		c = kbtab[c]; 
 
	if(caps && c<='z' && c>='a') 
		c += 'A' - 'a'; 
 
	/* 
	 *  keyup only important for shifts 
	 */ 
	if(keyup){ 
		switch(c){ 
		case Latin: 
			alt = 0; 
			break; 
		case Shift: 
			shift = 0; 
			break; 
		case Ctrl: 
			ctl = 0; 
			break; 
		} 
		return; 
	} 
 
	/* 
 	 *  normal character 
	 */ 
2000/0108    
	if(!(c & (Spec|KF))){ 
1999/0415    
		if(ctl){ 
			if(alt && c == Del) 
				exit(0); 
			c &= 0x1f; 
		} 
		if(!collecting){ 
			kbdputc(kbdq, c); 
			return; 
		} 
		kc[nk++] = c; 
		c = latin1(kc, nk); 
		if(c < -1)	/* need more keystrokes */ 
			return; 
		if(c != -1)	/* valid sequence */ 
			kbdputc(kbdq, c); 
		else	/* dump characters */ 
			for(i=0; i<nk; i++) 
				kbdputc(kbdq, kc[i]); 
		nk = 0; 
		collecting = 0; 
		return; 
	} else { 
		switch(c){ 
		case Caps: 
			caps ^= 1; 
			return; 
		case Num: 
			num ^= 1; 
			return; 
		case Shift: 
			shift = 1; 
			return; 
		case Latin: 
			alt = 1; 
			collecting = 1; 
			nk = 0; 
			return; 
		case Ctrl: 
			ctl = 1; 
			return; 
		} 
	} 
	kbdputc(kbdq, c); 
} 
 
void 
i8042auxenable(void (*putc)(int, int)) 
{ 
	char *err = "i8042: aux init failed\n"; 
 
	/* enable kbd/aux xfers and interrupts */ 
	ccc &= ~Cauxdis; 
	ccc |= Cauxint; 
 
	ilock(&i8042lock); 
	if(outready() < 0) 
		print(err); 
	outb(Cmd, 0x60);			/* write control register */ 
	if(outready() < 0) 
		print(err); 
	outb(Data, ccc); 
	if(outready() < 0) 
		print(err); 
	outb(Cmd, 0xA8);			/* auxilliary device enable */ 
	if(outready() < 0){ 
		iunlock(&i8042lock); 
		return; 
	} 
	auxputc = putc; 
2000/0401    
	intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux"); 
1999/0415    
	iunlock(&i8042lock); 
} 
 
2000/0401    
static void 
setscan(int code) 
{ 
	char *err = "setscan: set scan code failed\n"; 
 
	outb(Data, 0xF0); 
	if(inready() < 0 || inb(Data) != 0xFA || outready() < 0) { 
		print(err); 
		return; 
	} 
	outb(Data, code); 
	if(inready() < 0) { 
		print(err); 
		return; 
	} 
	inb(Data); 
	if(outready() < 0) 
		print(err); 
} 
 
1999/0415    
void 
kbdinit(void) 
{ 
1999/0514    
	int c; 
1999/0415    
 
	kbdq = qopen(4*1024, 0, 0, 0); 
	if(kbdq == nil) 
		panic("kbdinit"); 
	qnoblock(kbdq, 1); 
 
2000/0401    
	ioalloc(Data, 1, 0, "kbd"); 
	ioalloc(Cmd, 1, 0, "kbd"); 
1999/0415    
 
2000/0401    
	intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd"); 
 
1999/0415    
	/* wait for a quiescent controller */ 
	while((c = inb(Status)) & (Outbusy | Inready)) 
1999/0514    
		if(c & Inready) 
			inb(Data); 
1999/0415    
 
	/* get current controller command byte */ 
	outb(Cmd, 0x20); 
	if(inready() < 0){ 
		print("kbdinit: can't read ccc\n"); 
		ccc = 0; 
	} else 
		ccc = inb(Data); 
 
	/* enable kbd xfers and interrupts */ 
	/* disable mouse */ 
	ccc &= ~Ckbddis; 
	ccc |= Csf | Ckbdint | Cscs1; 
	if(outready() < 0) 
		print("kbd init failed\n"); 
	outb(Cmd, 0x60); 
	if(outready() < 0) 
		print("kbd init failed\n"); 
	outb(Data, ccc); 
	outready(); 
2000/0401    
	setscan(0x02); 
1999/0415    
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)