⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 write.c

📁 基于USB驱动的虚拟串口程序
💻 C
📖 第 1 页 / 共 3 页
字号:
/**************** XXX

Copyright (c) 1991, 1992, 1993 - 1997 Microsoft Corporation
Copyright (c) 2000 Maverick Research Inc. Pte Ltd

Module Name:

    write.c

Abstract:

    This module contains the code that is very specific to write
    operations in the serial driver

Author:

    Anthony V. Ercolano 26-Sep-1991
	Henry Tan			18-Aug-2000

Environment:

    Kernel mode

--*/

//#include "precomp.h"
#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"
#include "usbdi.h"
#include "usbdlib.h"
#include "usbcom.h"


BOOLEAN
SerialGiveWriteToIsr(
    IN PVOID Context
    );

VOID
SerialCancelCurrentWrite(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    );

BOOLEAN
SerialGrabWriteFromIsr(
    IN PVOID Context
    );

BOOLEAN
SerialGrabXoffFromIsr(
    IN PVOID Context
    );

VOID
SerialCancelCurrentXoff(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    );

BOOLEAN
SerialGiveXoffToIsr(
    IN PVOID Context
    );
/*
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGESER,SerialProcessEmptyTransmit)
#pragma alloc_text(PAGESER,SerialWrite)
#pragma alloc_text(PAGESER,SerialStartWrite)
#pragma alloc_text(PAGESER,SerialGetNextWrite)
#pragma alloc_text(PAGESER,SerialGiveWriteToIsr)
#pragma alloc_text(PAGESER,SerialCancelCurrentWrite)
#pragma alloc_text(PAGESER,SerialGrabWriteFromIsr)
#pragma alloc_text(PAGESER,SerialGrabXoffFromIsr)
#pragma alloc_text(PAGESER,SerialCancelCurrentXoff)
#pragma alloc_text(PAGESER,SerialGiveXoffToIsr)
#endif
*/

NTSTATUS
SerialWrite(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the dispatch routine for write.  It validates the parameters
    for the write request and if all is ok then it places the request
    on the work queue.

Arguments:

    DeviceObject - Pointer to the device object for this device

    Irp - Pointer to the IRP for the current request

Return Value:

    If the io is zero length then it will return STATUS_SUCCESS,
    otherwise this routine will return STATUS_PENDING.

--*/

{

    PDEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
    NTSTATUS status;

    //SERIAL_LOCKED_PAGED_CODE();

    //SerialDump(SERTRACECALLS, ("Entering SerialWrite\n"));
    // Can't accept a new io request if:
    //  1) device is removed, 
    //  2) has never been started, 
    //  3) is stopped,
    //  4) has a remove request pending,
    //  5) has a stop device pending
    if ( !UsbCom_CanAcceptIoRequests(DeviceObject) ) {
        status = STATUS_DELETE_PENDING;
        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = 0;

        IoCompleteRequest( Irp, IO_NO_INCREMENT );

		DbgPrint("SerialWrite: Removed: %d, Started: %d, Stop: %d\n", 
			Extension->DeviceRemoved,
			Extension->DeviceStarted,
			Extension->StopDeviceRequested);

        return status;
    }

    UsbCom_IncrementIoCount(DeviceObject);
/*
    if ((status = SerialIRPPrologue(Irp, Extension)) != STATUS_SUCCESS) {
      SerialCompleteRequest(Extension, Irp, IO_NO_INCREMENT);
      SerialDump(SERTRACECALLS, ("Leaving SerialWrite (1)\n"));
      return status;
   }

    SerialDump(
        SERIRPPATH,
        ("SERIAL: Dispatch entry for: %x\n",Irp)
        );
    if (SerialCompleteIfError(
            DeviceObject,
            Irp
            ) != STATUS_SUCCESS) {

       SerialDump(SERTRACECALLS, ("Leaving SerialWrite (2)\n"));

        return STATUS_CANCELLED;

    }
*/
    Irp->IoStatus.Information = 0L;

    //
    // Quick check for a zero length write.  If it is zero length
    // then we are already done!
    //

    if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Write.Length) {

        //
        // Well it looks like we actually have to do some
        // work.  Put the write on the queue so that we can
        // process it when our previous writes are done.
        //


      // SerialDump(SERTRACECALLS, ("Leaving SerialWrite (3)\n"));

        return SerialStartOrQueue(
                   Extension,
                   Irp,
                   &Extension->WriteQueue,
                   &Extension->CurrentWriteIrp,
                   SerialStartWrite
                   );

    } else {

        Irp->IoStatus.Status = STATUS_SUCCESS;
        //SerialDump(
          //  SERIRPPATH,
            //("SERIAL: DO=%08x Complete Irp: %x\n",DeviceObject, Irp)
            //);
        SerialCompleteRequest(Extension, Irp, 0);

        //SerialDump(SERTRACECALLS, ("Leaving SerialWrite (4)\n"));
        return STATUS_SUCCESS;

    }

}

