555 lines
13 KiB
C
555 lines
13 KiB
C
/***************************************************************************
|
||
|
||
Copyright (c) 1998 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
WRITE.C
|
||
|
||
Abstract:
|
||
|
||
Routines that perform write functionality
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
||
PURPOSE.
|
||
|
||
Copyright (c) 1998 Microsoft Corporation. All Rights Reserved.
|
||
|
||
|
||
Revision History:
|
||
|
||
9/25/98 : created
|
||
|
||
Authors:
|
||
|
||
Louis J. Giliberto, Jr.
|
||
|
||
|
||
****************************************************************************/
|
||
|
||
|
||
#include <wdm.h>
|
||
#include <ntddser.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <usb.h>
|
||
#include <usbdrivr.h>
|
||
#include <usbdlib.h>
|
||
#include <usbcomm.h>
|
||
|
||
#ifdef WMI_SUPPORT
|
||
#include <wmilib.h>
|
||
#include <wmidata.h>
|
||
#include <wmistr.h>
|
||
#endif
|
||
|
||
#include "usbser.h"
|
||
#include "utils.h"
|
||
#include "debugwdm.h"
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGEUSBS, UsbSer_Write)
|
||
#pragma alloc_text(PAGEUSBS, UsbSerGiveWriteToUsb)
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
|
||
NTSTATUS
|
||
UsbSerFlush(IN PDEVICE_OBJECT PDevObj, PIRP PIrp)
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PDEVICE_EXTENSION pDevExt;
|
||
ULONG pendingIrps;
|
||
|
||
UsbSerSerialDump(USBSERTRACEWR, (">UsbSerFlush(%08X)\n", PIrp));
|
||
|
||
pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
|
||
|
||
//
|
||
// All we will do is wait until the write pipe has nothing pending.
|
||
// We do this by checking the outstanding count, and if it hits 1 or 0,
|
||
// then the completion routine will set an event we are waiting for.
|
||
//
|
||
|
||
InterlockedIncrement(&pDevExt->PendingDataOutCount);
|
||
|
||
pendingIrps = InterlockedDecrement(&pDevExt->PendingDataOutCount);
|
||
|
||
if ((pendingIrps) && (pendingIrps != 1)) {
|
||
//
|
||
// Wait for flush
|
||
//
|
||
|
||
KeWaitForSingleObject(&pDevExt->PendingFlushEvent, Executive,
|
||
KernelMode, FALSE, NULL);
|
||
} else {
|
||
if (pendingIrps == 0) {
|
||
//
|
||
// We need to wake others since our decrement caused the event
|
||
//
|
||
|
||
KeSetEvent(&pDevExt->PendingDataOutEvent, IO_NO_INCREMENT, FALSE);
|
||
}
|
||
}
|
||
|
||
PIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
|
||
IoCompleteRequest(PIrp, IO_NO_INCREMENT);
|
||
|
||
UsbSerSerialDump(USBSERTRACEWR, ("<UsbSerFlush %08X \n", STATUS_SUCCESS));
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
UsbSer_Write(IN PDEVICE_OBJECT PDevObj, PIRP PIrp)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Process the IRPs sent to this device for writing.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to the device object for the device written to
|
||
PIrp - Pointer to the write IRP.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
KIRQL oldIrql;
|
||
LARGE_INTEGER totalTime;
|
||
SERIAL_TIMEOUTS timeouts;
|
||
NTSTATUS status;
|
||
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
|
||
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
|
||
|
||
USBSER_ALWAYS_LOCKED_CODE();
|
||
|
||
UsbSerSerialDump(USBSERTRACEWR, (">UsbSer_Write(%08X)\n", PIrp));
|
||
|
||
PIrp->IoStatus.Information = 0L;
|
||
totalTime.QuadPart = (LONGLONG)0;
|
||
|
||
//
|
||
// Quick check for a zero length write. If it is zero length
|
||
// then we are already done!
|
||
//
|
||
|
||
if (pIrpSp->Parameters.Write.Length == 0) {
|
||
status = PIrp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoCompleteRequest(PIrp, IO_NO_INCREMENT);
|
||
goto UsbSer_WriteExit;
|
||
}
|
||
|
||
|
||
//
|
||
// Make sure the device is accepting request and then...
|
||
// Calculate the timeout value needed for the
|
||
// request. Note that the values stored in the
|
||
// timeout record are in milliseconds. Note that
|
||
// if the timeout values are zero then we won't start
|
||
// the timer.
|
||
//
|
||
|
||
ACQUIRE_SPINLOCK(pDevExt, &pDevExt->ControlLock, &oldIrql);
|
||
|
||
if (pDevExt->CurrentDevicePowerState != PowerDeviceD0) {
|
||
RELEASE_SPINLOCK(pDevExt, &pDevExt->ControlLock, oldIrql);
|
||
status = PIrp->IoStatus.Status = STATUS_UNSUCCESSFUL;
|
||
IoCompleteRequest(PIrp, IO_NO_INCREMENT);
|
||
goto UsbSer_WriteExit;
|
||
}
|
||
|
||
timeouts = pDevExt->Timeouts;
|
||
RELEASE_SPINLOCK(pDevExt, &pDevExt->ControlLock, oldIrql);
|
||
|
||
if (timeouts.WriteTotalTimeoutConstant
|
||
|| timeouts.WriteTotalTimeoutMultiplier) {
|
||
|
||
//
|
||
// We have some timer values to calculate.
|
||
//
|
||
|
||
|
||
totalTime.QuadPart
|
||
= ((LONGLONG)((UInt32x32To64((pIrpSp->MajorFunction == IRP_MJ_WRITE)
|
||
? (pIrpSp->Parameters.Write.Length)
|
||
: 1,
|
||
timeouts.WriteTotalTimeoutMultiplier)
|
||
+ timeouts.WriteTotalTimeoutConstant))) * -10000;
|
||
|
||
}
|
||
|
||
//
|
||
// The Irp may be going to the write routine shortly. Now
|
||
// is a good time to init its ref counts.
|
||
//
|
||
|
||
USBSER_INIT_REFERENCE(PIrp);
|
||
|
||
//
|
||
// We need to see if this Irp should be cancelled.
|
||
//
|
||
|
||
ACQUIRE_CANCEL_SPINLOCK(pDevExt, &oldIrql);
|
||
|
||
if (PIrp->Cancel) {
|
||
RELEASE_CANCEL_SPINLOCK(pDevExt, oldIrql);
|
||
status = PIrp->IoStatus.Status = STATUS_CANCELLED;
|
||
} else {
|
||
// IoMarkIrpPending(PIrp);
|
||
// status = STATUS_PENDING;
|
||
|
||
//
|
||
// We give the IRP to the USB subsystem -- he will need
|
||
// to know how to cancel it himself
|
||
//
|
||
|
||
IoSetCancelRoutine(PIrp, NULL);
|
||
RELEASE_CANCEL_SPINLOCK(pDevExt, oldIrql);
|
||
|
||
status = UsbSerGiveWriteToUsb(pDevExt, PIrp, totalTime);
|
||
}
|
||
|
||
UsbSer_WriteExit:;
|
||
|
||
UsbSerSerialDump(USBSERTRACEWR, ("<UsbSer_Write %08X\n", status));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
UsbSerWriteComplete(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp,
|
||
IN PUSBSER_WRITE_PACKET PPacket)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the completion routine for all write requests.
|
||
When a write completes, we go through here in order to free up
|
||
the URB.
|
||
|
||
Arguments:
|
||
|
||
PDevObj - Pointer to device object
|
||
|
||
PIrp - Irp we are completing
|
||
|
||
PUrb - Urb which will be freed
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS -- as stored in the Irp.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
|
||
KIRQL cancelIrql;
|
||
PURB pUrb = &PPacket->Urb;
|
||
PDEVICE_EXTENSION pDevExt = PPacket->DeviceExtension;
|
||
ULONG curCount;
|
||
|
||
UsbSerSerialDump(USBSERTRACEWR, (">UsbSerWriteComplete(%08X)\n", PIrp));
|
||
|
||
status = PIrp->IoStatus.Status;
|
||
|
||
if (status == STATUS_SUCCESS) {
|
||
|
||
// see if we are reusing an IOCTL IRP
|
||
if(pIrpStack->MajorFunction == IRP_MJ_DEVICE_CONTROL)
|
||
{
|
||
PIrp->IoStatus.Information = 0L;
|
||
}
|
||
else
|
||
{
|
||
PIrp->IoStatus.Information
|
||
= pUrb->UrbBulkOrInterruptTransfer.TransferBufferLength;
|
||
pIrpStack->Parameters.Write.Length = (ULONG)PIrp->IoStatus.Information;
|
||
}
|
||
|
||
} else if (status == STATUS_CANCELLED) {
|
||
//
|
||
// If it comes back as cancelled, it may really have timed out. We
|
||
// can tell by looking at the packet attached to it.
|
||
//
|
||
|
||
if (PPacket->Status) {
|
||
status = PIrp->IoStatus.Status = PPacket->Status;
|
||
UsbSerSerialDump(USBSERTRACEWR, ("Modified Write Status %08X\n",
|
||
PIrp->IoStatus.Status));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Cancel the write timer
|
||
//
|
||
|
||
if (PPacket->WriteTimeout.QuadPart != 0) {
|
||
KeCancelTimer(&PPacket->WriteTimer);
|
||
}
|
||
|
||
DEBUG_MEMFREE(PPacket);
|
||
|
||
//
|
||
// Reset the pend if necessary
|
||
//
|
||
|
||
if (PIrp->PendingReturned) {
|
||
IoMarkIrpPending(PIrp);
|
||
}
|
||
|
||
//
|
||
// See if we should mark the transmit as empty
|
||
//
|
||
|
||
if (InterlockedDecrement(&pDevExt->PendingWriteCount) == 0) {
|
||
UsbSerProcessEmptyTransmit(pDevExt);
|
||
}
|
||
|
||
//
|
||
// Notify everyone if this is the last IRP
|
||
//
|
||
|
||
curCount = InterlockedDecrement(&pDevExt->PendingDataOutCount);
|
||
|
||
if ((curCount == 0) || (curCount == 1)) {
|
||
UsbSerSerialDump(USBSERTRACEWR, ("DataOut Pipe is flushed\n"));
|
||
KeSetEvent(&pDevExt->PendingFlushEvent, IO_NO_INCREMENT, FALSE);
|
||
|
||
if (curCount == 0) {
|
||
UsbSerSerialDump(USBSERTRACEWR, ("DataOut Pipe is empty\n"));
|
||
KeSetEvent(&pDevExt->PendingDataOutEvent, IO_NO_INCREMENT, FALSE);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Finish off this IRP
|
||
//
|
||
|
||
|
||
ACQUIRE_CANCEL_SPINLOCK(pDevExt, &cancelIrql);
|
||
|
||
UsbSerTryToCompleteCurrent(pDevExt, cancelIrql, status,
|
||
&PIrp, NULL, NULL,
|
||
&pDevExt->WriteRequestTotalTimer, NULL,
|
||
NULL, USBSER_REF_RXBUFFER, FALSE);
|
||
|
||
|
||
UsbSerSerialDump(USBSERTRACEWR, ("<UsbSerWriteComplete %08X\n", status));
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
UsbSerGiveWriteToUsb(IN PDEVICE_EXTENSION PDevExt, IN PIRP PIrp,
|
||
IN LARGE_INTEGER TotalTime)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function passes a write IRP down to USB to perform the write
|
||
to the device.
|
||
|
||
Arguments:
|
||
|
||
PDevExt - Pointer to device extension
|
||
|
||
PIrp - Write IRP
|
||
|
||
TotalTime - Timeout value for total timer
|
||
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PURB pTxUrb;
|
||
PIO_STACK_LOCATION pIrpSp;
|
||
KIRQL cancelIrql;
|
||
PUSBSER_WRITE_PACKET pWrPacket;
|
||
|
||
USBSER_ALWAYS_LOCKED_CODE();
|
||
|
||
UsbSerSerialDump(USBSERTRACEWR, (">UsbSerGiveWriteToUsb(%08X)\n",
|
||
PIrp));
|
||
|
||
USBSER_SET_REFERENCE(PIrp, USBSER_REF_RXBUFFER);
|
||
|
||
|
||
pIrpSp = IoGetCurrentIrpStackLocation(PIrp);
|
||
|
||
//
|
||
// Allocate memory for URB / Write packet
|
||
//
|
||
|
||
pWrPacket = DEBUG_MEMALLOC(NonPagedPool, sizeof(USBSER_WRITE_PACKET));
|
||
|
||
if (pWrPacket == NULL) {
|
||
status = PIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
ACQUIRE_CANCEL_SPINLOCK(PDevExt, &cancelIrql);
|
||
|
||
UsbSerTryToCompleteCurrent(PDevExt, cancelIrql, status, &PIrp,
|
||
NULL,
|
||
&PDevExt->WriteRequestTotalTimer, NULL, NULL,
|
||
NULL, USBSER_REF_RXBUFFER, TRUE);
|
||
|
||
return status;
|
||
}
|
||
|
||
RtlZeroMemory(pWrPacket, sizeof(USBSER_WRITE_PACKET));
|
||
|
||
pTxUrb = &pWrPacket->Urb;
|
||
pWrPacket->DeviceExtension = PDevExt;
|
||
pWrPacket->Irp = PIrp;
|
||
pWrPacket->WriteTimeout = TotalTime;
|
||
|
||
if (TotalTime.QuadPart != 0) {
|
||
KeInitializeTimer(&pWrPacket->WriteTimer);
|
||
KeInitializeDpc(&pWrPacket->TimerDPC, UsbSerWriteTimeout, pWrPacket);
|
||
KeSetTimer(&pWrPacket->WriteTimer, TotalTime, &pWrPacket->TimerDPC);
|
||
}
|
||
|
||
//
|
||
// Build USB write request
|
||
//
|
||
|
||
BuildReadRequest(pTxUrb, PIrp->AssociatedIrp.SystemBuffer,
|
||
pIrpSp->Parameters.Write.Length, PDevExt->DataOutPipe,
|
||
FALSE);
|
||
|
||
#if DBG
|
||
if (UsbSerSerialDebugLevel & USBSERDUMPWR) {
|
||
ULONG i;
|
||
|
||
DbgPrint("WR: ");
|
||
|
||
for (i = 0; i < pIrpSp->Parameters.Write.Length; i++) {
|
||
DbgPrint("%02x ", *(((PUCHAR)PIrp->AssociatedIrp.SystemBuffer) + i) & 0xFF);
|
||
}
|
||
|
||
DbgPrint("\n\n");
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Set Irp up for a submit Urb IOCTL
|
||
//
|
||
|
||
IoCopyCurrentIrpStackLocationToNext(PIrp);
|
||
|
||
pIrpSp = IoGetNextIrpStackLocation(PIrp);
|
||
|
||
pIrpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
pIrpSp->Parameters.Others.Argument1 = pTxUrb;
|
||
pIrpSp->Parameters.DeviceIoControl.IoControlCode
|
||
= IOCTL_INTERNAL_USB_SUBMIT_URB;
|
||
|
||
IoSetCompletionRoutine(PIrp, UsbSerWriteComplete, pWrPacket, TRUE, TRUE,
|
||
TRUE);
|
||
|
||
//
|
||
// Increment the pending write count
|
||
//
|
||
|
||
InterlockedIncrement(&PDevExt->PendingWriteCount);
|
||
InterlockedIncrement(&PDevExt->PendingDataOutCount);
|
||
|
||
//
|
||
// Send IRP down
|
||
//
|
||
|
||
status = IoCallDriver(PDevExt->StackDeviceObject, PIrp);
|
||
|
||
|
||
#if 0
|
||
|
||
// this is done in the completion routine, so we don't need to do it here
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
ULONG outCount;
|
||
|
||
if (InterlockedDecrement(&PDevExt->PendingWriteCount) == 0) {
|
||
UsbSerProcessEmptyTransmit(PDevExt);
|
||
}
|
||
|
||
outCount = InterlockedDecrement(&PDevExt->PendingDataOutCount);
|
||
|
||
if ((outCount == 0) || (outCount == 1)) {
|
||
KeSetEvent(&PDevExt->PendingFlushEvent, IO_NO_INCREMENT, FALSE);
|
||
|
||
if (outCount == 0) {
|
||
KeSetEvent(&PDevExt->PendingDataOutEvent, IO_NO_INCREMENT, FALSE);
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
UsbSerSerialDump(USBSERTRACEWR, ("<UsbSerGiveWriteToUsb %08X\n", status));
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
VOID
|
||
UsbSerWriteTimeout(IN PKDPC PDpc, IN PVOID DeferredContext,
|
||
IN PVOID SystemContext1, IN PVOID SystemContext2)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called when the write timeout timer expires.
|
||
|
||
Arguments:
|
||
|
||
PDpc - Unused
|
||
|
||
DeferredContext - Actually the write packet
|
||
|
||
SystemContext1 - Unused
|
||
|
||
SystemContext2 - Unused
|
||
|
||
|
||
Return Value:
|
||
|
||
VOID
|
||
|
||
--*/
|
||
{
|
||
PUSBSER_WRITE_PACKET pPacket = (PUSBSER_WRITE_PACKET)DeferredContext;
|
||
|
||
UNREFERENCED_PARAMETER(PDpc);
|
||
UNREFERENCED_PARAMETER(SystemContext1);
|
||
UNREFERENCED_PARAMETER(SystemContext2);
|
||
|
||
UsbSerSerialDump(USBSERTRACETM, (">UsbSerWriteTimeout\n"));
|
||
|
||
if (IoCancelIrp(pPacket->Irp)) {
|
||
pPacket->Status = STATUS_TIMEOUT;
|
||
}
|
||
|
||
UsbSerSerialDump(USBSERTRACETM, ("<UsbSerWriteTimeout\n"));
|
||
}
|
||
|