/*++ Copyright (c) 1996 Microsoft Corporation Module Name: write.c Abstract Write handling routines Author: Forrest Foltz Ervin P. Environment: Kernel mode only Revision History: --*/ #include "pch.h" /* ******************************************************************************** * HidpInterruptWriteComplete ******************************************************************************** * * */ NTSTATUS HidpInterruptWriteComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context) { PHIDCLASS_DEVICE_EXTENSION hidDeviceExtension = (PHIDCLASS_DEVICE_EXTENSION)Context; NTSTATUS status = Irp->IoStatus.Status; PHID_XFER_PACKET hidWritePacket; DBG_COMMON_ENTRY() ASSERT(hidDeviceExtension->isClientPdo); ASSERT(Irp->UserBuffer); hidWritePacket = Irp->UserBuffer; ExFreePool(hidWritePacket); Irp->UserBuffer = NULL; if (NT_SUCCESS(status)){ FDO_EXTENSION *fdoExt = &hidDeviceExtension->pdoExt.deviceFdoExt->fdoExt; PHIDP_COLLECTION_DESC collectionDesc = GetCollectionDesc(fdoExt, hidDeviceExtension->pdoExt.collectionNum); if (collectionDesc){ HidpSetDeviceBusy(fdoExt); Irp->IoStatus.Information = collectionDesc->OutputLength; } else { // // How could we get here? Had to get the collectionDesc in order // to start the write! // TRAP; } DBGVERBOSE(("HidpInterruptWriteComplete: write irp %ph succeeded, wrote %xh bytes.", Irp, Irp->IoStatus.Information)) } else { DBGWARN(("HidpInterruptWriteComplete: write irp %ph failed w/ status %xh.", Irp, status)) } /* * If the lower driver returned PENDING, mark our stack location as pending also. */ if (Irp->PendingReturned){ IoMarkIrpPending(Irp); } DBGSUCCESS(status, FALSE) DBG_COMMON_EXIT() return status; } /* ******************************************************************************** * HidpIrpMajorWrite ******************************************************************************** * * Note: This function cannot be pageable code because * writes can happen at dispatch level. * */ NTSTATUS HidpIrpMajorWrite(IN PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, IN OUT PIRP Irp) { NTSTATUS status; PDO_EXTENSION *pdoExt; FDO_EXTENSION *fdoExt; PIO_STACK_LOCATION currentIrpSp, nextIrpSp; BOOLEAN securityCheckOk = FALSE; PUCHAR buffer; PHIDP_REPORT_IDS reportIdentifier; PHIDP_COLLECTION_DESC collectionDesc; PHID_XFER_PACKET hidWritePacket; DBG_COMMON_ENTRY() ASSERT(HidDeviceExtension->isClientPdo); pdoExt = &HidDeviceExtension->pdoExt; fdoExt = &HidDeviceExtension->pdoExt.deviceFdoExt->fdoExt; currentIrpSp = IoGetCurrentIrpStackLocation(Irp); nextIrpSp = IoGetNextIrpStackLocation(Irp); if (pdoExt->state != COLLECTION_STATE_RUNNING || fdoExt->state != DEVICE_STATE_START_SUCCESS){ status = STATUS_DEVICE_NOT_CONNECTED; goto HidpIrpMajorWriteDone; } /* * Get the file extension. */ if (currentIrpSp->FileObject){ PHIDCLASS_FILE_EXTENSION fileExtension = (PHIDCLASS_FILE_EXTENSION)currentIrpSp->FileObject->FsContext; if (fileExtension) { ASSERT(fileExtension->Signature == HIDCLASS_FILE_EXTENSION_SIG); securityCheckOk = TRUE; } DBGASSERT(fileExtension, ("Attempted write with no file extension"), FALSE) } else { /* * KBDCLASS can send a NULL FileObject to set LEDs on a keyboard * (it may need to do this for a keyboard which was opened by * the raw user input thread, for which kbdclass has no fileObj). * A write with FileObject==NULL can only come from kernel space, * so we treat this as a secure write. */ securityCheckOk = TRUE; } /* * Check security. */ if (!securityCheckOk){ status = STATUS_PRIVILEGE_NOT_HELD; goto HidpIrpMajorWriteDone; } status = HidpCheckIdleState(HidDeviceExtension, Irp); if (status != STATUS_SUCCESS) { Irp = (status != STATUS_PENDING) ? Irp : (PIRP) BAD_POINTER; goto HidpIrpMajorWriteDone; } buffer = HidpGetSystemAddressForMdlSafe(Irp->MdlAddress); if (!buffer) { status = STATUS_INVALID_USER_BUFFER; goto HidpIrpMajorWriteDone; } /* * Extract the report identifier with the given id from * the HID device extension. The report id is the first * byte of the buffer. */ reportIdentifier = GetReportIdentifier(fdoExt, buffer[0]); collectionDesc = GetCollectionDesc(fdoExt, HidDeviceExtension->pdoExt.collectionNum); if (!collectionDesc || !reportIdentifier) { status = STATUS_INVALID_PARAMETER; goto HidpIrpMajorWriteDone; } if (!reportIdentifier->OutputLength){ status = STATUS_INVALID_PARAMETER; goto HidpIrpMajorWriteDone; } /* * Make sure the caller's buffer is the right size. */ if (currentIrpSp->Parameters.Write.Length != collectionDesc->OutputLength){ status = STATUS_INVALID_BUFFER_SIZE; goto HidpIrpMajorWriteDone; } /* * All parameters are correct. Allocate the write packet and * send this puppy down. */ try { hidWritePacket = ALLOCATEQUOTAPOOL(NonPagedPool, sizeof(HID_XFER_PACKET)); } except (EXCEPTION_EXECUTE_HANDLER) { hidWritePacket = NULL; status = GetExceptionCode(); } if (!hidWritePacket){ status = STATUS_INSUFFICIENT_RESOURCES; goto HidpIrpMajorWriteDone; } /* * Prepare write packet for minidriver. */ hidWritePacket->reportBuffer = buffer; hidWritePacket->reportBufferLen = reportIdentifier->OutputLength; /* * The client includes the report id as the first byte of the report. * We send down the report byte only if the device has multiple * report IDs (i.e. the report id is not implicit). */ hidWritePacket->reportId = hidWritePacket->reportBuffer[0]; if (fdoExt->deviceDesc.ReportIDs[0].ReportID == 0){ ASSERT(hidWritePacket->reportId == 0); hidWritePacket->reportBuffer++; } Irp->UserBuffer = (PVOID)hidWritePacket; /* * Prepare the next (lower) IRP stack location. * This will be HIDUSB's "current" stack location. */ nextIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextIrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_HID_WRITE_REPORT; nextIrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(HID_XFER_PACKET); IoSetCompletionRoutine( Irp, HidpInterruptWriteComplete, (PVOID)HidDeviceExtension, TRUE, TRUE, TRUE ); status = HidpCallDriver(fdoExt->fdo, Irp); /* * The Irp no longer belongs to us, and it can be * completed at any time; so don't touch it. */ Irp = (PIRP)BAD_POINTER; HidpIrpMajorWriteDone: if (ISPTR(Irp)){ Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } DBGSUCCESS(status, FALSE) DBG_COMMON_EXIT(); return status; }