1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- pl050.c
- Abstract:
- This module implements the driver for the ARM PrimeCell PL050 keyboard and
- mouse controller.
- Author:
- Evan Green 22-Sep-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/driver.h>
- #include "../i8042.h"
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // These macros read and write byte registers in the PL050. The first register
- // is a pointer to the controller structure, the second is the register number.
- //
- #define PL050_READ(_Controller, _Register) \
- HlReadRegister8(((PUCHAR)(_Controller)->RegisterBase) + (_Register))
- #define PL050_WRITE(_Controller, _Register, _Value) \
- HlWriteRegister8(((PUCHAR)(_Controller)->RegisterBase) + (_Register), \
- (_Value))
- //
- // This macro spins waiting for the last keyboard command to finish.
- //
- #define WAIT_FOR_INPUT_BUFFER(_Device) \
- while ((PL050_READ(_Device, Pl050RegisterStatus) & \
- PL050_STATUS_TRANSMIT_EMPTY) == 0) { \
- \
- NOTHING; \
- }
- //
- // This macro determines if data is available to be received from the device.
- //
- #define IS_DATA_AVAILABLE(_Device) \
- ((PL050_READ(_Device, Pl050RegisterStatus) & \
- PL050_STATUS_RECEIVE_FULL) != 0)
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // Define the size of the buffer of bytes stored directly by the ISR.
- //
- #define PL050_BUFFER_SIZE 256
- #define PL050_ALLOCATION_TAG 0x30506C50 // '05lP'
- //
- // Define the number of microseconds to wait for a command to complete.
- //
- #define PL050_COMMAND_TIMEOUT (50 * MICROSECONDS_PER_MILLISECOND)
- //
- // Define control register bits.
- //
- #define PL050_CONTROL_ENABLE 0x04
- #define PL050_CONTROL_TRANSMIT_INTERRUPT_ENABLE 0x08
- #define PL050_CONTROL_RECEIVE_INTERRUPT_ENABLE 0x10
- //
- // Define status register bits.
- //
- #define PL050_STATUS_RECEIVE_BUSY 0x08
- #define PL050_STATUS_RECEIVE_FULL 0x10
- #define PL050_STATUS_TRANSMIT_BUSY 0x20
- #define PL050_STATUS_TRANSMIT_EMPTY 0x40
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef enum _PL050_REGISTER {
- Pl050RegisterControl = 0x00,
- Pl050RegisterStatus = 0x04,
- Pl050RegisterData = 0x08,
- Pl050RegisterClockDivisor = 0x0C,
- Pl050RegisterInterruptStatus = 0x10
- } PL050_REGISTER, *PPL050_REGISTER;
- /*++
- Structure Description:
- This structure stores context about a device driven by the Pl050 driver.
- Members:
- IsMouse - Stores a boolean indicating whether the device is a mouse (TRUE)
- or a keyboard (FALSE).
- PhysicalAddress - Stores the physical address of the registers.
- RegisterBase - Stores the virtual address of the registers.
- 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 _PL050_DEVICE {
- BOOL IsMouse;
- ULONGLONG PhysicalAddress;
- PVOID RegisterBase;
- 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[PL050_BUFFER_SIZE];
- } PL050_DEVICE, *PPL050_DEVICE;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- Pl050AddDevice (
- PVOID Driver,
- PSTR DeviceId,
- PSTR ClassId,
- PSTR CompatibleIds,
- PVOID DeviceToken
- );
- VOID
- Pl050DispatchStateChange (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- VOID
- Pl050DispatchOpen (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- VOID
- Pl050DispatchClose (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- VOID
- Pl050DispatchIo (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- VOID
- Pl050DispatchSystemControl (
- PIRP Irp,
- PVOID DeviceContext,
- PVOID IrpContext
- );
- INTERRUPT_STATUS
- Pl050InterruptService (
- PVOID Context
- );
- INTERRUPT_STATUS
- Pl050InterruptServiceWorker (
- PVOID Parameter
- );
- KSTATUS
- Pl050pProcessResourceRequirements (
- PIRP Irp,
- PPL050_DEVICE Device
- );
- KSTATUS
- Pl050pStartDevice (
- PIRP Irp,
- PPL050_DEVICE Device
- );
- KSTATUS
- Pl050pEnableDevice (
- PVOID OsDevice,
- PPL050_DEVICE Device
- );
- KSTATUS
- Pl050pDisableDevice (
- PPL050_DEVICE Device
- );
- KSTATUS
- Pl050pSetLedState (
- PVOID Device,
- PVOID DeviceContext,
- ULONG LedState
- );
- KSTATUS
- Pl050pSendKeyboardCommand (
- PPL050_DEVICE Device,
- UCHAR Command,
- UCHAR Parameter
- );
- KSTATUS
- Pl050pSetScanSet (
- PPL050_DEVICE Device,
- UCHAR ScanSet
- );
- KSTATUS
- Pl050pIdentifyDevice (
- PPL050_DEVICE Device,
- PBOOL IsMouse
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- PDRIVER Pl050Driver = NULL;
- //
- // ------------------------------------------------------------------ Functions
- //
- KSTATUS
- DriverEntry (
- PDRIVER Driver
- )
- /*++
- Routine Description:
- This routine is the entry point for the Pl050 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;
- Pl050Driver = Driver;
- RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE));
- FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION;
- FunctionTable.AddDevice = Pl050AddDevice;
- FunctionTable.DispatchStateChange = Pl050DispatchStateChange;
- FunctionTable.DispatchOpen = Pl050DispatchOpen;
- FunctionTable.DispatchClose = Pl050DispatchClose;
- FunctionTable.DispatchIo = Pl050DispatchIo;
- FunctionTable.DispatchSystemControl = Pl050DispatchSystemControl;
- Status = IoRegisterDriverFunctions(Driver, &FunctionTable);
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- Pl050AddDevice (
- PVOID Driver,
- PSTR DeviceId,
- PSTR ClassId,
- PSTR CompatibleIds,
- PVOID DeviceToken
- )
- /*++
- Routine Description:
- This routine is called when a device is detected for which the Pl050 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.
- --*/
- {
- PPL050_DEVICE NewDevice;
- KSTATUS Status;
- //
- // there is a match, create the device context and attach to the device.
- //
- NewDevice = MmAllocateNonPagedPool(sizeof(PL050_DEVICE),
- PL050_ALLOCATION_TAG);
- if (NewDevice == NULL) {
- return STATUS_INSUFFICIENT_RESOURCES;
- }
- RtlZeroMemory(NewDevice, sizeof(PL050_DEVICE));
- KeInitializeSpinLock(&(NewDevice->InterruptLock));
- NewDevice->InterruptHandle = INVALID_HANDLE;
- NewDevice->UserInputDeviceHandle = INVALID_HANDLE;
- 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->UserInputDeviceHandle != INVALID_HANDLE) {
- InDestroyInputDevice(NewDevice->UserInputDeviceHandle);
- }
- if (NewDevice->ReadLock != NULL) {
- KeDestroyQueuedLock(NewDevice->ReadLock);
- }
- MmFreeNonPagedPool(NewDevice);
- }
- }
- return Status;
- }
- VOID
- Pl050DispatchStateChange (
- 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.
- --*/
- {
- PPL050_DEVICE Device;
- KSTATUS Status;
- ASSERT(Irp->MajorCode == IrpMajorStateChange);
- Device = (PPL050_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 = Pl050pProcessResourceRequirements(Irp, Device);
- if (!KSUCCESS(Status)) {
- IoCompleteIrp(Pl050Driver, Irp, Status);
- }
- }
- break;
- case IrpMinorStartDevice:
- //
- // Attempt to fire the thing up if the bus has already started it.
- //
- if (Irp->Direction == IrpUp) {
- Status = Pl050pStartDevice(Irp, Device);
- if (!KSUCCESS(Status)) {
- IoCompleteIrp(Pl050Driver, Irp, Status);
- }
- }
- break;
- //
- // For all other IRPs, do nothing.
- //
- default:
- break;
- }
- return;
- }
- VOID
- Pl050DispatchOpen (
- 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
- Pl050DispatchClose (
- 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
- Pl050DispatchIo (
- 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
- Pl050DispatchSystemControl (
- 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
- Pl050InterruptService (
- PVOID Context
- )
- /*++
- Routine Description:
- This routine implements the PL-050 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 device
- context.
- Return Value:
- Interrupt status.
- --*/
- {
- UCHAR Byte;
- PPL050_DEVICE Device;
- INTERRUPT_STATUS InterruptStatus;
- ULONG WriteIndex;
- Device = (PPL050_DEVICE)Context;
- InterruptStatus = InterruptStatusNotClaimed;
- //
- // Check to see if there is data waiting.
- //
- if ((PL050_READ(Device, Pl050RegisterStatus) &
- PL050_STATUS_RECEIVE_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 ((PL050_READ(Device, Pl050RegisterStatus) &
- PL050_STATUS_RECEIVE_FULL) != 0) {
- Byte = PL050_READ(Device, Pl050RegisterData);
- if (((WriteIndex + 1) % PL050_BUFFER_SIZE) != Device->ReadIndex) {
- Device->DataBuffer[WriteIndex] = Byte;
- //
- // Advance the write index.
- //
- if ((WriteIndex + 1) == PL050_BUFFER_SIZE) {
- WriteIndex = 0;
- } else {
- WriteIndex += 1;
- }
- } else {
- RtlDebugPrint("Pl050: Device 0x%08x, buffer overflow, losing "
- "byte %02X\n",
- Device,
- Byte);
- }
- }
- //
- // Save the new write index now that everything's out.
- //
- Device->WriteIndex = WriteIndex;
- KeReleaseSpinLock(&(Device->InterruptLock));
- }
- return InterruptStatus;
- }
- INTERRUPT_STATUS
- Pl050InterruptServiceWorker (
- PVOID Parameter
- )
- /*++
- Routine Description:
- This routine processes interrupts for the Pl050 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;
- PPL050_DEVICE Device;
- USER_INPUT_EVENT Event;
- BOOL KeyUp;
- ULONG ReadIndex;
- Code1 = 0;
- Code2 = 0;
- Code3 = 0;
- Device = (PPL050_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 == PL050_BUFFER_SIZE) {
- ReadIndex = 0;
- }
- if (Device->IsMouse != FALSE) {
- //
- // Eventually process the mouse events.
- //
- } else {
- //
- // If the first byte read was the extended 2 code, then another two
- // 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
- Pl050pProcessResourceRequirements (
- PIRP Irp,
- PPL050_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 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
- Pl050pStartDevice (
- PIRP Irp,
- PPL050_DEVICE Device
- )
- /*++
- Routine Description:
- This routine starts up the PL-050 controller.
- Arguments:
- Irp - Supplies a pointer to the I/O request packet.
- Device - Supplies a pointer to this controller device.
- Return Value:
- Status code.
- --*/
- {
- PRESOURCE_ALLOCATION Allocation;
- PRESOURCE_ALLOCATION_LIST AllocationList;
- IO_CONNECT_INTERRUPT_PARAMETERS Connect;
- PRESOURCE_ALLOCATION LineAllocation;
- BOOL RegistersFound;
- KSTATUS Status;
- RegistersFound = 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 == ResourceTypePhysicalAddressSpace) {
- if (Device->PhysicalAddress != Allocation->Allocation) {
- if (Device->RegisterBase != NULL) {
- MmUnmapAddress(Device->RegisterBase, Allocation->Length);
- Device->RegisterBase = NULL;
- }
- Device->PhysicalAddress = Allocation->Allocation;
- }
- RegistersFound = TRUE;
- if (Device->RegisterBase == NULL) {
- Device->RegisterBase = MmMapPhysicalAddress(
- Device->PhysicalAddress,
- Allocation->Length,
- TRUE,
- FALSE,
- TRUE);
- if (Device->RegisterBase == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto StartDeviceEnd;
- }
- }
- //
- // 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 the controller base wasn't found.
- //
- if (RegistersFound == FALSE) {
- Status = STATUS_INVALID_CONFIGURATION;
- goto StartDeviceEnd;
- }
- //
- // Make sure the device and its interrupts are disabled before connecting
- // the interrupt. There may be leftover state from the last reboot.
- //
- Status = Pl050pDisableDevice(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 = Pl050InterruptService;
- Connect.LowLevelServiceRoutine = Pl050InterruptServiceWorker;
- Connect.Context = Device;
- Connect.Interrupt = &(Device->InterruptHandle);
- Status = IoConnectInterrupt(&Connect);
- if (!KSUCCESS(Status)) {
- goto StartDeviceEnd;
- }
- //
- // Fire up the device.
- //
- Status = Pl050pEnableDevice(Irp->Device, Device);
- if (!KSUCCESS(Status)) {
- goto StartDeviceEnd;
- }
- StartDeviceEnd:
- if (!KSUCCESS(Status)) {
- 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
- Pl050pEnableDevice (
- PVOID OsDevice,
- PPL050_DEVICE Device
- )
- /*++
- Routine Description:
- This routine enables the given PL-050 device.
- Arguments:
- OsDevice - Supplies a pointer to the OS device token.
- Device - Supplies a pointer to this controller device.
- Return Value:
- Status code.
- --*/
- {
- UCHAR ControlByte;
- USER_INPUT_DEVICE_DESCRIPTION Description;
- KSTATUS Status;
- ControlByte = PL050_CONTROL_ENABLE;
- PL050_WRITE(Device, Pl050RegisterControl, ControlByte);
- //
- // Figure out if this is a keyboard or a mouse.
- //
- Status = Pl050pIdentifyDevice(Device, &(Device->IsMouse));
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- if (Device->IsMouse != FALSE) {
- //
- // Mice are not currently supported.
- //
- return STATUS_NOT_IMPLEMENTED;
- } else {
- //
- // Set the scan set for the keyboard.
- //
- Status = Pl050pSetScanSet(Device, 1);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- //
- // Set the typematic rate/delay on the keyboard. Start by sending the
- // command. This command overlaps with the mouse sample rate.
- //
- Status = Pl050pSendKeyboardCommand(Device,
- KEYBOARD_COMMAND_SET_TYPEMATIC,
- DEFAULT_TYPEMATIC_VALUE);
- if (!KSUCCESS(Status)) {
- goto EnableDeviceEnd;
- }
- //
- // Enable the keyboard. This overlaps with the mouse enable command.
- //
- Status = Pl050pSendKeyboardCommand(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 = Pl050pSetLedState;
- Device->UserInputDeviceHandle = InRegisterInputDevice(&Description);
- if (Device->UserInputDeviceHandle == INVALID_HANDLE) {
- Status = STATUS_UNSUCCESSFUL;
- goto EnableDeviceEnd;
- }
- }
- //
- // Enable the keyboard interrupt.
- //
- ControlByte |= PL050_CONTROL_RECEIVE_INTERRUPT_ENABLE;
- PL050_WRITE(Device, Pl050RegisterControl, ControlByte);
- }
- EnableDeviceEnd:
- return Status;
- }
- KSTATUS
- Pl050pDisableDevice (
- PPL050_DEVICE Device
- )
- /*++
- Routine Description:
- This routine disables a PL050 mouse or keyboard.
- Arguments:
- Device - Supplies a pointer to this controller device.
- Return Value:
- Status code.
- --*/
- {
- UCHAR KeyboardResult;
- UCHAR ReadStatus;
- KSTATUS ReturnStatus;
- //
- // Send the disable command and wait for one of the expected status codes.
- // The keyboard command overlaps with the mouse disable command.
- //
- WAIT_FOR_INPUT_BUFFER(Device);
- PL050_WRITE(Device, Pl050RegisterData, KEYBOARD_COMMAND_RESET_AND_DISABLE);
- while (TRUE) {
- //
- // Loop waiting for the command to be received.
- //
- while (TRUE) {
- ReadStatus = PL050_READ(Device, Pl050RegisterStatus);
- if ((ReadStatus & PL050_STATUS_RECEIVE_FULL) != 0) {
- break;
- }
- }
- //
- // Read the result. If it is not a keyboard status, just eat it and try
- // again. It's likely that there is something in the keyboard buffer.
- //
- KeyboardResult = PL050_READ(Device, Pl050RegisterData);
- if (KeyboardResult == KEYBOARD_STATUS_ACKNOWLEDGE) {
- ReturnStatus = STATUS_SUCCESS;
- break;
- }
- if (KeyboardResult == KEYBOARD_STATUS_RESEND) {
- ReturnStatus = STATUS_NOT_READY;
- break;
- }
- if (KeyboardResult == KEYBOARD_STATUS_OVERRUN) {
- ReturnStatus = STATUS_BUFFER_OVERRUN;
- break;
- }
- if (KeyboardResult == KEYBOARD_STATUS_INVALID) {
- ReturnStatus = STATUS_DEVICE_IO_ERROR;
- break;
- }
- }
- //
- // The control register is supposed to be cleared to zero on reset, but
- // just make sure in case of faulty hardware. This will disable interrupts.
- //
- PL050_WRITE(Device, Pl050RegisterControl, 0);
- return ReturnStatus;
- }
- KSTATUS
- Pl050pSetLedState (
- 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.
- --*/
- {
- UCHAR KeyboardLedState;
- PPL050_DEVICE Pl050Device;
- KSTATUS Status;
- Pl050Device = (PPL050_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 = Pl050pSendKeyboardCommand(Pl050Device,
- KEYBOARD_COMMAND_SET_LEDS,
- KeyboardLedState);
- return Status;
- }
- KSTATUS
- Pl050pSendKeyboardCommand (
- PPL050_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 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.
- --*/
- {
- ULONGLONG EndTime;
- UCHAR KeyboardResult;
- UCHAR Status;
- WAIT_FOR_INPUT_BUFFER(Device);
- PL050_WRITE(Device, Pl050RegisterData, Command);
- if (Parameter != KEYBOARD_COMMAND_NO_PARAMETER) {
- WAIT_FOR_INPUT_BUFFER(Device);
- PL050_WRITE(Device, Pl050RegisterData, Parameter);
- }
- //
- // Wait for the command to complete.
- //
- EndTime = HlQueryTimeCounter() +
- KeConvertMicrosecondsToTimeTicks(PL050_COMMAND_TIMEOUT);
- while (TRUE) {
- Status = PL050_READ(Device, Pl050RegisterStatus);
- if ((Status & PL050_STATUS_RECEIVE_FULL) != 0) {
- break;
- }
- if (HlQueryTimeCounter() >= EndTime) {
- return STATUS_TIMEOUT;
- }
- }
- //
- // Read the result.
- //
- KeyboardResult = PL050_READ(Device, Pl050RegisterData);
- 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
- Pl050pSetScanSet (
- PPL050_DEVICE Device,
- UCHAR ScanSet
- )
- /*++
- Routine Description:
- This routine sets the scan set for the keyboard.
- Arguments:
- Device - Supplies a pointer to this controller device.
- ScanSet - Supplies the scan set to get. Valid values are 1, 2, and 3.
- Return Value:
- Status code indicating whether the keyboard succeeded or failed the
- command.
- --*/
- {
- KSTATUS Status;
- Status = Pl050pSendKeyboardCommand(Device,
- KEYBOARD_COMMAND_GET_SET_SCAN_SET,
- ScanSet);
- return Status;
- }
- KSTATUS
- Pl050pIdentifyDevice (
- PPL050_DEVICE Device,
- PBOOL IsMouse
- )
- /*++
- Routine Description:
- This routine determines if the given device is a mouse or a keyboard.
- Arguments:
- Device - Supplies a pointer to this controller device.
- IsMouse - Supplies a boolean indicating if the device is a mouse.
- Return Value:
- Status code indicating whether the determination was successful or not.
- --*/
- {
- ULONGLONG EndTime;
- UCHAR Result[3];
- ULONG ResultCount;
- ULONG ResultIndex;
- KSTATUS Status;
- //
- // Disable the device to prevent keystrokes from getting in the way during
- // the determination.
- //
- Status = Pl050pDisableDevice(Device);
- if (!KSUCCESS(Status)) {
- goto IdentifyDeviceEnd;
- }
- //
- // Get the keyboard identity. This overlaps with the mouse read ID command.
- //
- WAIT_FOR_INPUT_BUFFER(Device);
- PL050_WRITE(Device, Pl050RegisterData, KEYBOARD_COMMAND_IDENTIFY);
- EndTime = HlQueryTimeCounter() +
- KeConvertMicrosecondsToTimeTicks(PL050_COMMAND_TIMEOUT);
- ResultCount = 0;
- while (ResultCount < sizeof(Result)) {
- if (IS_DATA_AVAILABLE(Device)) {
- Result[ResultCount] = PL050_READ(Device, Pl050RegisterData);
- ResultCount += 1;
- continue;
- }
- if (HlQueryTimeCounter() >= EndTime) {
- Status = STATUS_TIMEOUT;
- break;
- }
- }
- if (ResultCount == 0) {
- goto IdentifyDeviceEnd;
- }
- ResultIndex = 0;
- if (Result[ResultIndex] == KEYBOARD_STATUS_ACKNOWLEDGE) {
- ResultIndex += 1;
- }
- *IsMouse = FALSE;
- if ((ResultCount > ResultIndex) &&
- ((Result[ResultIndex] == PS2_STANDARD_MOUSE) ||
- (Result[ResultIndex] == PS2_MOUSE_WITH_SCROLL_WHEEL) ||
- (Result[ResultIndex] == PS2_FIVE_BUTTON_MOUSE))) {
- *IsMouse = TRUE;
- }
- //
- // Re-enable scanning. This overlaps with the mouse enable command.
- //
- Status = Pl050pSendKeyboardCommand(Device,
- KEYBOARD_COMMAND_ENABLE,
- KEYBOARD_COMMAND_NO_PARAMETER);
- IdentifyDeviceEnd:
- return Status;
- }
|