329 lines
5.6 KiB
C
329 lines
5.6 KiB
C
/*
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
* in the LICENSE file.
|
|
*/
|
|
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "../port/error.h"
|
|
|
|
#include "io.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) */
|
|
|
|
};
|
|
|
|
|
|
enum
|
|
{
|
|
/* controller command byte */
|
|
Cscs1= (1<<6), /* scan code set 1 */
|
|
Cauxdis= (1<<5), /* mouse disable */
|
|
Ckeybdis= (1<<4), /* keyb disable */
|
|
Csf= (1<<2), /* system flag */
|
|
Cauxint= (1<<1), /* mouse interrupt enable */
|
|
Ckeybint= (1<<0), /* keyb interrupt enable */
|
|
};
|
|
|
|
static Queue *keybq;
|
|
static Queue *mouseq;
|
|
|
|
|
|
static int nokeyb = 1;
|
|
|
|
static Lock i8042lock;
|
|
static uint8_t ccc;
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
void
|
|
i8042systemreset(void)
|
|
{
|
|
uint16_t *s = KADDR(0x472);
|
|
int i, x;
|
|
|
|
if(nokeyb)
|
|
return;
|
|
|
|
*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
|
|
mousecmd(int cmd)
|
|
{
|
|
unsigned int c;
|
|
int tries;
|
|
static int badkbd;
|
|
|
|
if(badkbd)
|
|
return -1;
|
|
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){
|
|
jehanne_print("mousecmd: %2.2ux returned to the %2.2ux command\n", c, cmd);
|
|
badkbd = 1; /* don't keep trying; there might not be one */
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
mousecmds(uint8_t *cmd, int ncmd)
|
|
{
|
|
int i;
|
|
|
|
ilock(&i8042lock);
|
|
for(i=0; i<ncmd; i++){
|
|
if(outready() == -1)
|
|
break;
|
|
outb(Cmd, 0xD4);
|
|
if(outready() == -1)
|
|
break;
|
|
outb(Data, cmd[i]);
|
|
}
|
|
iunlock(&i8042lock);
|
|
return i;
|
|
}
|
|
|
|
static void
|
|
i8042intr(Ureg* u, void* v)
|
|
{
|
|
uint8_t stat, data;
|
|
|
|
ilock(&i8042lock);
|
|
stat = inb(Status);
|
|
if((stat&Inready) == 0){
|
|
iunlock(&i8042lock);
|
|
return;
|
|
}
|
|
data = inb(Data);
|
|
iunlock(&i8042lock);
|
|
|
|
if(stat & Minready){
|
|
if(mouseq != nil)
|
|
qiwrite(mouseq, &data, 1);
|
|
} else {
|
|
if(keybq != nil)
|
|
qiwrite(keybq, &data, 1);
|
|
}
|
|
}
|
|
|
|
static int
|
|
outbyte(int port, int c)
|
|
{
|
|
if(outready() == -1) {
|
|
return -1;
|
|
}
|
|
outb(port, c);
|
|
if(outready() == -1) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static long
|
|
mouserwrite(Chan* c, void *vbuf, long len, int64_t off64)
|
|
{
|
|
return mousecmds(vbuf, len);
|
|
}
|
|
|
|
static long
|
|
mouseread(Chan* c, void *vbuf, long len, int64_t off64)
|
|
{
|
|
return qread(mouseq, vbuf, len);
|
|
}
|
|
|
|
void
|
|
mouseenable(void)
|
|
{
|
|
mouseq = qopen(32, 0, 0, 0);
|
|
if(mouseq == nil)
|
|
panic("mouseenable");
|
|
qnoblock(mouseq, 1);
|
|
|
|
ccc &= ~Cauxdis;
|
|
ccc |= Cauxint;
|
|
|
|
ilock(&i8042lock);
|
|
if(outready() == -1)
|
|
iprint("mouseenable: failed 0\n");
|
|
outb(Cmd, 0x60); /* write control register */
|
|
if(outready() == -1)
|
|
iprint("mouseenable: failed 1\n");
|
|
outb(Data, ccc);
|
|
if(outready() == -1)
|
|
iprint("mouseenable: failed 2\n");
|
|
outb(Cmd, 0xA8); /* auxilliary device enable */
|
|
if(outready() == -1){
|
|
iprint("mouseenable: failed 3\n");
|
|
iunlock(&i8042lock);
|
|
return;
|
|
}
|
|
iunlock(&i8042lock);
|
|
|
|
intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "mouse");
|
|
addarchfile("ps2mouse", 0666, mouseread, mouserwrite);
|
|
}
|
|
|
|
void
|
|
keybinit(void)
|
|
{
|
|
int c, try;
|
|
|
|
/* wait for a quiescent controller */
|
|
ilock(&i8042lock);
|
|
|
|
try = 1000;
|
|
while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
|
|
if(c & Inready)
|
|
inb(Data);
|
|
delay(1);
|
|
}
|
|
if (try <= 0) {
|
|
iunlock(&i8042lock);
|
|
jehanne_print("keybinit failed 0\n");
|
|
return;
|
|
}
|
|
|
|
/* get current controller command byte */
|
|
outb(Cmd, 0x20);
|
|
if(inready() == -1){
|
|
iunlock(&i8042lock);
|
|
jehanne_print("keybinit failed 1\n");
|
|
ccc = 0;
|
|
} else
|
|
ccc = inb(Data);
|
|
|
|
/* enable keyb xfers and interrupts */
|
|
ccc &= ~(Ckeybdis);
|
|
ccc |= Csf | Ckeybint | Cscs1;
|
|
if(outready() == -1) {
|
|
iunlock(&i8042lock);
|
|
jehanne_print("keybinit failed 2\n");
|
|
return;
|
|
}
|
|
|
|
if (outbyte(Cmd, 0x60) == -1){
|
|
iunlock(&i8042lock);
|
|
jehanne_print("keybinit failed 3\n");
|
|
return;
|
|
}
|
|
if (outbyte(Data, ccc) == -1){
|
|
iunlock(&i8042lock);
|
|
jehanne_print("keybinit failed 4\n");
|
|
return;
|
|
}
|
|
|
|
nokeyb = 0;
|
|
|
|
iunlock(&i8042lock);
|
|
}
|
|
|
|
static long
|
|
keybread(Chan* c, void *vbuf, long len, int64_t off64)
|
|
{
|
|
return qread(keybq, vbuf, len);
|
|
}
|
|
|
|
void
|
|
keybenable(void)
|
|
{
|
|
keybq = qopen(32, 0, 0, 0);
|
|
if(keybq == nil)
|
|
panic("keybinit");
|
|
qnoblock(keybq, 1);
|
|
|
|
ioalloc(Data, 1, 0, "keyb");
|
|
ioalloc(Cmd, 1, 0, "keyb");
|
|
|
|
intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "keyb");
|
|
|
|
addarchfile("ps2keyb", 0666, keybread, nil);
|
|
|
|
}
|