1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642 |
- /*++
- Copyright (c) 2012 Minoca Corp. All Rights Reserved
- Module Name:
- i8042.c
- Abstract:
- This module implements the Intel 8042 keyboard/mouse controller driver.
- Author:
- Evan Green 20-Dec-2012
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include "i8042.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // These macros read from and write to the control, status, and data registers.
- //
- #define WRITE_CONTROL_REGISTER(_Device, _Value) \
- HlIoPortOutByte(_Device->ControlPort, _Value)
- #define READ_STATUS_REGISTER(_Device) HlIoPortInByte(_Device->ControlPort)
- #define WRITE_DATA_REGISTER(_Device, _Value) \
- HlIoPortOutByte(_Device->DataPort, _Value)
- #define READ_DATA_REGISTER(_Device) HlIoPortInByte(_Device->DataPort)
- //
- // This macro spins waiting for the last keyboard command to finish.
- //
- #define WAIT_FOR_INPUT_BUFFER(_Device) \
- while ((READ_STATUS_REGISTER(_Device) & \
- I8042_STATUS_INPUT_BUFFER_FULL) != 0) { \
- \
- NOTHING; \
- }
- //
- // This macro determines if data is available to be received from the device.
- //
- #define IS_DATA_AVAILABLE(_Device) \
- ((READ_STATUS_REGISTER(_Device) & I8042_STATUS_OUTPUT_BUFFER_FULL) != 0)
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the size of the device keyboard buffer size.
- //
- #define I8042_BUFFER_SIZE 256
- //
- // Define the bits in the 8042 status register.
- //
- #define I8042_STATUS_OUTPUT_BUFFER_FULL 0x01
- #define I8042_STATUS_INPUT_BUFFER_FULL 0x02
- #define I8042_STATUS_SELF_TEST_COMPLETE 0x04
- #define I8042_STATUS_LAST_WRITE_COMMAND 0x08
- #define I8042_STATUS_KEYBOARD_UNLOCK 0x10
- #define I8042_STATUS_DATA_FROM_MOUSE 0x20
- #define I8042_STATUS_TIMEOUT 0x40
- #define I8042_STATUS_PARITY_ERROR 0x80
- //
- // Define bits in the 8042 command byte register.
- //
- #define I8042_COMMAND_BYTE_KEYBOARD_INTERRUPT_ENABLED 0x01
- #define I8042_COMMAND_BYTE_MOUSE_INTERRUPT_ENABLED 0x02
- #define I8042_COMMAND_BYTE_SYSTEM_FLAG 0x04
- #define I8042_COMMAND_BYTE_PCAT_INHIBIT 0x08
- #define I8042_COMMAND_BYTE_KEYBOARD_DISABLED 0x10
- #define I8042_COMMAND_BYTE_MOUSE_DISABLED 0x20
- #define I8042_COMMAND_BYTE_TRANSLATION_ENABLED 0x40
- //
- // Define the known device identifiers that this driver responds to.
- //
- #define KEYBOARD_HARDWARE_IDENTIFIER "PNP0303"
- #define MOUSE_HARDWARE_IDENTIFIER "PNP0F13"
- //
- // Define the allocation tag used by this driver.
- //
- #define I8042_ALLOCATION_TAG 0x32343869 // '248i'
- //
- // Define the amount of time to allow the keyboard to reset, in microseconds.
- //
- #define I8042_RESET_DELAY 10000
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- /*++
- Structure Description:
- This structure stores context about a device driven by the i8042 driver.
- Members:
- IsMouse - Stores a boolean indicating whether the device is a mouse (TRUE)
- or a keyboard (FALSE).
- ControlPort - Stores the I/O port number of the 8042 control port.
- DataPort - Stores the I/O port number of the 8042 data port.
- InterruptVector - Stores the interrupt vector that this interrupt comes in
- on.
- InterruptLine - Stores the interrupt line that the interrupt comes in on.
- InterruptResourcesFound - Stores a boolean indicating whether or not the
- interrupt vector and interrupt line fields are valid.
- InterruptHandle - Stores the handle for the connected interrupt.
- UserInputDeviceHandle - Stores the handle returned by the User Input
- library.
- InterruptLock - Stores a spinlock synchronizing access to the device with
- the interrupt service routine.
- ReadLock - Stores a pointer to a queued lock that serializes read access
- to the data buffer.
- ReadIndex - Stores the index of the next byte to read out of the data
- buffer.
- WriteIndex - Stores the index of the next byte to write to the data buffer.
- DataBuffer - Stores the buffer of keys coming out of the controller.
- --*/
- typedef struct _I8042_DEVICE {
- BOOL IsMouse;
- USHORT ControlPort;
- USHORT DataPort;
- ULONGLONG InterruptVector;
- ULONGLONG InterruptLine;
- BOOL InterruptResourcesFound;
- HANDLE InterruptHandle;
- HANDLE UserInputDeviceHandle;
- KSPIN_LOCK InterruptLock;
- PQUEUED_LOCK ReadLock;
- volatile ULONG ReadIndex;
- volatile ULONG WriteIndex;
- volatile UCHAR DataBuffer[I8042_BUFFER_SIZE];
- } I8042_DEVICE, *PI8042_DEVICE;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- I8042AddDevice (
- PVOID Driver,
- PSTR DeviceId,
- PSTR ClassId,
- PSTR CompatibleIds,
- PVOID DeviceToken
- );
- VOID
- I8042DispatchStateChange (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- VOID
- I8042DispatchOpen (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- VOID
- I8042DispatchClose (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- VOID
- I8042DispatchIo (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- VOID
- I8042DispatchSystemControl (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- INTERRUPT_STATUS
- I8042InterruptService (
- PVOID Context
- );
- INTERRUPT_STATUS
- I8042InterruptServiceWorker (
- PVOID Parameter
- );
- KSTATUS
- I8042pProcessResourceRequirements (
- PIRP Irp,
- PI8042_DEVICE Device
- );
- KSTATUS
- I8042pStartDevice (
- PIRP Irp,
- PI8042_DEVICE Device
- );
- KSTATUS
- I8042pEnableDevice (
- PVOID OsDevice,
- PI8042_DEVICE Device
- );
- KSTATUS
- I8042pSetLedState (
- PVOID Device,
- PVOID DeviceContext,
- ULONG LedState
- );
- UCHAR
- I8042pReadCommandByte (
- PI8042_DEVICE Device
- );
- VOID
- I8042pWriteCommandByte (
- PI8042_DEVICE Device,
- UCHAR Value
- );
- KSTATUS
- I8042pSendKeyboardCommand (
- PI8042_DEVICE Device,
- UCHAR Command,
- UCHAR Parameter
- );
- KSTATUS
- I8042pSendCommand (
- PI8042_DEVICE Device,
- UCHAR Command
- );
- KSTATUS
- I8042pReceiveResponse (
- PI8042_DEVICE Device,
- PUCHAR Data
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- PDRIVER I8042Driver = NULL;
- //
- // ------------------------------------------------------------------ Functions
- //
- KSTATUS
- DriverEntry (
- PDRIVER Driver
- )
- /*++
- Routine Description:
- This routine is the entry point for the i8042 driver. It registers its other
- dispatch functions, and performs driver-wide initialization.
- Arguments:
- Driver - Supplies a pointer to the driver object.
- Return Value:
- STATUS_SUCCESS on success.
- Failure code on error.
- --*/
- {
- DRIVER_FUNCTION_TABLE FunctionTable;
- KSTATUS Status;
- I8042Driver = Driver;
- RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE));
- FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION;
- FunctionTable.AddDevice = I8042AddDevice;
- FunctionTable.DispatchStateChange = I8042DispatchStateChange;
- FunctionTable.DispatchOpen = I8042DispatchOpen;
- FunctionTable.DispatchClose = I8042DispatchClose;
- FunctionTable.DispatchIo = I8042DispatchIo;
- FunctionTable.DispatchSystemControl = I8042DispatchSystemControl;
- Status = IoRegisterDriverFunctions(Driver, &FunctionTable);
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- I8042AddDevice (
- PVOID Driver,
- PSTR DeviceId,
- PSTR ClassId,
- PSTR CompatibleIds,
- PVOID DeviceToken
- )
- /*++
- Routine Description:
- This routine is called when a device is detected for which the i8042 driver
- acts as the function driver. The driver will attach itself to the stack.
- Arguments:
- Driver - Supplies a pointer to the driver being called.
- DeviceId - Supplies a pointer to a string with the device ID.
- ClassId - Supplies a pointer to a string containing the device's class ID.
- CompatibleIds - Supplies a pointer to a string containing device IDs
- that would be compatible with this device.
- DeviceToken - Supplies an opaque token that the driver can use to identify
- the device in the system. This token should be used when attaching to
- the stack.
- Return Value:
- STATUS_SUCCESS on success.
- Failure code if the driver was unsuccessful in attaching itself.
- --*/
- {
- BOOL DeviceIdsAreEqual;
- BOOL DeviceIsMouse;
- BOOL MatchesCompatibleId;
- BOOL MatchFound;
- PI8042_DEVICE NewDevice;
- KSTATUS Status;
- DeviceIsMouse = FALSE;
- MatchFound = FALSE;
- DeviceIdsAreEqual = IoAreDeviceIdsEqual(DeviceId,
- KEYBOARD_HARDWARE_IDENTIFIER);
- MatchesCompatibleId =
- IoIsDeviceIdInCompatibleIdList(KEYBOARD_HARDWARE_IDENTIFIER,
- DeviceToken);
- if ((DeviceIdsAreEqual != FALSE) || (MatchesCompatibleId != FALSE)) {
- MatchFound = TRUE;
- } else {
- //
- // Attempt to match against the mouse ID.
- //
- DeviceIdsAreEqual = IoAreDeviceIdsEqual(DeviceId,
- MOUSE_HARDWARE_IDENTIFIER);
- MatchesCompatibleId =
- IoIsDeviceIdInCompatibleIdList(MOUSE_HARDWARE_IDENTIFIER,
- DeviceToken);
- if ((DeviceIdsAreEqual != FALSE) || (MatchesCompatibleId != FALSE)) {
- MatchFound = TRUE;
- DeviceIsMouse = TRUE;
- }
- }
- //
- // If there is no match, return now.
- //
- if (MatchFound == FALSE) {
- return STATUS_SUCCESS;
- }
- //
- // there is a match, create the device context and attach to the device.
- //
- NewDevice = MmAllocateNonPagedPool(sizeof(I8042_DEVICE),
- I8042_ALLOCATION_TAG);
- if (NewDevice == NULL) {
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- RtlZeroMemory(NewDevice, sizeof(I8042_DEVICE));
- KeInitializeSpinLock(&(NewDevice->InterruptLock));
- NewDevice->InterruptHandle = INVALID_HANDLE;
- NewDevice->UserInputDeviceHandle = INVALID_HANDLE;
- NewDevice->IsMouse = DeviceIsMouse;
- NewDevice->ReadLock = KeCreateQueuedLock();
- if (NewDevice->ReadLock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto AddDeviceEnd;
- }
- Status = IoAttachDriverToDevice(Driver, DeviceToken, NewDevice);
- if (!KSUCCESS(Status)) {
- goto AddDeviceEnd;
- }
- AddDeviceEnd:
- if (!KSUCCESS(Status)) {
- if (NewDevice != NULL) {
- if (NewDevice->ReadLock != NULL) {
- KeDestroyQueuedLock(NewDevice->ReadLock);
- }
- MmFreeNonPagedPool(NewDevice);
- }
- }
- return Status;
- }
- VOID
- I8042DispatchStateChange (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- )
- /*++
- Routine Description:
- This routine handles State Change IRPs.
- Arguments:
- Irp - Supplies a pointer to the I/O request packet.
- DeviceContext - Supplies the context pointer supplied by the driver when it
- attached itself to the driver stack. Presumably this pointer contains
- driver-specific device context.
- IrpContext - Supplies the context pointer supplied by the driver when
- the IRP was created.
- Return Value:
- None.
- --*/
- {
- PI8042_DEVICE Device;
- KSTATUS Status;
- ASSERT(Irp->MajorCode == IrpMajorStateChange);
- Device = (PI8042_DEVICE)DeviceContext;
- switch (Irp->MinorCode) {
- case IrpMinorQueryResources:
- //
- // On the way up, filter the resource requirements to add interrupt
- // vectors to any lines.
- //
- if (Irp->Direction == IrpUp) {
- Status = I8042pProcessResourceRequirements(Irp, Device);
- if (!KSUCCESS(Status)) {
- IoCompleteIrp(I8042Driver, Irp, Status);
- }
- }
- break;
- case IrpMinorStartDevice:
- //
- // Attempt to fire the thing up if the bus has already started it.
- //
- if (Irp->Direction == IrpUp) {
- Status = I8042pStartDevice(Irp, Device);
- if (!KSUCCESS(Status)) {
- IoCompleteIrp(I8042Driver, Irp, Status);
- }
- }
- break;
- //
- // For all other IRPs, do nothing.
- //
- default:
- break;
- }
- return;
- }
- VOID
- I8042DispatchOpen (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- )
- /*++
- Routine Description:
- This routine handles Open IRPs.
- Arguments:
- Irp - Supplies a pointer to the I/O request packet.
- DeviceContext - Supplies the context pointer supplied by the driver when it
- attached itself to the driver stack. Presumably this pointer contains
- driver-specific device context.
- IrpContext - Supplies the context pointer supplied by the driver when
- the IRP was created.
- Return Value:
- None.
- --*/
- {
- return;
- }
- VOID
- I8042DispatchClose (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- )
- /*++
- Routine Description:
- This routine handles Close IRPs.
- Arguments:
- Irp - Supplies a pointer to the I/O request packet.
- DeviceContext - Supplies the context pointer supplied by the driver when it
- attached itself to the driver stack. Presumably this pointer contains
- driver-specific device context.
- IrpContext - Supplies the context pointer supplied by the driver when
- the IRP was created.
- Return Value:
- None.
- --*/
- {
- return;
- }
- VOID
- I8042DispatchIo (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- )
- /*++
- Routine Description:
- This routine handles I/O IRPs.
- Arguments:
- Irp - Supplies a pointer to the I/O request packet.
- DeviceContext - Supplies the context pointer supplied by the driver when it
- attached itself to the driver stack. Presumably this pointer contains
- driver-specific device context.
- IrpContext - Supplies the context pointer supplied by the driver when
- the IRP was created.
- Return Value:
- None.
- --*/
- {
- return;
- }
- VOID
- I8042DispatchSystemControl (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- )
- /*++
- Routine Description:
- This routine handles System Control IRPs.
- Arguments:
- Irp - Supplies a pointer to the I/O request packet.
- DeviceContext - Supplies the context pointer supplied by the driver when it
- attached itself to the driver stack. Presumably this pointer contains
- driver-specific device context.
- IrpContext - Supplies the context pointer supplied by the driver when
- the IRP was created.
- Return Value:
- None.
- --*/
- {
- ASSERT(Irp->MajorCode == IrpMajorSystemControl);
- //
- // Do no processing on any IRPs. Let them flow.
- //
- return;
- }
- INTERRUPT_STATUS
- I8042InterruptService (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine implements the 8042 keyboard controller interrupt service
- routine.
- Arguments:
- Context - Supplies the context pointer given to the system when the
- interrupt was connected. In this case, this points to the 8042 device
- context.
- Return Value:
- Interrupt status.
- --*/
- {
- UCHAR Byte;
- PI8042_DEVICE Device;
- INTERRUPT_STATUS InterruptStatus;
- ULONG WriteIndex;
- Device = (PI8042_DEVICE)Context;
- InterruptStatus = InterruptStatusNotClaimed;
- //
- // Check to see if there is data waiting.
- //
- if ((READ_STATUS_REGISTER(Device) & I8042_STATUS_OUTPUT_BUFFER_FULL) != 0) {
- //
- // There was data here, so most likely it was this device interrupting.
- //
- InterruptStatus = InterruptStatusClaimed;
- //
- // Read the bytes out of the controller.
- //
- KeAcquireSpinLock(&(Device->InterruptLock));
- WriteIndex = Device->WriteIndex;
- while ((READ_STATUS_REGISTER(Device) &
- I8042_STATUS_OUTPUT_BUFFER_FULL) != 0) {
- Byte = READ_DATA_REGISTER(Device);
- if (((WriteIndex + 1) % I8042_BUFFER_SIZE) != Device->ReadIndex) {
- Device->DataBuffer[WriteIndex] = Byte;
- //
- // Advance the write index.
- //
- if ((WriteIndex + 1) == I8042_BUFFER_SIZE) {
- WriteIndex = 0;
- } else {
- WriteIndex += 1;
- }
- } else {
- RtlDebugPrint("I8042: Buffer overflow, losing byte %02X\n",
- Byte);
- }
- }
- //
- // Save the new write index now that everything's out.
- //
- Device->WriteIndex = WriteIndex;
- KeReleaseSpinLock(&(Device->InterruptLock));
- }
- return InterruptStatus;
- }
- INTERRUPT_STATUS
- I8042InterruptServiceWorker (
- PVOID Parameter
- )
- /*++
- Routine Description:
- This routine processes interrupts for the e100 controller at low level.
- Arguments:
- Parameter - Supplies an optional parameter passed in by the creator of the
- work item.
- Return Value:
- Interrupt status.
- --*/
- {
- UCHAR Byte;
- UCHAR Code1;
- UCHAR Code2;
- UCHAR Code3;
- PI8042_DEVICE Device;
- USER_INPUT_EVENT Event;
- BOOL KeyUp;
- ULONG ReadIndex;
- Code1 = 0;
- Code2 = 0;
- Code3 = 0;
- Device = (PI8042_DEVICE)Parameter;
- ASSERT(KeGetRunLevel() == RunLevelLow);
- RtlZeroMemory(&Event, sizeof(USER_INPUT_EVENT));
- //
- // Pull as much data out of the buffer as there is.
- //
- KeAcquireQueuedLock(Device->ReadLock);
- ReadIndex = Device->ReadIndex;
- while (ReadIndex != Device->WriteIndex) {
- Byte = Device->DataBuffer[ReadIndex];
- ReadIndex += 1;
- if (ReadIndex == I8042_BUFFER_SIZE) {
- ReadIndex = 0;
- }
- //
- // If the first byte read was the extended 2 code, then another 2 bytes
- // should be coming in. Get those bytes.
- //
- if (Code1 == SCAN_CODE_1_EXTENDED_2_CODE) {
- if (Code2 == 0) {
- Code2 = Byte;
- continue;
- }
- Code3 = Byte;
- //
- // If the first byte read was the extended (1) code, then another byte
- // should be coming in. Get that byte.
- //
- } else if (Code1 == SCAN_CODE_1_EXTENDED_CODE) {
- Code2 = Byte;
- } else {
- Code1 = Byte;
- if ((Code1 == SCAN_CODE_1_EXTENDED_CODE) ||
- (Code1 == SCAN_CODE_1_EXTENDED_2_CODE)) {
- continue;
- }
- }
- //
- // Get the specifics of the event.
- //
- Event.U.Key = I8042ConvertScanCodeToKey(Code1, Code2, Code3, &KeyUp);
- if (Event.U.Key != KeyboardKeyInvalid) {
- if (KeyUp != FALSE) {
- Event.EventType = UserInputEventKeyUp;
- } else {
- Event.EventType = UserInputEventKeyDown;
- }
- //
- // Log the event.
- //
- InReportInputEvent(Device->UserInputDeviceHandle, &Event);
- }
- //
- // A full key combination was read, move the read index forward.
- //
- Device->ReadIndex = ReadIndex;
- Code1 = 0;
- Code2 = 0;
- }
- KeReleaseQueuedLock(Device->ReadLock);
- return InterruptStatusClaimed;
- }
- KSTATUS
- I8042pProcessResourceRequirements (
- PIRP Irp,
- PI8042_DEVICE Device
- )
- /*++
- Routine Description:
- This routine filters through the resource requirements presented by the
- bus. It adds an interrupt vector requirement for any interrupt line
- requested.
- Arguments:
- Irp - Supplies a pointer to the I/O request packet.
- Device - Supplies a pointer to this 8042 controller device.
- Return Value:
- Status code.
- --*/
- {
- PRESOURCE_CONFIGURATION_LIST Requirements;
- KSTATUS Status;
- RESOURCE_REQUIREMENT VectorRequirement;
- ASSERT((Irp->MajorCode == IrpMajorStateChange) &&
- (Irp->MinorCode == IrpMinorQueryResources));
- //
- // Initialize a nice interrupt vector requirement in preparation.
- //
- RtlZeroMemory(&VectorRequirement, sizeof(RESOURCE_REQUIREMENT));
- VectorRequirement.Type = ResourceTypeInterruptVector;
- VectorRequirement.Minimum = 0;
- VectorRequirement.Maximum = -1;
- VectorRequirement.Length = 1;
- //
- // Loop through all configuration lists, creating a vector for each line.
- //
- Requirements = Irp->U.QueryResources.ResourceRequirements;
- Status = IoCreateAndAddInterruptVectorsForLines(Requirements,
- &VectorRequirement);
- if (!KSUCCESS(Status)) {
- goto ProcessResourceRequirementsEnd;
- }
- ProcessResourceRequirementsEnd:
- return Status;
- }
- KSTATUS
- I8042pStartDevice (
- PIRP Irp,
- PI8042_DEVICE Device
- )
- /*++
- Routine Description:
- This routine starts up the 8042 controller.
- Arguments:
- Irp - Supplies a pointer to the I/O request packet.
- Device - Supplies a pointer to this 8042 controller device.
- Return Value:
- Status code.
- --*/
- {
- PRESOURCE_ALLOCATION Allocation;
- PRESOURCE_ALLOCATION_LIST AllocationList;
- IO_CONNECT_INTERRUPT_PARAMETERS Connect;
- BOOL ControlFound;
- BOOL DataFound;
- PRESOURCE_ALLOCATION LineAllocation;
- KSTATUS Status;
- ControlFound = FALSE;
- DataFound = FALSE;
- //
- // If there are no resources, then return success but don't start anything.
- //
- AllocationList = Irp->U.StartDevice.ProcessorLocalResources;
- if (AllocationList == NULL) {
- Status = STATUS_SUCCESS;
- goto StartDeviceEnd;
- }
- //
- // Loop through the allocated resources to get the control and data ports,
- // and the interrupt.
- //
- Allocation = IoGetNextResourceAllocation(AllocationList, NULL);
- while (Allocation != NULL) {
- if (Allocation->Type == ResourceTypeIoPort) {
- ASSERT(Allocation->Length == 1);
- ASSERT(Allocation->Allocation <= 0xFFFF);
- //
- // Assume the first resource is the data port.
- //
- if (DataFound == FALSE) {
- Device->DataPort = (USHORT)Allocation->Allocation;
- DataFound = TRUE;
- //
- // The second resource must be the control port.
- //
- } else if (ControlFound == FALSE) {
- Device->ControlPort = (USHORT)Allocation->Allocation;
- ControlFound = TRUE;
- }
- //
- // If the resource is an interrupt vector, then it should have an
- // owning interrupt line allocation.
- //
- } else if (Allocation->Type == ResourceTypeInterruptVector) {
- //
- // Currently only one interrupt resource is expected.
- //
- ASSERT(Device->InterruptResourcesFound == FALSE);
- ASSERT(Allocation->OwningAllocation != NULL);
- //
- // Save the line and vector number.
- //
- LineAllocation = Allocation->OwningAllocation;
- Device->InterruptLine = LineAllocation->Allocation;
- Device->InterruptVector = Allocation->Allocation;
- Device->InterruptResourcesFound = TRUE;
- }
- //
- // Get the next allocation in the list.
- //
- Allocation = IoGetNextResourceAllocation(AllocationList, Allocation);
- }
- //
- // Fail if both ports were not found.
- //
- if ((DataFound == FALSE) || (ControlFound == FALSE)) {
- Status = STATUS_INVALID_CONFIGURATION;
- goto StartDeviceEnd;
- }
- //
- // Fire up the device.
- //
- Status = I8042pEnableDevice(Irp->Device, Device);
- if (!KSUCCESS(Status)) {
- goto StartDeviceEnd;
- }
- //
- // Attempt to connect the interrupt.
- //
- ASSERT(Device->InterruptHandle == INVALID_HANDLE);
- RtlZeroMemory(&Connect, sizeof(IO_CONNECT_INTERRUPT_PARAMETERS));
- Connect.Version = IO_CONNECT_INTERRUPT_PARAMETERS_VERSION;
- Connect.Device = Irp->Device;
- Connect.LineNumber = Device->InterruptLine;
- Connect.Vector = Device->InterruptVector;
- Connect.InterruptServiceRoutine = I8042InterruptService;
- Connect.LowLevelServiceRoutine = I8042InterruptServiceWorker;
- Connect.Context = Device;
- Connect.Interrupt = &(Device->InterruptHandle);
- Status = IoConnectInterrupt(&Connect);
- if (!KSUCCESS(Status)) {
- goto StartDeviceEnd;
- }
- StartDeviceEnd:
- if (!KSUCCESS(Status)) {
- Device->InterruptResourcesFound = FALSE;
- if (Device->InterruptHandle != INVALID_HANDLE) {
- IoDisconnectInterrupt(Device->InterruptHandle);
- Device->InterruptHandle = INVALID_HANDLE;
- }
- if (Device->UserInputDeviceHandle != INVALID_HANDLE) {
- InDestroyInputDevice(Device->UserInputDeviceHandle);
- Device->UserInputDeviceHandle = INVALID_HANDLE;
- }
- }
- return Status;
- }
- KSTATUS
- I8042pEnableDevice (
- PVOID OsDevice,
- PI8042_DEVICE Device
- )
- /*++
- Routine Description:
- This routine enables the given 8042 device.
- Arguments:
- OsDevice - Supplies a pointer to the OS device token.
- Device - Supplies a pointer to this 8042 controller device.
- Return Value:
- Status code.
- --*/
- {
- UCHAR CommandByte;
- USER_INPUT_DEVICE_DESCRIPTION Description;
- UCHAR Response;
- KSTATUS Status;
- BOOL TwoPorts;
- if (Device->IsMouse != FALSE) {
- //
- // Mice are not currently supported.
- //
- return STATUS_NOT_IMPLEMENTED;
- } else {
- //
- // Disable both ports.
- //
- I8042pSendCommand(Device, I8042_COMMAND_DISABLE_KEYBOARD);
- I8042pSendCommand(Device, I8042_COMMAND_DISABLE_MOUSE_PORT);
- //
- // Flush any leftover data out.
- //
- while ((READ_STATUS_REGISTER(Device) &
- I8042_STATUS_OUTPUT_BUFFER_FULL) != 0) {
- READ_DATA_REGISTER(Device);
- }
- //
- // Enable the keyboard in the command byte. Disable the interrupt
- // for now during setup.
- //
- CommandByte = I8042pReadCommandByte(Device);
- CommandByte &= ~(I8042_COMMAND_BYTE_KEYBOARD_DISABLED |
- I8042_COMMAND_BYTE_PCAT_INHIBIT |
- I8042_COMMAND_BYTE_KEYBOARD_INTERRUPT_ENABLED |
- I8042_COMMAND_BYTE_MOUSE_INTERRUPT_ENABLED);
- I8042pWriteCommandByte(Device, CommandByte);
- //
- // Send a self test to the controller itself, and verify that it
- // passes.
- //
- I8042pSendCommand(Device, I8042_COMMAND_SELF_TEST);
- HlBusySpin(I8042_RESET_DELAY);
- Status = I8042pReceiveResponse(Device, &Response);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- if (Response != I8042_SELF_TEST_SUCCESS) {
- RtlDebugPrint("i8042: Received %x to keyboard reset instead of "
- "expected %x.\n",
- Response,
- I8042_SELF_TEST_SUCCESS);
- Status = STATUS_DEVICE_IO_ERROR;
- goto EnableDeviceEnd;
- }
- //
- // Determine if there are two ports. Enable the mouse port, and the
- // "data from mouse" bit in the status should clear.
- //
- TwoPorts = FALSE;
- I8042pSendCommand(Device, I8042_COMMAND_ENABLE_MOUSE_PORT);
- if ((READ_STATUS_REGISTER(Device) &
- I8042_STATUS_DATA_FROM_MOUSE) == 0) {
- TwoPorts = TRUE;
- }
- I8042pSendCommand(Device, I8042_COMMAND_DISABLE_MOUSE_PORT);
- //
- // Test the ports.
- //
- I8042pSendCommand(Device, I8042_COMMAND_INTERFACE_TEST);
- Status = I8042pReceiveResponse(Device, &Response);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- if (Response != I8042_PORT_TEST_SUCCESS) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto EnableDeviceEnd;
- }
- if (TwoPorts != FALSE) {
- I8042pSendCommand(Device, I8042_COMMAND_TEST_MOUSE_PORT);
- Status = I8042pReceiveResponse(Device, &Response);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- if (Response != I8042_PORT_TEST_SUCCESS) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto EnableDeviceEnd;
- }
- }
- //
- // Enable the ports.
- //
- I8042pSendCommand(Device, I8042_COMMAND_ENABLE_KEYBOARD);
- if (TwoPorts != FALSE) {
- I8042pSendCommand(Device, I8042_COMMAND_ENABLE_MOUSE_PORT);
- }
- //
- // Reset the keyboard.
- //
- Status = I8042pSendKeyboardCommand(Device,
- KEYBOARD_COMMAND_RESET,
- KEYBOARD_COMMAND_NO_PARAMETER);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- //
- // Read the BAT (Basic Assurance Test) code that the keyboard sends
- // when it finishes resetting.
- //
- Status = I8042pReceiveResponse(Device, &Response);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- if (Response != KEYBOARD_BAT_PASS) {
- goto EnableDeviceEnd;
- }
- //
- // Set the typematic rate/delay on the keyboard. Start by sending the
- // command.
- //
- Status = I8042pSendKeyboardCommand(Device,
- KEYBOARD_COMMAND_SET_TYPEMATIC,
- DEFAULT_TYPEMATIC_VALUE);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- //
- // Enable the keyboard.
- //
- Status = I8042pSendKeyboardCommand(Device,
- KEYBOARD_COMMAND_ENABLE,
- KEYBOARD_COMMAND_NO_PARAMETER);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- //
- // Create the user input handle if not already done.
- //
- if (Device->UserInputDeviceHandle == INVALID_HANDLE) {
- Description.Device = OsDevice;
- Description.DeviceContext = Device;
- Description.Type = UserInputDeviceKeyboard;
- Description.InterfaceVersion =
- USER_INPUT_KEYBOARD_DEVICE_INTERFACE_VERSION;
- Description.U.KeyboardInterface.SetLedState = I8042pSetLedState;
- Device->UserInputDeviceHandle = InRegisterInputDevice(&Description);
- if (Device->UserInputDeviceHandle == INVALID_HANDLE) {
- Status = STATUS_UNSUCCESSFUL;
- goto EnableDeviceEnd;
- }
- }
- //
- // Enable the keyboard interrupt.
- //
- CommandByte |= I8042_COMMAND_BYTE_KEYBOARD_INTERRUPT_ENABLED;
- I8042pWriteCommandByte(Device, CommandByte);
- }
- EnableDeviceEnd:
- return Status;
- }
- KSTATUS
- I8042pSetLedState (
- PVOID Device,
- PVOID DeviceContext,
- ULONG LedState
- )
- /*++
- Routine Description:
- This routine sets a keyboard's LED state (e.g. Number lock, Caps lock and
- scroll lock). The state is absolute; the desired state for each LED must be
- supplied.
- Arguments:
- Device - Supplies a pointer to the OS device representing the user input
- device.
- DeviceContext - Supplies the opaque device context supplied in the device
- description upon registration with the user input library.
- LedState - Supplies a bitmask of flags describing the desired LED state.
- See USER_INPUT_KEYBOARD_LED_* for definition.
- Return Value:
- Status code.
- --*/
- {
- PI8042_DEVICE I8042Device;
- UCHAR KeyboardLedState;
- KSTATUS Status;
- I8042Device = (PI8042_DEVICE)DeviceContext;
- //
- // Convert the LED state to the proper format.
- //
- KeyboardLedState = 0;
- if ((LedState & USER_INPUT_KEYBOARD_LED_SCROLL_LOCK) != 0) {
- KeyboardLedState |= KEYBOARD_LED_SCROLL_LOCK;
- }
- if ((LedState & USER_INPUT_KEYBOARD_LED_NUM_LOCK) != 0) {
- KeyboardLedState |= KEYBOARD_LED_NUM_LOCK;
- }
- if ((LedState & USER_INPUT_KEYBOARD_LED_CAPS_LOCK) != 0) {
- KeyboardLedState |= KEYBOARD_LED_CAPS_LOCK;
- }
- Status = I8042pSendKeyboardCommand(I8042Device,
- KEYBOARD_COMMAND_SET_LEDS,
- KeyboardLedState);
- return Status;
- }
- UCHAR
- I8042pReadCommandByte (
- PI8042_DEVICE Device
- )
- /*++
- Routine Description:
- This routine reads the contents of the command byte in the 8042 keyboard
- controller.
- Arguments:
- Device - Supplies a pointer to this 8042 controller device.
- Return Value:
- Status code.
- --*/
- {
- I8042pSendCommand(Device, I8042_COMMAND_READ_COMMAND_BYTE);
- return READ_DATA_REGISTER(Device);
- }
- VOID
- I8042pWriteCommandByte (
- PI8042_DEVICE Device,
- UCHAR Value
- )
- /*++
- Routine Description:
- This routine reads the contents of the command byte in the 8042 keyboard
- controller.
- Arguments:
- Device - Supplies a pointer to this 8042 controller device.
- Value - Supplies the value to write to the command register.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- Status = I8042pSendCommand(Device, I8042_COMMAND_WRITE_COMMAND_BYTE);
- if (KSUCCESS(Status)) {
- WRITE_DATA_REGISTER(Device, Value);
- }
- return;
- }
- KSTATUS
- I8042pSendKeyboardCommand (
- PI8042_DEVICE Device,
- UCHAR Command,
- UCHAR Parameter
- )
- /*++
- Routine Description:
- This routine sends a command byte to the keyboard itself (not the
- keyboard controller) and checks the return status byte.
- Arguments:
- Device - Supplies a pointer to this 8042 controller device.
- Command - Supplies the command to write to the keyboard.
- Parameter - Supplies an additional byte to send. Set this to 0xFF to
- skip sending this byte.
- Return Value:
- Status code indicating whether the keyboard succeeded or failed the
- command.
- --*/
- {
- UCHAR KeyboardResult;
- KSTATUS Status;
- WAIT_FOR_INPUT_BUFFER(Device);
- WRITE_DATA_REGISTER(Device, Command);
- if (Parameter != KEYBOARD_COMMAND_NO_PARAMETER) {
- WAIT_FOR_INPUT_BUFFER(Device);
- WRITE_DATA_REGISTER(Device, Parameter);
- }
- Status = I8042pReceiveResponse(Device, &KeyboardResult);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- if (KeyboardResult == KEYBOARD_STATUS_ACKNOWLEDGE) {
- return STATUS_SUCCESS;
- }
- if (KeyboardResult == KEYBOARD_STATUS_RESEND) {
- return STATUS_NOT_READY;
- }
- if (KeyboardResult == KEYBOARD_STATUS_OVERRUN) {
- return STATUS_BUFFER_OVERRUN;
- }
- return STATUS_DEVICE_IO_ERROR;
- }
- KSTATUS
- I8042pSendCommand (
- PI8042_DEVICE Device,
- UCHAR Command
- )
- /*++
- Routine Description:
- This routine sends a command to the PS/2 controller (not the device
- connected to it).
- Arguments:
- Device - Supplies a pointer to this 8042 controller device.
- Command - Supplies the command to write to the controller.
- Return Value:
- Status code indicating whether the keyboard succeeded or failed the
- command.
- --*/
- {
- WAIT_FOR_INPUT_BUFFER(Device);
- WRITE_CONTROL_REGISTER(Device, Command);
- WAIT_FOR_INPUT_BUFFER(Device);
- return STATUS_SUCCESS;
- }
- KSTATUS
- I8042pReceiveResponse (
- PI8042_DEVICE Device,
- PUCHAR Data
- )
- /*++
- Routine Description:
- This routine receives a byte from the data port, with a timeout.
- Arguments:
- Device - Supplies a pointer to this 8042 controller device.
- Data - Supplies a pointer where the data will be returned on success.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS StatusCode;
- UCHAR StatusRegister;
- ULONGLONG Timeout;
- Timeout = KeGetRecentTimeCounter() +
- ((HlQueryTimeCounterFrequency() * I8042_COMMAND_TIMEOUT) /
- MILLISECONDS_PER_SECOND);
- StatusCode = STATUS_TIMEOUT;
- do {
- StatusRegister = READ_STATUS_REGISTER(Device);
- if ((StatusRegister & I8042_STATUS_TIMEOUT) != 0) {
- StatusCode = STATUS_TIMEOUT;
- break;
- } else if ((StatusRegister & I8042_STATUS_PARITY_ERROR) != 0) {
- StatusCode = STATUS_PARITY_ERROR;
- break;
- } else if ((StatusRegister & I8042_STATUS_OUTPUT_BUFFER_FULL) != 0) {
- *Data = READ_DATA_REGISTER(Device);
- StatusCode = STATUS_SUCCESS;
- break;
- }
- } while (KeGetRecentTimeCounter() <= Timeout);
- return StatusCode;
- }
|