LOCAL void i8042MseHwInit( I8042_MSE_DEVICE *pDev ) { u_int8_t cfg; u_int8_t stat; i8042Command(pDev->cmdReg, I8042_KBD_DISABLE); /* Get configuration */ i8042Command(pDev->cmdReg, I8042_KBD_RD_CONFIG); i8042Read(pDev->statReg, pDev->dataReg, &cfg); /* Suspend keyboard interrupts */ i8042Command(pDev->cmdReg, I8042_KBD_WT_CONFIG); i8042Write(pDev->cmdReg, pDev->dataReg, cfg & 0xfc); /* Enable auxilary */ i8042Command(pDev->cmdReg, I8042_KBD_ENABLE_AUX); /* Check device interface */ i8042Command(pDev->cmdReg, I8042_KBD_IF_AUX_TEST); if (i8042Read(pDev->statReg, pDev->dataReg, &stat) == OK) { if (stat == I8042_KBD_IF_OK) { /* Setup mouse mode */ i8042Command(pDev->cmdReg, I8042_KBD_WT_AUX); i8042Write(pDev->cmdReg, pDev->dataReg, I8042_KBDM_SETS_CMD); if (i8042Read(pDev->statReg, pDev->dataReg, &stat) == OK) { if (stat == I8042_KBDM_ACK) { /* Enable mouse */ i8042Command(pDev->cmdReg, I8042_KBD_WT_AUX); i8042Write(pDev->cmdReg, pDev->dataReg, I8042_KBDM_ENABLE_CMD); if (i8042Read(pDev->statReg, pDev->dataReg, &stat) == OK) { if (stat == I8042_KBDM_ACK) { cfg |= I8042_KBD_AUX_INT; cfg &= ~I8042_KBDM_DISABLE_CLK; /* Enable keyboard with mouse support */ i8042Command(pDev->cmdReg, I8042_KBD_WT_CONFIG); i8042Write(pDev->cmdReg, pDev->dataReg, cfg); } } } } } } i8042Command(pDev->cmdReg, I8042_KBD_ENABLE); }
BOOLEAN i8042IsrWritePort( IN PPORT_DEVICE_EXTENSION DeviceExtension, IN UCHAR Value, IN UCHAR SelectCmd OPTIONAL) { if (SelectCmd) if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, SelectCmd)) return FALSE; return i8042Write(DeviceExtension, DeviceExtension->DataPort, Value); }
/* * These functions are callbacks for filter driver custom * initialization routines. */ NTSTATUS NTAPI i8042SynchWritePort( IN PPORT_DEVICE_EXTENSION DeviceExtension, IN UCHAR Port, IN UCHAR Value, IN BOOLEAN WaitForAck) { NTSTATUS Status; UCHAR Ack; ULONG ResendIterations; ResendIterations = DeviceExtension->Settings.ResendIterations + 1; do { if (Port) if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Port)) { WARN_(I8042PRT, "Failed to write Port\n"); return STATUS_IO_TIMEOUT; } if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Value)) { WARN_(I8042PRT, "Failed to write Value\n"); return STATUS_IO_TIMEOUT; } if (WaitForAck) { Status = i8042ReadDataWait(DeviceExtension, &Ack); if (!NT_SUCCESS(Status)) { WARN_(I8042PRT, "Failed to read Ack\n"); return Status; } if (Ack == KBD_ACK) return STATUS_SUCCESS; else if (Ack == KBD_RESEND) INFO_(I8042PRT, "i8042 asks for a data resend\n"); } else { return STATUS_SUCCESS; } TRACE_(I8042PRT, "Reiterating\n"); ResendIterations--; } while (ResendIterations); return STATUS_IO_TIMEOUT; }
/* Write the current byte of the packet. Returns FALSE in case * of problems. */ static BOOLEAN i8042PacketWrite( IN PPORT_DEVICE_EXTENSION DeviceExtension) { UCHAR Port = DeviceExtension->PacketPort; if (Port) { if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, Port)) { /* something is really wrong! */ WARN_(I8042PRT, "Failed to send packet byte!\n"); return FALSE; } } return i8042Write(DeviceExtension, DeviceExtension->DataPort, DeviceExtension->Packet.Bytes[DeviceExtension->Packet.CurrentByte]); }
/* This is all pretty confusing. There's more than one way to * disable/enable the keyboard. You can send KBD_ENABLE to the * keyboard, and it will start scanning keys. Sending KBD_DISABLE * will disable the key scanning but also reset the parameters to * defaults. * * You can also send 0xAE to the controller for enabling the * keyboard clock line and 0xAD for disabling it. Then it'll * automatically get turned on at the next command. The last * way is by modifying the bit that drives the clock line in the * 'command byte' of the controller. This is almost, but not quite, * the same as the AE/AD thing. The difference can be used to detect * some really old broken keyboard controllers which I hope won't be * necessary. * * We change the command byte, sending KBD_ENABLE/DISABLE seems to confuse * some kvm switches. */ BOOLEAN i8042ChangeMode( IN PPORT_DEVICE_EXTENSION DeviceExtension, IN UCHAR FlagsToDisable, IN UCHAR FlagsToEnable) { UCHAR Value; NTSTATUS Status; if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, KBD_READ_MODE)) { WARN_(I8042PRT, "Can't read i8042 mode\n"); return FALSE; } Status = i8042ReadDataWait(DeviceExtension, &Value); if (!NT_SUCCESS(Status)) { WARN_(I8042PRT, "No response after read i8042 mode\n"); return FALSE; } Value &= ~FlagsToDisable; Value |= FlagsToEnable; if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, KBD_WRITE_MODE)) { WARN_(I8042PRT, "Can't set i8042 mode\n"); return FALSE; } if (!i8042Write(DeviceExtension, DeviceExtension->DataPort, Value)) { WARN_(I8042PRT, "Can't send i8042 mode\n"); return FALSE; } return TRUE; }
static NTSTATUS i8042BasicDetect( IN PPORT_DEVICE_EXTENSION DeviceExtension) { NTSTATUS Status; ULONG ResendIterations; UCHAR Value = 0; /* Don't enable keyboard and mouse interrupts, disable keyboard/mouse */ i8042Flush(DeviceExtension); if (!i8042ChangeMode(DeviceExtension, CCB_KBD_INT_ENAB | CCB_MOUSE_INT_ENAB, CCB_KBD_DISAB | CCB_MOUSE_DISAB)) return STATUS_IO_DEVICE_ERROR; i8042Flush(DeviceExtension); /* Issue a CTRL_SELF_TEST command to check if this is really an i8042 controller */ ResendIterations = DeviceExtension->Settings.ResendIterations + 1; while (ResendIterations--) { if (!i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_SELF_TEST)) { WARN_(I8042PRT, "Writing CTRL_SELF_TEST command failed\n"); return STATUS_IO_TIMEOUT; } Status = i8042ReadDataWait(DeviceExtension, &Value); if (!NT_SUCCESS(Status)) { WARN_(I8042PRT, "Failed to read CTRL_SELF_TEST response, status 0x%08lx\n", Status); return Status; } if (Value == KBD_SELF_TEST_OK) { INFO_(I8042PRT, "CTRL_SELF_TEST completed successfully!\n"); break; } else if (Value == KBD_RESEND) { TRACE_(I8042PRT, "Resending...\n", Value); KeStallExecutionProcessor(50); } else { WARN_(I8042PRT, "Got 0x%02x instead of 0x55\n", Value); return STATUS_IO_DEVICE_ERROR; } } return STATUS_SUCCESS; }
static VOID i8042DetectMouse( IN PPORT_DEVICE_EXTENSION DeviceExtension) { NTSTATUS Status; UCHAR Value; UCHAR ExpectedReply[] = { MOUSE_ACK, 0xAA }; UCHAR ReplyByte; /* First do a mouse line test */ if (i8042Write(DeviceExtension, DeviceExtension->ControlPort, MOUSE_LINE_TEST)) { Status = i8042ReadDataWait(DeviceExtension, &Value); if (!NT_SUCCESS(Status) || Value != 0) { WARN_(I8042PRT, "Mouse line test failed\n"); goto failure; } } /* Now reset the mouse */ i8042Flush(DeviceExtension); if(!i8042IsrWritePort(DeviceExtension, MOU_CMD_RESET, CTRL_WRITE_MOUSE)) { WARN_(I8042PRT, "Failed to write reset command to mouse\n"); goto failure; } /* The implementation of the "Mouse Reset" command differs much from chip to chip. By default, the first byte is an ACK, when the mouse is plugged in and working and NACK when it's not. On success, the next bytes are 0xAA and 0x00. But on some systems (like ECS K7S5A Pro, SiS 735 chipset), we always get an ACK and 0xAA. Only the last byte indicates, whether a mouse is plugged in. It is either sent or not, so there is no byte, which indicates a failure here. After the Mouse Reset command was issued, it usually takes some time until we get a response. So get the first two bytes in a loop. */ for (ReplyByte = 0; ReplyByte < sizeof(ExpectedReply) / sizeof(ExpectedReply[0]); ReplyByte++) { ULONG Counter = 500; do { Status = i8042ReadDataWait(DeviceExtension, &Value); if(!NT_SUCCESS(Status)) { /* Wait some time before trying again */ KeStallExecutionProcessor(50); } } while (Status == STATUS_IO_TIMEOUT && Counter--); if (!NT_SUCCESS(Status)) { WARN_(I8042PRT, "No ACK after mouse reset, status 0x%08lx\n", Status); goto failure; } else if (Value != ExpectedReply[ReplyByte]) { WARN_(I8042PRT, "Unexpected reply: 0x%02x (expected 0x%02x)\n", Value, ExpectedReply[ReplyByte]); goto failure; } } /* Finally get the third byte, but only try it one time (see above). Otherwise this takes around 45 seconds on a K7S5A Pro, when no mouse is plugged in. */ Status = i8042ReadDataWait(DeviceExtension, &Value); if(!NT_SUCCESS(Status)) { WARN_(I8042PRT, "Last byte was not transmitted after mouse reset, status 0x%08lx\n", Status); goto failure; } else if(Value != 0x00) { WARN_(I8042PRT, "Last byte after mouse reset was not 0x00, but 0x%02x\n", Value); goto failure; } DeviceExtension->Flags |= MOUSE_PRESENT; INFO_(I8042PRT, "Mouse detected\n"); return; failure: /* There is probably no mouse present. On some systems, the probe locks the entire keyboard controller. Let's try to get access to the keyboard again by sending a reset */ i8042Flush(DeviceExtension); i8042Write(DeviceExtension, DeviceExtension->ControlPort, CTRL_SELF_TEST); i8042ReadDataWait(DeviceExtension, &Value); i8042Flush(DeviceExtension); INFO_(I8042PRT, "Mouse not detected\n"); }