windows-nt/Source/XPSP1/NT/drivers/wdm/usb/driver/usbser/write.c
2020-09-26 16:20:57 +08:00

555 lines
13 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/***************************************************************************
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"));
}