Continued keyboard controller work - the controller part of the poll is now a state machine and the PS/2 MCA IRQ latch (cleared on port 0x60 read) is back - fixes #3238.
This commit is contained in:
@@ -92,12 +92,22 @@
|
|||||||
#define KBC_VEN_PHOENIX 0x3c
|
#define KBC_VEN_PHOENIX 0x3c
|
||||||
#define KBC_VEN_MASK 0x3c
|
#define KBC_VEN_MASK 0x3c
|
||||||
|
|
||||||
|
enum {
|
||||||
|
KBC_STATE_RESET = 0,
|
||||||
|
KBC_STATE_NORMAL,
|
||||||
|
KBC_STATE_KBD,
|
||||||
|
KBC_STATE_MOUSE
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t command, status, ib, out, old_out, secr_phase,
|
uint8_t command, status, ib, out,
|
||||||
mem_addr, input_port, output_port, old_output_port,
|
old_out, secr_phase, mem_addr, input_port,
|
||||||
key_command, output_locked, ami_stat, want60,
|
output_port, old_output_port, key_command, output_locked,
|
||||||
wantirq, key_wantdata, ami_flags, first_write,
|
ami_stat, want60, key_wantdata, ami_flags,
|
||||||
key_wantcmd, key_dat, mouse_wantcmd, mouse_dat;
|
key_wantcmd, key_dat, mouse_wantcmd, mouse_dat,
|
||||||
|
kbc_state, kbd_state, mouse_state, pad;
|
||||||
|
|
||||||
|
uint16_t irq_levels, pad0;
|
||||||
|
|
||||||
uint8_t mem[0x100];
|
uint8_t mem[0x100];
|
||||||
|
|
||||||
@@ -683,6 +693,16 @@ kbc_queue_add(atkbd_t *dev, uint8_t val, uint8_t channel)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
kbc_irq(atkbd_t *dev, uint16_t irq, int raise)
|
||||||
|
{
|
||||||
|
picint_common(irq, (dev->flags & KBC_TYPE_MASK) > KBC_TYPE_PS2_NOREF, raise);
|
||||||
|
if (raise)
|
||||||
|
dev->irq_levels |= irq;
|
||||||
|
else
|
||||||
|
dev->irq_levels &= ~irq;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_hi)
|
add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_hi)
|
||||||
{
|
{
|
||||||
@@ -694,8 +714,6 @@ add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_
|
|||||||
stat_hi |= 0x10;
|
stat_hi |= 0x10;
|
||||||
|
|
||||||
kbd_log("ATkbc: Adding %02X to front...\n", val);
|
kbd_log("ATkbc: Adding %02X to front...\n", val);
|
||||||
dev->wantirq = 0;
|
|
||||||
|
|
||||||
dev->status = (dev->status & ~0xf0) | STAT_OFULL | stat_hi;
|
dev->status = (dev->status & ~0xf0) | STAT_OFULL | stat_hi;
|
||||||
|
|
||||||
/* WARNING: On PS/2, all IRQ's are level-triggered, but the IBM PS/2 KBC firmware is explicitly
|
/* WARNING: On PS/2, all IRQ's are level-triggered, but the IBM PS/2 KBC firmware is explicitly
|
||||||
@@ -705,12 +723,12 @@ add_to_kbc_queue_front(atkbd_t *dev, uint8_t val, uint8_t channel, uint8_t stat_
|
|||||||
dev->status |= STAT_MFULL;
|
dev->status |= STAT_MFULL;
|
||||||
|
|
||||||
if (dev->mem[0x20] & 0x02)
|
if (dev->mem[0x20] & 0x02)
|
||||||
picint(1 << 12);
|
kbc_irq(dev, 1 << 12, 1);
|
||||||
picintc(1 << 1);
|
kbc_irq(dev, 1 << 1, 0);
|
||||||
} else {
|
} else {
|
||||||
if (dev->mem[0x20] & 0x01)
|
if (dev->mem[0x20] & 0x01)
|
||||||
picint(1 << 1);
|
kbc_irq(dev, 1 << 1, 1);
|
||||||
picintc(1 << 12);
|
kbc_irq(dev, 1 << 12, 0);
|
||||||
}
|
}
|
||||||
} else if (dev->mem[0x20] & 0x01)
|
} else if (dev->mem[0x20] & 0x01)
|
||||||
picintlevel(1 << 1); /* AT KBC: IRQ 1 is level-triggered because it is tied to OBF. */
|
picintlevel(1 << 1); /* AT KBC: IRQ 1 is level-triggered because it is tied to OBF. */
|
||||||
@@ -786,73 +804,73 @@ set_enable_mouse(atkbd_t *dev, uint8_t enable)
|
|||||||
dev->mem[0x20] |= (enable ? 0x00 : 0x20);
|
dev->mem[0x20] |= (enable ? 0x00 : 0x20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: State machines for controller, keyboard, and mouse. */
|
||||||
static void
|
static void
|
||||||
kbd_poll(void *priv)
|
kbd_poll(void *priv)
|
||||||
{
|
{
|
||||||
atkbd_t *dev = (atkbd_t *) priv;
|
atkbd_t *dev = (atkbd_t *) priv;
|
||||||
const uint8_t channels[4] = { 1, 2, 0, 0 };
|
|
||||||
int mouse_enabled;
|
int mouse_enabled;
|
||||||
|
|
||||||
timer_advance_u64(&dev->send_delay_timer, (100ULL * TIMER_USEC));
|
timer_advance_u64(&dev->send_delay_timer, (100ULL * TIMER_USEC));
|
||||||
|
|
||||||
if (dev->status & STAT_IFULL) {
|
mouse_enabled = !(dev->mem[0x20] & 0x20) && ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF);
|
||||||
dev->status &= ~STAT_IFULL;
|
|
||||||
if ((dev->status & STAT_CD) || dev->want60)
|
|
||||||
kbc_process_cmd(dev);
|
|
||||||
else if (!(dev->status & STAT_CD) && !dev->want60) {
|
|
||||||
dev->status &= ~STAT_IFULL;
|
|
||||||
set_enable_kbd(dev, 1);
|
|
||||||
kbc_queue_reset(4);
|
|
||||||
dev->key_wantcmd = 1;
|
|
||||||
dev->key_dat = dev->ib;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* TODO: Move keyboard controller queue processing here (it's just our way to do commands
|
|
||||||
with multi-byte output) and split out_new into two separate variables, one for
|
|
||||||
the keyboard and one for the mouse.
|
|
||||||
We also need a way to make sure that in case of us sending data to the keyboard
|
|
||||||
or the mouse, we do ignore output from the other channel until we have received
|
|
||||||
the one byte from the channel we are talking to. */
|
|
||||||
if ((dev->out_new != -1) && !(dev->status & STAT_OFULL)) {
|
|
||||||
dev->wantirq = 0;
|
|
||||||
if (dev->out_new & 0x100) {
|
|
||||||
kbd_log("ATkbc: %02X coming from channel 2\n", dev->out_new & 0xff);
|
|
||||||
add_to_kbc_queue_front(dev, dev->out_new & 0xff, 2, 0x00);
|
|
||||||
} else {
|
|
||||||
kbd_log("ATkbc: %02X coming from channel %i\n", dev->out_new & 0xff,
|
|
||||||
channels[(dev->out_new >> 8) & 0x03]);
|
|
||||||
add_to_kbc_queue_front(dev, dev->out_new & 0xff, channels[(dev->out_new >> 8) & 0x03], 0x00);
|
|
||||||
}
|
|
||||||
dev->out_new = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
mouse_enabled = !(dev->mem[0x20] & 0x20) && ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF);
|
switch (dev->kbc_state) {
|
||||||
|
/* Reset state. */
|
||||||
/* It would be better to separate the operation in two: keyboard/mouse processing fetches data from the FIFO
|
case KBC_STATE_RESET:
|
||||||
and passes it to the controller, the controller then passes it on to the guest. */
|
if ((dev->status & STAT_IFULL) && (dev->status & STAT_CD) && (dev->ib == 0xaa))
|
||||||
if (!(dev->status & STAT_OFULL) && (dev->out_new == -1)) {
|
kbc_process_cmd(dev);
|
||||||
if (key_ctrl_queue_start != key_ctrl_queue_end) {
|
break;
|
||||||
kbd_log("ATkbc: %02X on channel 0\n", key_ctrl_queue[key_ctrl_queue_start]);
|
/* Process commands and/or monitor the attached devices. */
|
||||||
dev->out_new = key_ctrl_queue[key_ctrl_queue_start] | 0x200;
|
case KBC_STATE_NORMAL:
|
||||||
key_ctrl_queue_start = (key_ctrl_queue_start + 1) & 0x3f;
|
if (!(dev->status & STAT_OFULL)) {
|
||||||
} else if (!(dev->mem[0x20] & 0x10) && (key_cmd_queue_start != key_cmd_queue_end)) {
|
if (dev->status & STAT_IFULL) {
|
||||||
kbd_log("ATkbc: %02X on channel 1\n", key_cmd_queue[key_cmd_queue_start]);
|
dev->status &= ~STAT_IFULL;
|
||||||
dev->out_new = key_cmd_queue[key_cmd_queue_start];
|
if ((dev->status & STAT_CD) || dev->want60)
|
||||||
key_cmd_queue_start = (key_cmd_queue_start + 1) & 0xf;
|
kbc_process_cmd(dev);
|
||||||
} else if (!(dev->mem[0x20] & 0x10) && key_queue_start != key_queue_end) {
|
else if (!(dev->status & STAT_CD) && !dev->want60) {
|
||||||
kbd_log("ATkbc: %02X on channel 1\n", key_queue[key_queue_start]);
|
dev->status &= ~STAT_IFULL;
|
||||||
dev->out_new = key_queue[key_queue_start];
|
set_enable_kbd(dev, 1);
|
||||||
key_queue_start = (key_queue_start + 1) & 0xf;
|
kbc_queue_reset(4);
|
||||||
} else if (mouse_enabled && (mouse_cmd_queue_start != mouse_cmd_queue_end)) {
|
dev->key_wantcmd = 1;
|
||||||
kbd_log("ATkbc: %02X on channel 2\n", mouse_cmd_queue[mouse_cmd_queue_start]);
|
dev->key_dat = dev->ib;
|
||||||
dev->out_new = mouse_cmd_queue[mouse_cmd_queue_start] | 0x100;
|
dev->kbc_state = KBC_STATE_KBD;
|
||||||
mouse_cmd_queue_start = (mouse_cmd_queue_start + 1) & 0xf;
|
}
|
||||||
} else if (mouse_enabled && (mouse_queue_start != mouse_queue_end)) {
|
} else {
|
||||||
kbd_log("ATkbc: %02X on channel 2\n", mouse_queue[mouse_queue_start]);
|
if (key_ctrl_queue_start != key_ctrl_queue_end) {
|
||||||
dev->out_new = mouse_queue[mouse_queue_start] | 0x100;
|
kbd_log("ATkbc: %02X coming from channel 0\n", dev->out_new & 0xff);
|
||||||
mouse_queue_start = (mouse_queue_start + 1) & 0xf;
|
add_to_kbc_queue_front(dev, key_ctrl_queue[key_ctrl_queue_start], 0, 0x00);
|
||||||
|
key_ctrl_queue_start = (key_ctrl_queue_start + 1) & 0x3f;
|
||||||
|
} else if (mouse_enabled && (dev->out_new_mouse != -1)) {
|
||||||
|
kbd_log("ATkbc: %02X coming from channel 2\n", dev->out_new_mouse);
|
||||||
|
add_to_kbc_queue_front(dev, dev->out_new_mouse, 2, 0x00);
|
||||||
|
dev->out_new_mouse = -1;
|
||||||
|
} else if (!(dev->mem[0x20] & 0x10) && (dev->out_new != -1)) {
|
||||||
|
kbd_log("ATkbc: %02X coming from channel 1\n", dev->out_new);
|
||||||
|
add_to_kbc_queue_front(dev, dev->out_new, 1, 0x00);
|
||||||
|
dev->out_new = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
|
/* Wait for keyboard command response. */
|
||||||
|
case KBC_STATE_KBD:
|
||||||
|
if (!(dev->mem[0x20] & 0x10) && (dev->out_new != -1)) {
|
||||||
|
kbd_log("ATkbc: %02X coming from channel 1\n", dev->out_new);
|
||||||
|
add_to_kbc_queue_front(dev, dev->out_new, 1, 0x00);
|
||||||
|
dev->out_new = -1;
|
||||||
|
dev->kbc_state = KBC_STATE_NORMAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* Wait for keyboard mouse response. */
|
||||||
|
case KBC_STATE_MOUSE:
|
||||||
|
if (mouse_enabled && (dev->out_new_mouse != -1)) {
|
||||||
|
kbd_log("ATkbc: %02X coming from channel 2\n", dev->out_new_mouse);
|
||||||
|
add_to_kbc_queue_front(dev, dev->out_new_mouse, 2, 0x00);
|
||||||
|
dev->out_new_mouse = -1;
|
||||||
|
dev->kbc_state = KBC_STATE_NORMAL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->reset_delay) {
|
if (dev->reset_delay) {
|
||||||
@@ -863,8 +881,21 @@ kbd_poll(void *priv)
|
|||||||
}
|
}
|
||||||
} else if (dev->key_wantcmd) {
|
} else if (dev->key_wantcmd) {
|
||||||
if ((key_cmd_queue_start == key_cmd_queue_end) && (dev->out_new == -1) && (dev->reset_delay == 0)) {
|
if ((key_cmd_queue_start == key_cmd_queue_end) && (dev->out_new == -1) && (dev->reset_delay == 0)) {
|
||||||
|
kbd_log("ATkbc: Processing keyboard command...\n");
|
||||||
kbd_process_cmd(dev);
|
kbd_process_cmd(dev);
|
||||||
dev->key_wantcmd = 0;
|
dev->key_wantcmd = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (dev->out_new == -1) {
|
||||||
|
if (key_cmd_queue_start != key_cmd_queue_end) {
|
||||||
|
kbd_log("ATkbc: %02X (CMD ) on channel 1\n", key_cmd_queue[key_cmd_queue_start]);
|
||||||
|
dev->out_new = key_cmd_queue[key_cmd_queue_start];
|
||||||
|
key_cmd_queue_start = (key_cmd_queue_start + 1) & 0xf;
|
||||||
|
} else if (key_queue_start != key_queue_end) {
|
||||||
|
kbd_log("ATkbc: %02X (DATA) on channel 1\n", key_queue[key_queue_start]);
|
||||||
|
dev->out_new = key_queue[key_queue_start];
|
||||||
|
key_queue_start = (key_queue_start + 1) & 0xf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,15 +903,26 @@ kbd_poll(void *priv)
|
|||||||
dev->mouse_reset_delay--;
|
dev->mouse_reset_delay--;
|
||||||
if (!dev->mouse_reset_delay) {
|
if (!dev->mouse_reset_delay) {
|
||||||
kbd_log("ATkbc: Sending AA 00 on mouse reset...\n");
|
kbd_log("ATkbc: Sending AA 00 on mouse reset...\n");
|
||||||
keyboard_at_adddata_mouse_cmd(0xaa);
|
// keyboard_at_adddata_mouse_cmd(0xaa);
|
||||||
keyboard_at_adddata_mouse_cmd(0x00);
|
// keyboard_at_adddata_mouse_cmd(0x00);
|
||||||
}
|
}
|
||||||
} else if (dev->mouse_wantcmd) {
|
} else if (dev->mouse_wantcmd) {
|
||||||
if ((mouse_cmd_queue_start == mouse_cmd_queue_end) && (dev->out_new == -1) && (dev->mouse_reset_delay == 0)) {
|
if ((mouse_cmd_queue_start == mouse_cmd_queue_end) && (dev->out_new == -1) && (dev->mouse_reset_delay == 0)) {
|
||||||
mouse_write(dev->mouse_dat, mouse_p);
|
mouse_write(dev->mouse_dat, mouse_p);
|
||||||
// if (dev->mouse_dat == 0xff)
|
// if (dev->mouse_dat == 0xff)
|
||||||
// dev->mouse_reset_delay = RESET_DELAY_TIME;
|
// dev->mouse_reset_delay = RESET_DELAY_TIME;
|
||||||
dev->mouse_wantcmd = 0;
|
dev->mouse_wantcmd = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dev->out_new_mouse == -1) {
|
||||||
|
if (mouse_cmd_queue_start != mouse_cmd_queue_end) {
|
||||||
|
kbd_log("ATkbc: %02X (CMD ) on channel 2\n", mouse_cmd_queue[mouse_cmd_queue_start]);
|
||||||
|
dev->out_new_mouse = mouse_cmd_queue[mouse_cmd_queue_start];
|
||||||
|
mouse_cmd_queue_start = (mouse_cmd_queue_start + 1) & 0xf;
|
||||||
|
} else if (mouse_queue_start != mouse_queue_end) {
|
||||||
|
kbd_log("ATkbc: %02X (DATA) on channel 2\n", mouse_queue[mouse_queue_start]);
|
||||||
|
dev->out_new_mouse = mouse_queue[mouse_queue_start];
|
||||||
|
mouse_queue_start = (mouse_queue_start + 1) & 0xf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1173,31 +1215,24 @@ write_output(atkbd_t *dev, uint8_t val)
|
|||||||
kbd_log("ATkbc: write output port: %02X (old: %02X)\n", val, dev->output_port);
|
kbd_log("ATkbc: write output port: %02X (old: %02X)\n", val, dev->output_port);
|
||||||
|
|
||||||
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
|
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
|
||||||
if ((kbc_ven != KBC_VEN_OLIVETTI) && ((kbc_ven == KBC_VEN_AMI) || ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF)))
|
|
||||||
val |= ((dev->mem[0x20] << 4) & 0x10);
|
|
||||||
|
|
||||||
/*IRQ 12*/
|
/* PS/2: Handle IRQ's. */
|
||||||
if ((old ^ val) & 0x20) {
|
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) {
|
||||||
if (val & 0x20)
|
/* IRQ 12 */
|
||||||
picint(1 << 12);
|
kbc_irq(dev, 1 << 12, val & 0x20);
|
||||||
else
|
|
||||||
picintc(1 << 12);
|
/* IRQ 1 */
|
||||||
|
kbc_irq(dev, 1 << 12, val & 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*IRQ 1*/
|
/* AT, PS/2: Handle A20. */
|
||||||
if ((old ^ val) & 0x10) {
|
if ((old ^ val) & 0x02) { /* A20 enable change */
|
||||||
if (val & 0x10)
|
|
||||||
picint(1 << 1);
|
|
||||||
else
|
|
||||||
picintc(1 << 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((old ^ val) & 0x02) { /*A20 enable change*/
|
|
||||||
mem_a20_key = val & 0x02;
|
mem_a20_key = val & 0x02;
|
||||||
mem_a20_recalc();
|
mem_a20_recalc();
|
||||||
flushmmucache();
|
flushmmucache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* AT, PS/2: Handle reset. */
|
||||||
/* 0 holds the CPU in the RESET state, 1 releases it. To simplify this,
|
/* 0 holds the CPU in the RESET state, 1 releases it. To simplify this,
|
||||||
we just do everything on release. */
|
we just do everything on release. */
|
||||||
if ((old ^ val) & 0x01) { /*Reset*/
|
if ((old ^ val) & 0x01) { /*Reset*/
|
||||||
@@ -1231,15 +1266,13 @@ write_cmd(atkbd_t *dev, uint8_t val)
|
|||||||
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
|
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
|
||||||
kbd_log("ATkbc: write command byte: %02X (old: %02X)\n", val, dev->mem[0x20]);
|
kbd_log("ATkbc: write command byte: %02X (old: %02X)\n", val, dev->mem[0x20]);
|
||||||
|
|
||||||
if ((val & 1) && (dev->status & STAT_OFULL))
|
|
||||||
dev->wantirq = 1;
|
|
||||||
if (!(val & 1) && dev->wantirq)
|
|
||||||
dev->wantirq = 0;
|
|
||||||
|
|
||||||
/* PS/2 type 2 keyboard controllers always force the XLAT bit to 0. */
|
/* PS/2 type 2 keyboard controllers always force the XLAT bit to 0. */
|
||||||
if ((dev->flags & KBC_TYPE_MASK) == KBC_TYPE_PS2_2) {
|
if ((dev->flags & KBC_TYPE_MASK) == KBC_TYPE_PS2_2) {
|
||||||
val &= ~CCB_TRANSLATE;
|
val &= ~CCB_TRANSLATE;
|
||||||
dev->mem[0x20] &= ~CCB_TRANSLATE;
|
dev->mem[0x20] &= ~CCB_TRANSLATE;
|
||||||
|
} else if ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF) {
|
||||||
|
if (val & 0x10)
|
||||||
|
dev->mem[0x2e] = 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scan code translate ON/OFF. */
|
/* Scan code translate ON/OFF. */
|
||||||
@@ -1258,8 +1291,8 @@ write_cmd(atkbd_t *dev, uint8_t val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((kbc_ven == KBC_VEN_AMI) || ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF)) {
|
if ((kbc_ven == KBC_VEN_AMI) || ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF)) {
|
||||||
/* Update the output port to mirror the KBD DIS and AUX DIS bits, if active. */
|
/* Update the output port to mirror the IBF and OBF bits, if active. */
|
||||||
write_output(dev, dev->output_port);
|
write_output(dev, (dev->output_port & 0x0f) | ((val & 0x03) << 4) | ((val & 0x20) ? 0xc0 : 0x00));
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd_log("Command byte now: %02X (%02X)\n", dev->mem[0x20], val);
|
kbd_log("Command byte now: %02X (%02X)\n", dev->mem[0x20], val);
|
||||||
@@ -1840,6 +1873,23 @@ write64_toshiba(void *priv, uint8_t val)
|
|||||||
return write64_generic(dev, val);
|
return write64_generic(dev, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
kbd_key_reset(atkbd_t *dev, int do_fa)
|
||||||
|
{
|
||||||
|
kbc_queue_reset(1);
|
||||||
|
kbd_last_scan_code = 0x00;
|
||||||
|
|
||||||
|
/* Set scan code set to 2. */
|
||||||
|
keyboard_mode = (keyboard_mode & 0xfc) | 0x02;
|
||||||
|
set_scancode_map(dev);
|
||||||
|
|
||||||
|
if (do_fa)
|
||||||
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
|
|
||||||
|
keyboard_scan = 1;
|
||||||
|
dev->reset_delay = RESET_DELAY_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
kbd_process_cmd(void *priv)
|
kbd_process_cmd(void *priv)
|
||||||
{
|
{
|
||||||
@@ -1858,19 +1908,19 @@ kbd_process_cmd(void *priv)
|
|||||||
*/
|
*/
|
||||||
if (dev->key_dat == dev->key_command) {
|
if (dev->key_dat == dev->key_command) {
|
||||||
/* Respond NAK and ignore it. */
|
/* Respond NAK and ignore it. */
|
||||||
add_data_kbd(0xfe);
|
add_data_kbd_raw(dev, 0xfe);
|
||||||
dev->key_command = 0x00;
|
dev->key_command = 0x00;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (dev->key_command) {
|
switch (dev->key_command) {
|
||||||
case 0xed: /* set/reset LEDs */
|
case 0xed: /* set/reset LEDs */
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
kbd_log("ATkbd: set LEDs [%02x]\n", dev->key_dat);
|
kbd_log("ATkbd: set LEDs [%02x]\n", dev->key_dat);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xf0: /* get/set scancode set */
|
case 0xf0: /* get/set scancode set */
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
if (dev->key_dat == 0) {
|
if (dev->key_dat == 0) {
|
||||||
kbd_log("Get scan code set: %02X\n", keyboard_mode & 3);
|
kbd_log("Get scan code set: %02X\n", keyboard_mode & 3);
|
||||||
add_data_kbd_front(dev, keyboard_mode & 3);
|
add_data_kbd_front(dev, keyboard_mode & 3);
|
||||||
@@ -1885,12 +1935,12 @@ kbd_process_cmd(void *priv)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xf3: /* set typematic rate/delay */
|
case 0xf3: /* set typematic rate/delay */
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
kbd_log("ATkbd: bad keyboard 0060 write %02X command %02X\n", dev->key_dat, dev->key_command);
|
kbd_log("ATkbd: bad keyboard 0060 write %02X command %02X\n", dev->key_dat, dev->key_command);
|
||||||
add_data_kbd_front(dev, 0xfe);
|
add_data_kbd_raw(dev, 0xfe);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1904,19 +1954,19 @@ kbd_process_cmd(void *priv)
|
|||||||
switch (dev->key_dat) {
|
switch (dev->key_dat) {
|
||||||
case 0x00 ... 0x7f:
|
case 0x00 ... 0x7f:
|
||||||
kbd_log("ATkbd: invalid command %02X\n", dev->key_dat);
|
kbd_log("ATkbd: invalid command %02X\n", dev->key_dat);
|
||||||
add_data_kbd_front(dev, 0xfe);
|
add_data_kbd_raw(dev, 0xfe);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xed: /* set/reset LEDs */
|
case 0xed: /* set/reset LEDs */
|
||||||
kbd_log("ATkbd: set/reset leds\n");
|
kbd_log("ATkbd: set/reset leds\n");
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
|
|
||||||
dev->key_wantdata = 1;
|
dev->key_wantdata = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xee: /* diagnostic echo */
|
case 0xee: /* diagnostic echo */
|
||||||
kbd_log("ATkbd: ECHO\n");
|
kbd_log("ATkbd: ECHO\n");
|
||||||
add_data_kbd_front(dev, 0xee);
|
add_data_kbd_raw(dev, 0xee);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xef: /* NOP (reserved for future use) */
|
case 0xef: /* NOP (reserved for future use) */
|
||||||
@@ -1925,7 +1975,7 @@ kbd_process_cmd(void *priv)
|
|||||||
|
|
||||||
case 0xf0: /* get/set scan code set */
|
case 0xf0: /* get/set scan code set */
|
||||||
kbd_log("ATkbd: scan code set\n");
|
kbd_log("ATkbd: scan code set\n");
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
dev->key_wantdata = 1;
|
dev->key_wantdata = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1934,20 +1984,20 @@ kbd_process_cmd(void *priv)
|
|||||||
kbd_log("ATkbd: read keyboard id\n");
|
kbd_log("ATkbd: read keyboard id\n");
|
||||||
/* TODO: After keyboard type selection is implemented, make this
|
/* TODO: After keyboard type selection is implemented, make this
|
||||||
return the correct keyboard ID for the selected type. */
|
return the correct keyboard ID for the selected type. */
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
add_data_kbd_front(dev, 0xab);
|
add_data_kbd_front(dev, 0xab);
|
||||||
add_data_kbd_front(dev, 0x83);
|
add_data_kbd_front(dev, 0x83);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xf3: /* set typematic rate/delay */
|
case 0xf3: /* set typematic rate/delay */
|
||||||
kbd_log("ATkbd: set typematic rate/delay\n");
|
kbd_log("ATkbd: set typematic rate/delay\n");
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
dev->key_wantdata = 1;
|
dev->key_wantdata = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xf4: /* enable keyboard */
|
case 0xf4: /* enable keyboard */
|
||||||
kbd_log("ATkbd: enable keyboard\n");
|
kbd_log("ATkbd: enable keyboard\n");
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
keyboard_scan = 1;
|
keyboard_scan = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -1957,7 +2007,7 @@ kbd_process_cmd(void *priv)
|
|||||||
keyboard_scan = (dev->key_dat == 0xf6);
|
keyboard_scan = (dev->key_dat == 0xf6);
|
||||||
kbd_log("dev->key_dat = %02X, keyboard_scan = %i, dev->mem[0x20] = %02X\n",
|
kbd_log("dev->key_dat = %02X, keyboard_scan = %i, dev->mem[0x20] = %02X\n",
|
||||||
dev->key_dat, keyboard_scan, dev->mem[0]);
|
dev->key_dat, keyboard_scan, dev->mem[0]);
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
|
|
||||||
keyboard_set3_all_break = 0;
|
keyboard_set3_all_break = 0;
|
||||||
keyboard_set3_all_repeat = 0;
|
keyboard_set3_all_repeat = 0;
|
||||||
@@ -1968,25 +2018,25 @@ kbd_process_cmd(void *priv)
|
|||||||
|
|
||||||
case 0xf7: /* set all keys to repeat */
|
case 0xf7: /* set all keys to repeat */
|
||||||
kbd_log("ATkbd: set all keys to repeat\n");
|
kbd_log("ATkbd: set all keys to repeat\n");
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
keyboard_set3_all_break = 1;
|
keyboard_set3_all_break = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xf8: /* set all keys to give make/break codes */
|
case 0xf8: /* set all keys to give make/break codes */
|
||||||
kbd_log("ATkbd: set all keys to give make/break codes\n");
|
kbd_log("ATkbd: set all keys to give make/break codes\n");
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
keyboard_set3_all_break = 1;
|
keyboard_set3_all_break = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xf9: /* set all keys to give make codes only */
|
case 0xf9: /* set all keys to give make codes only */
|
||||||
kbd_log("ATkbd: set all keys to give make codes only\n");
|
kbd_log("ATkbd: set all keys to give make codes only\n");
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
keyboard_set3_all_break = 0;
|
keyboard_set3_all_break = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xfa: /* set all keys to repeat and give make/break codes */
|
case 0xfa: /* set all keys to repeat and give make/break codes */
|
||||||
kbd_log("ATkbd: set all keys to repeat and give make/break codes\n");
|
kbd_log("ATkbd: set all keys to repeat and give make/break codes\n");
|
||||||
add_data_kbd_front(dev, 0xfa);
|
add_data_kbd_raw(dev, 0xfa);
|
||||||
keyboard_set3_all_repeat = 1;
|
keyboard_set3_all_repeat = 1;
|
||||||
keyboard_set3_all_break = 1;
|
keyboard_set3_all_break = 1;
|
||||||
break;
|
break;
|
||||||
@@ -1998,16 +2048,7 @@ kbd_process_cmd(void *priv)
|
|||||||
|
|
||||||
case 0xff: /* reset */
|
case 0xff: /* reset */
|
||||||
kbd_log("ATkbd: kbd reset\n");
|
kbd_log("ATkbd: kbd reset\n");
|
||||||
kbc_queue_reset(1);
|
kbd_key_reset(dev, 1);
|
||||||
kbd_last_scan_code = 0x00;
|
|
||||||
add_data_kbd_front(dev, 0xfa);
|
|
||||||
|
|
||||||
/* Set scan code set to 2. */
|
|
||||||
keyboard_mode = (keyboard_mode & 0xfc) | 0x02;
|
|
||||||
set_scancode_map(dev);
|
|
||||||
|
|
||||||
keyboard_scan = 1;
|
|
||||||
dev->reset_delay = RESET_DELAY_TIME;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -2046,23 +2087,67 @@ kbc_process_cmd(void *priv)
|
|||||||
|
|
||||||
case 0xaa: /* self-test */
|
case 0xaa: /* self-test */
|
||||||
kbd_log("ATkbc: self-test\n");
|
kbd_log("ATkbc: self-test\n");
|
||||||
|
|
||||||
|
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) {
|
||||||
|
dev->status = 0x60;
|
||||||
|
|
||||||
|
dev->mem[0x20] = 0x30;
|
||||||
|
dev->mem[0x21] = 0x01;
|
||||||
|
dev->mem[0x22] = 0x0b;
|
||||||
|
dev->mem[0x25] = 0x02;
|
||||||
|
dev->mem[0x27] = 0xf8;
|
||||||
|
dev->mem[0x28] = 0xce;
|
||||||
|
dev->mem[0x29] = 0x0b;
|
||||||
|
dev->mem[0x2a] = 0x10;
|
||||||
|
dev->mem[0x2b] = 0x20;
|
||||||
|
dev->mem[0x2c] = 0x15;
|
||||||
|
dev->mem[0x30] = 0x0b;
|
||||||
|
} else {
|
||||||
|
if (dev->kbc_state != KBC_STATE_RESET) {
|
||||||
|
kbd_log("ATkbc: self-test reinitialization\n");
|
||||||
|
dev->input_port |= 0xff;
|
||||||
|
write_output(dev, 0xcf);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->status = 0x60;
|
||||||
|
|
||||||
|
dev->mem[0x20] = 0x10;
|
||||||
|
dev->mem[0x21] = 0x01;
|
||||||
|
dev->mem[0x22] = 0x06;
|
||||||
|
dev->mem[0x25] = 0x01;
|
||||||
|
dev->mem[0x27] = 0xfb;
|
||||||
|
dev->mem[0x28] = 0xe0;
|
||||||
|
dev->mem[0x29] = 0x06;
|
||||||
|
dev->mem[0x2a] = 0x10;
|
||||||
|
dev->mem[0x2b] = 0x20;
|
||||||
|
dev->mem[0x2c] = 0x15;
|
||||||
|
}
|
||||||
|
|
||||||
|
kbc_queue_reset(0);
|
||||||
|
dev->kbc_state = KBC_STATE_NORMAL;
|
||||||
|
|
||||||
|
add_to_kbc_queue_front(dev, 0x55, 0, 0x00);
|
||||||
|
|
||||||
|
#if 0
|
||||||
write_output(dev, ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) ? 0x4b : 0xcf);
|
write_output(dev, ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) ? 0x4b : 0xcf);
|
||||||
|
|
||||||
/* Always reinitialize all queues - the real hardware pulls keyboard and mouse
|
/* Always reinitialize all queues - the real hardware pulls keyboard and mouse
|
||||||
clocks high, which stops keyboard scanning. */
|
clocks high, which stops keyboard scanning. */
|
||||||
kbd_log("ATkbc: self-test reinitialization\n");
|
kbd_log("ATkbc: self-test reinitialization\n");
|
||||||
dev->out_new = -1;
|
dev->out_new = dev->out_new_mouse = -1;
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
kbc_queue_reset(i);
|
kbc_queue_reset(i);
|
||||||
|
keyboard_scan = 0;
|
||||||
|
mouse_scan = 0;
|
||||||
kbd_last_scan_code = 0x00;
|
kbd_last_scan_code = 0x00;
|
||||||
dev->status &= ~STAT_OFULL;
|
dev->status &= ~STAT_OFULL;
|
||||||
dev->key_wantcmd = 0;
|
|
||||||
|
|
||||||
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF)
|
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF)
|
||||||
write_cmd(dev, 0x30 | STAT_SYSFLAG);
|
write_cmd(dev, 0x30 | STAT_SYSFLAG);
|
||||||
else
|
else
|
||||||
write_cmd(dev, 0x10 | STAT_SYSFLAG);
|
write_cmd(dev, 0x10 | STAT_SYSFLAG);
|
||||||
add_to_kbc_queue_front(dev, 0x55, 0, 0x00);
|
add_to_kbc_queue_front(dev, 0x55, 0, 0x00);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xab: /* interface test */
|
case 0xab: /* interface test */
|
||||||
@@ -2209,6 +2294,7 @@ kbc_process_cmd(void *priv)
|
|||||||
kbc_queue_reset(3);
|
kbc_queue_reset(3);
|
||||||
dev->mouse_wantcmd = 1;
|
dev->mouse_wantcmd = 1;
|
||||||
dev->mouse_dat = dev->ib;
|
dev->mouse_dat = dev->ib;
|
||||||
|
dev->kbc_state = KBC_STATE_MOUSE;
|
||||||
} else
|
} else
|
||||||
add_to_kbc_queue_front(dev, 0xfe, 2, 0x40);
|
add_to_kbc_queue_front(dev, 0xfe, 2, 0x40);
|
||||||
}
|
}
|
||||||
@@ -2291,6 +2377,11 @@ kbd_read(uint16_t port, void *priv)
|
|||||||
This also means that in AT mode, the IRQ is level-triggered. */
|
This also means that in AT mode, the IRQ is level-triggered. */
|
||||||
if ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF)
|
if ((dev->flags & KBC_TYPE_MASK) < KBC_TYPE_PS2_NOREF)
|
||||||
picintc(1 << 1);
|
picintc(1 << 1);
|
||||||
|
else if ((dev->flags & KBC_TYPE_MASK) > KBC_TYPE_PS2_NOREF) {
|
||||||
|
/* PS/2 MCA: Latched as level-sensitive until port 0x60 is read (and with it, OBF is cleared),
|
||||||
|
in accordance with the IBM PS/2 Model 80 Technical Reference Manual. */
|
||||||
|
kbc_irq(dev, dev->irq_levels, 0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x64:
|
case 0x64:
|
||||||
@@ -2314,12 +2405,9 @@ kbd_reset(void *priv)
|
|||||||
int i;
|
int i;
|
||||||
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
|
uint8_t kbc_ven = dev->flags & KBC_VEN_MASK;
|
||||||
|
|
||||||
dev->first_write = 1;
|
|
||||||
dev->status = STAT_UNLOCKED;
|
dev->status = STAT_UNLOCKED;
|
||||||
dev->mem[0x20] = 0x01;
|
dev->mem[0x20] = 0x01;
|
||||||
dev->mem[0x20] |= CCB_TRANSLATE;
|
dev->mem[0x20] |= CCB_TRANSLATE;
|
||||||
dev->wantirq = 0;
|
|
||||||
write_output(dev, 0xcf);
|
|
||||||
dev->secr_phase = 0;
|
dev->secr_phase = 0;
|
||||||
dev->key_wantdata = 0;
|
dev->key_wantdata = 0;
|
||||||
|
|
||||||
@@ -2338,7 +2426,7 @@ kbd_reset(void *priv)
|
|||||||
set_enable_mouse(dev, 0);
|
set_enable_mouse(dev, 0);
|
||||||
mouse_scan = 0;
|
mouse_scan = 0;
|
||||||
|
|
||||||
dev->out_new = -1;
|
dev->out_new = dev->out_new_mouse = -1;
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
kbc_queue_reset(i);
|
kbc_queue_reset(i);
|
||||||
kbd_last_scan_code = 0;
|
kbd_last_scan_code = 0;
|
||||||
@@ -2351,6 +2439,21 @@ kbd_reset(void *priv)
|
|||||||
|
|
||||||
dev->ami_flags = ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) ? 0x01 : 0x00;
|
dev->ami_flags = ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) ? 0x01 : 0x00;
|
||||||
dev->ami_stat |= 0x02;
|
dev->ami_stat |= 0x02;
|
||||||
|
|
||||||
|
if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) {
|
||||||
|
write_output(dev, 0x4b);
|
||||||
|
} else {
|
||||||
|
/* The real thing writes CF and then AND's it with BF. */
|
||||||
|
write_output(dev, 0x8f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stage 1. */
|
||||||
|
dev->status = (dev->status & 0x0f) | (dev->input_port & 0xf0);
|
||||||
|
/* Wait for command AA. */
|
||||||
|
dev->kbc_state = KBC_STATE_RESET;
|
||||||
|
|
||||||
|
/* Reset the keyboard. */
|
||||||
|
// kbd_key_reset(dev, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset the AT keyboard - this is needed for the PCI TRC and is done
|
/* Reset the AT keyboard - this is needed for the PCI TRC and is done
|
||||||
|
Reference in New Issue
Block a user