NTSTATUS
SerialStartWrite(
    IN PDEVICE_EXTENSION Extension
    )

/*++

Routine Description:

    This routine is used to start off any write.  It initializes
    the Iostatus fields of the irp.  It will set up any timers
    that are used to control the write.

Arguments:

    Extension - Points to the serial device extension

Return Value:

    This routine will return STATUS_PENDING for all writes
    other than those that we find are cancelled.

--*/

{

    PIRP NewIrp;
    KIRQL OldIrql;
    LARGE_INTEGER TotalTime;
    BOOLEAN UseATimer;
    SERIAL_TIMEOUTS Timeouts;
    BOOLEAN SetFirstStatus = FALSE;
    NTSTATUS FirstStatus;

    //SERIAL_LOCKED_PAGED_CODE();

    //SerialDump(SERTRACECALLS, ("SERIAL: SerialStartWrite\n"));

    do {

        //
        // If there is an xoff counter then complete it.
        //

        IoAcquireCancelSpinLock(&OldIrql);

        //
        // We see if there is a actually an Xoff counter irp.
        //
        // If there is, we put the write irp back on the head
        // of the write list.  We then kill the xoff counter.
        // The xoff counter killing code will actually make the
        // xoff counter back into the current write irp, and
        // in the course of completing the xoff (which is now
        // the current write) we will restart this irp.
        //

        if (Extension->CurrentXoffIrp) {

            if (SERIAL_REFERENCE_COUNT(Extension->CurrentXoffIrp)) {

                //
                // The reference count is non-zero.  This implies that
                // the xoff irp has not made it through the completion
                // path yet.  We will increment the reference count
                // and attempt to complete it ourseleves.
                //

                SERIAL_SET_REFERENCE(
                    Extension->CurrentXoffIrp,
                    SERIAL_REF_XOFF_REF
                    );

                Extension->CurrentXoffIrp->IoStatus.Information = 0;

                //
                // The following call will actually release the
                // cancel spin lock.
                //

                SerialTryToCompleteCurrent(
                    Extension,
                    SerialGrabXoffFromIsr,
                    OldIrql,
                    STATUS_SERIAL_MORE_WRITES,
                    &Extension->CurrentXoffIrp,
                    NULL,
                    NULL,
                    &Extension->XoffCountTimer,
                    NULL,
                    NULL,
                    SERIAL_REF_XOFF_REF
                    );

            } else {

                //
                // The irp is well on its way to being finished.
                // We can let the regular completion code do the
                // work.  Just release the spin lock.
                //

                IoReleaseCancelSpinLock(OldIrql);

            }

        } else {

            IoReleaseCancelSpinLock(OldIrql);

        }

        UseATimer = FALSE;

        //
        // 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.
        //

        KeAcquireSpinLock(
            &Extension->ControlLock,
            &OldIrql
            );

        Timeouts = Extension->Timeouts;

        KeReleaseSpinLock(
            &Extension->ControlLock,
            OldIrql
            );

        if (Timeouts.WriteTotalTimeoutConstant ||
            Timeouts.WriteTotalTimeoutMultiplier) {

            PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(
                                           Extension->CurrentWriteIrp
                                           );
            UseATimer = TRUE;

            //
            // We have some timer values to calculate.
            //
            // Take care, we might have an xoff counter masquerading
            // as a write.
            //

            TotalTime.QuadPart =
                ((LONGLONG)((UInt32x32To64(
                                 (IrpSp->MajorFunction == IRP_MJ_WRITE)?
                                     (IrpSp->Parameters.Write.Length):
                                     (1),
                                 Timeouts.WriteTotalTimeoutMultiplier
                                 )
                                 + Timeouts.WriteTotalTimeoutConstant)))
                * -10000;

        }

        //
        // The irp may be going to the isr shortly.  Now
        // is a good time to initialize its reference counts.
        //

        SERIAL_INIT_REFERENCE(Extension->CurrentWriteIrp);

        //
        // We need to see if this irp should be canceled.
        //

        IoAcquireCancelSpinLock(&OldIrql);
        if (Extension->CurrentWriteIrp->Cancel) {

            IoReleaseCancelSpinLock(OldIrql);
            Extension->CurrentWriteIrp->IoStatus.Status = STATUS_CANCELLED;

            if (!SetFirstStatus) {

                FirstStatus = STATUS_CANCELLED;
                SetFirstStatus = TRUE;

            }

        } else {

            if (!SetFirstStatus) {

                //
                // If we haven't set our first status, then
                // this is the only irp that could have possibly
                // not been on the queue.  (It could have been
                // on the queue if this routine is being invoked
                // from the completion routine.)  Since this
                // irp might never have been on the queue we
                // should mark it as pending.
                //

                IoMarkIrpPending(Extension->CurrentWriteIrp);
                SetFirstStatus = TRUE;
                FirstStatus = STATUS_PENDING;

            }

            //
            // We give the irp to the isr to write out.
            // We set a cancel routine that knows how to
            // grab the current write away from the isr.
            //
            // Since the cancel routine has an implicit reference
            // to this irp up the reference count.
            //

            IoSetCancelRoutine(
                Extension->CurrentWriteIrp,
                SerialCancelCurrentWrite
                );

            SERIAL_SET_REFERENCE(
                Extension->CurrentWriteIrp,
                SERIAL_REF_CANCEL
                );

            if (UseATimer) {

                SerialSetTimer(
                    &Extension->WriteRequestTotalTimer,
                    TotalTime,
                    &Extension->TotalWriteTimeoutDpc,
                    Extension
                    );

                //
                // This timer now has a reference to the irp.
                //

                SERIAL_SET_REFERENCE(
                    Extension->CurrentWriteIrp,
                    SERIAL_REF_TOTAL_TIMER
                    );
            }
/*
            KeSynchronizeExecution(
                Extension->Interrupt,
                SerialGiveWriteToIsr,
                Extension
                );
*/

			SerialGiveWriteToIsr(Extension);	//henry

            IoReleaseCancelSpinLock(OldIrql);
            break;

        }

        //
        // Well the write was canceled before we could start it up.
        // Try to get another.
        //

        SerialGetNextWrite(
            &Extension->CurrentWriteIrp,
            &Extension->WriteQueue,
            &NewIrp,
            TRUE,
            Extension
            );

    } while (NewIrp);

    return FirstStatus;

}

VOID
SerialGetNextWrite(
    IN PIRP *CurrentOpIrp,
    IN PLIST_ENTRY QueueToProcess,
    IN PIRP *NewIrp,
    IN BOOLEAN CompleteCurrent,
    PDEVICE_EXTENSION Extension
    )

/*++

Routine Description:

    This routine completes the old write as well as getting

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -