|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
hid.c
Abstract: Human Input Device (HID) minidriver for Infrared (IR) devices
The HID IR Minidriver (HidIr) provides an abstraction layer for the HID Class to talk to HID IR devices.
Author: jsenior
Environment:
Kernel mode
Revision History:
--*/ #include "pch.h"
PVOID HidIrGetSystemAddressForMdlSafe(PMDL MdlAddress) { PVOID buf = NULL; /*
* Can't call MmGetSystemAddressForMdlSafe in a WDM driver, * so set the MDL_MAPPING_CAN_FAIL bit and check the result * of the mapping. */ if (MdlAddress) { MdlAddress->MdlFlags |= MDL_MAPPING_CAN_FAIL; buf = MmGetSystemAddressForMdl(MdlAddress); MdlAddress->MdlFlags &= ~(MDL_MAPPING_CAN_FAIL); } return buf; }
/*
******************************************************************************** * HidIrGetHidDescriptor ******************************************************************************** * * Routine Description: * * Return the hid descriptor of the requested type. This ioctl can only * be sent from the HidClass driver. The hidclass driver always sends the * irp with a userbuffer, so there is no need to check for its existence. * But better safe then sorry... * * Arguments: * * DeviceObject - pointer to a device object. * * Return Value: * * NT status code. * */ NTSTATUS HidIrGetHidDescriptor( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, USHORT DescriptorType ) { PHIDIR_EXTENSION devExt; PIO_STACK_LOCATION irpStack; ULONG descLength = 0, bytesToCopy; PUCHAR descriptor = NULL;
PAGED_CODE();
HidIrKdPrint((3, "HidIrGetHidDescriptor type %x", DescriptorType));
devExt = GET_MINIDRIVER_HIDIR_EXTENSION(DeviceObject); irpStack = IoGetCurrentIrpStackLocation(Irp); bytesToCopy = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
switch (DescriptorType) { case HID_HID_DESCRIPTOR_TYPE: descLength = devExt->HidDescriptor.bLength; descriptor = (PUCHAR)&devExt->HidDescriptor; break; case HID_REPORT_DESCRIPTOR_TYPE: descLength = devExt->HidDescriptor.DescriptorList[0].wDescriptorLength; descriptor = devExt->ReportDescriptor; break; case HID_PHYSICAL_DESCRIPTOR_TYPE: // Not handled
break; default: HIR_TRAP(); }
if (descLength == 0 || descriptor == NULL) { return STATUS_UNSUCCESSFUL; } if (bytesToCopy > descLength) { bytesToCopy = descLength; }
if (Irp->UserBuffer) { RtlCopyMemory((PUCHAR)Irp->UserBuffer, descriptor, bytesToCopy); Irp->IoStatus.Information = bytesToCopy; } else { HIR_TRAP(); return STATUS_INVALID_USER_BUFFER; }
return STATUS_SUCCESS; }
/*
******************************************************************************** * HidIrGetDeviceAttributes ******************************************************************************** * * Routine Description: * * Fill in the given struct _HID_DEVICE_ATTRIBUTES. This ioctl can only * be sent from the HidClass driver. The hidclass driver always sends the * irp with a userbuffer, so there is no need to check for its existence. * But better safe then sorry... * * Arguments: * * DeviceObject - pointer to a device object. * * Return Value: * * NT status code. * */ NTSTATUS HidIrGetDeviceAttributes( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS ntStatus;
PAGED_CODE();
HidIrKdPrint((3, "HidIrGetDeviceAttributes Enter"));
if (Irp->UserBuffer) {
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); PHID_DEVICE_ATTRIBUTES deviceAttributes = (PHID_DEVICE_ATTRIBUTES) Irp->UserBuffer;
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof (HID_DEVICE_ATTRIBUTES)){
PHIDIR_EXTENSION devExt = GET_MINIDRIVER_HIDIR_EXTENSION(DeviceObject);
//
// Report how many bytes were copied
//
Irp->IoStatus.Information = sizeof (HID_DEVICE_ATTRIBUTES);
deviceAttributes->Size = sizeof (HID_DEVICE_ATTRIBUTES); // TODO: Get these values from the bth stack.
deviceAttributes->VendorID = devExt->VendorID; deviceAttributes->ProductID = devExt->ProductID; deviceAttributes->VersionNumber = devExt->VersionNumber; ntStatus = STATUS_SUCCESS; } else { ntStatus = STATUS_INVALID_BUFFER_SIZE; } } else { HIR_TRAP(); ntStatus = STATUS_INVALID_USER_BUFFER; } ASSERT(NT_SUCCESS(ntStatus)); return ntStatus; }
/*
******************************************************************************** * HidIrIncrementPendingRequestCount ******************************************************************************** * * */ NTSTATUS HidIrIncrementPendingRequestCount(IN PHIDIR_EXTENSION DevExt) { LONG newRequestCount; NTSTATUS ntStatus = STATUS_SUCCESS;
newRequestCount = InterlockedIncrement(&DevExt->NumPendingRequests);
HidIrKdPrint((3, "Increment Pending Request Count to %x", newRequestCount));
// Make sure that the device is capable of receiving new requests.
if ((DevExt->DeviceState != DEVICE_STATE_RUNNING) && (DevExt->DeviceState != DEVICE_STATE_STARTING)){
HIR_TRAP();
// Device cannot receive any more IOs, decrement back, fail the increment
HidIrDecrementPendingRequestCount(DevExt); ntStatus = STATUS_NO_SUCH_DEVICE; }
return ntStatus; }
/*
******************************************************************************** * HidIrDecrementPendingRequestCount ******************************************************************************** * * */ VOID HidIrDecrementPendingRequestCount(IN PHIDIR_EXTENSION DevExt) { LONG PendingCount;
ASSERT(DevExt->NumPendingRequests >= 0);
PendingCount = InterlockedDecrement(&DevExt->NumPendingRequests);
HidIrKdPrint((3, "Decrement Pending Request Count to %x", PendingCount));
if (PendingCount < 0){
ASSERT(DevExt->DeviceState != DEVICE_STATE_RUNNING);
/*
* The device state is stopping, and the last outstanding request * has just completed. * * Note: RemoveDevice does an extra decrement, so we complete * the REMOVE IRP on the transition to -1, whether this * happens in RemoveDevice itself or subsequently while * RemoveDevice is waiting for this event to fire. */
KeSetEvent(&DevExt->AllRequestsCompleteEvent, 0, FALSE); } }
/*
******************************************************************************** * HidIrReadCompletion ******************************************************************************** * * */ NTSTATUS HidIrReadCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PHIDIR_EXTENSION DevExt ) { NTSTATUS ntStatus = Irp->IoStatus.Status; ULONG bytesRead; PUCHAR buffer; BOOLEAN resend = FALSE; PHIDIR_EXTENSION devExt;
devExt = GET_MINIDRIVER_HIDIR_EXTENSION(DeviceObject);
HidIrKdPrint((3, "HidIrReadCompletion status %x", ntStatus));
ASSERT(Irp->MdlAddress); buffer = HidIrGetSystemAddressForMdlSafe(Irp->MdlAddress);
if(!buffer) { // If this fails, we really should bugcheck, since someone
// in the kernel screwed up our MDL on us. I'll fail safely, but
// definitely trap on debug builds.
HIR_TRAP(); Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; } else if (NT_SUCCESS(ntStatus)){ // Get the bytes read from the status block
bytesRead = (ULONG)Irp->IoStatus.Information;
// Predispose to zero
Irp->IoStatus.Information = 0; if (bytesRead == sizeof(ULONG)) { ULONG value, i;
RtlCopyMemory(&value, buffer, sizeof(ULONG)); if (value == 0) { // Key up. Do we have a pending key down?
if (devExt->PreviousButton.UsageString[0]) { // We have a pending key down. Send key up.
PUCHAR destination; // Send the ~usage.
HidIrKdPrint((2,"Sending ~usage")); Irp->IoStatus.Information = devExt->ReportLength; destination = (PUCHAR) Irp->UserBuffer; RtlZeroMemory(Irp->UserBuffer, Irp->IoStatus.Information); // already checked that buffer is big enuf.
destination[0] = devExt->PreviousButton.UsageString[0]; // report ID
RtlZeroMemory(&devExt->PreviousButton, sizeof(devExt->PreviousButton)); } else { // No pending key down message for this key up. Fire it back.
resend = TRUE; } } else if (value == devExt->PreviousButton.IRString) { // Same thing as last time. Fire it back down.
resend = TRUE; } else { // Something new. Hmmm...
ULONG entrySize = HIDIR_TABLE_ENTRY_SIZE(devExt->ReportLength); PUSAGE_TABLE_ENTRY entry; // Predispose to bounce the irp back down if we don't find a match.
resend = TRUE; for (i = 0; i < devExt->NumUsages; i++) { entry = (PUSAGE_TABLE_ENTRY) (devExt->MappingTable+(entrySize*i));
if (entry->IRString == value) { HidIrKdPrint((2,"Found usage %x!", value));
// New usage. Copy it and complete the irp.
Irp->IoStatus.Information = devExt->ReportLength;
RtlCopyMemory(Irp->UserBuffer, entry->UsageString, devExt->ReportLength); RtlCopyMemory(&devExt->PreviousButton, entry, sizeof(devExt->PreviousButton)); // Check if we are allowed to send up standby button presses yet.
if (KeReadStateTimer(&devExt->IgnoreStandbyTimer) || !devExt->StandbyReportIdValid || devExt->StandbyReportId != entry->UsageString[0]) { resend = FALSE; } break; } } if (resend) { // This might be an OEM button. Check if it's within the approved range.
if (value >= 0x800F0400 && value <= 0x800F04FF) {
PUCHAR usageString = Irp->UserBuffer; UCHAR oemValue = (UCHAR) (value & 0xFF);
// It's in the range!
HidIrKdPrint((2,"OEM button %x", value)); RtlZeroMemory(usageString, devExt->ReportLength); // Check if this is the "flag" button. If so, and we are not running
// media center, we want to eject the windows key instead.
if (oemValue == 0x0D && !RunningMediaCenter && devExt->KeyboardReportIdValid) { HidIrKdPrint((2,"Change flag button to Windows key")); usageString[0] = devExt->KeyboardReportId; usageString[1] = 0x8; Irp->IoStatus.Information = devExt->ReportLength; } else { usageString[0] = 0x1; usageString[1] = oemValue; Irp->IoStatus.Information = 2; } devExt->PreviousButton.IRString = value; devExt->PreviousButton.UsageString[0] = usageString[0]; resend = FALSE; } } }
HidIrKdPrint((3, "HidIrReadCompletion buffer value 0x%x", value));
} else { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; }
} else if (ntStatus == STATUS_CANCELLED){ /*
* The IRP was cancelled, which means that the device is probably getting removed. */ HidIrKdPrint((1, "Read irp %p cancelled ...", Irp)); ASSERT(!Irp->CancelRoutine); }
// Balance the increment we did when we issued the read.
HidIrDecrementPendingRequestCount(DevExt);
//
// Don't need the MDL and buffer anymore.
//
if (Irp->MdlAddress) { IoFreeMdl(Irp->MdlAddress); Irp->MdlAddress = NULL; if (buffer) { ExFreePool(buffer); } }
// If we didn't get anything useful back, just poke it back down
// to the hardware.
if (resend) { BOOLEAN needsCompletion = TRUE; ntStatus = HidIrReadReport(DeviceObject, Irp, &needsCompletion); if (!needsCompletion) { return STATUS_MORE_PROCESSING_REQUIRED; } Irp->IoStatus.Status = ntStatus; // fall thru and irp will complete.
} /*
* If the lower driver returned PENDING, mark our stack location as * pending also. This prevents the IRP's thread from being freed if * the client's call returns pending. */ if (Irp->PendingReturned){ IoMarkIrpPending(Irp); }
return STATUS_SUCCESS; // something other than SMPR
}
/*
******************************************************************************** * HidIrReadReport ******************************************************************************** * * Routine Description: * * * Arguments: * * DeviceObject - Pointer to class device object. * * IrpStack - Pointer to Interrupt Request Packet. * * * Return Value: * * STATUS_SUCCESS, STATUS_UNSUCCESSFUL. * * * Note: this function cannot be pageable because reads/writes * can be made at dispatch-level. */ NTSTATUS HidIrReadReport( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN OUT BOOLEAN *NeedsCompletion) { NTSTATUS status = STATUS_UNSUCCESSFUL; PHIDIR_EXTENSION devExt; PUCHAR buffer; ULONG bufferLen; PIO_STACK_LOCATION irpStack;
HidIrKdPrint((3, "HidIrReadReport Enter"));
devExt = GET_MINIDRIVER_HIDIR_EXTENSION(DeviceObject);
ASSERT(Irp->UserBuffer);
irpStack = IoGetCurrentIrpStackLocation(Irp);
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength < devExt->ReportLength) { return STATUS_INVALID_BUFFER_SIZE; }
bufferLen = HIDIR_REPORT_SIZE; buffer = ALLOCATEPOOL(NonPagedPool, bufferLen); if (buffer) { ASSERT(!Irp->MdlAddress); if (IoAllocateMdl(buffer, bufferLen, FALSE, FALSE, Irp)) { MmBuildMdlForNonPagedPool(Irp->MdlAddress);
irpStack = IoGetNextIrpStackLocation(Irp);
irpStack->MajorFunction = IRP_MJ_READ; irpStack->DeviceObject = GET_NEXT_DEVICE_OBJECT(DeviceObject); irpStack->Parameters.Read.Length = bufferLen;
IoSetCompletionRoutine( Irp, HidIrReadCompletion, devExt, TRUE, TRUE, TRUE );
//
// We need to keep track of the number of pending requests
// so that we can make sure they're all cancelled properly during
// processing of a stop device request.
//
if (NT_SUCCESS(HidIrIncrementPendingRequestCount(devExt))){ status = IoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp); *NeedsCompletion = FALSE; } else { IoFreeMdl(Irp->MdlAddress); Irp->MdlAddress = NULL; ExFreePool(buffer); status = STATUS_NO_SUCH_DEVICE; } } else { ExFreePool(buffer); status = STATUS_INSUFFICIENT_RESOURCES; } } else { status = STATUS_INSUFFICIENT_RESOURCES; }
return status; }
|