1. 程式人生 > >(轉)驅動開發之五 --- TDI之六 【譯文】

(轉)驅動開發之五 --- TDI之六 【譯文】

http://hi.baidu.com/combojiang/item/d0287ca509180ddf5bf19132

同樣的使用TDI_RECEIVE來完成資料接收。然而我們卻沒有用它來實現。實際上,如果你注意到,你可以建立回撥來通知你資料或者其他事件什麼時候到達。這就是我們所做的。我實現了一個API包裝函式來建立任意的事件控制代碼。如下:

NTSTATUS TdiFuncs_SetEventHandler(PFILE_OBJECT pfoTdiFileObject,

            LONG InEventType, PVOID InEventHandler, PVOID InEventContext)

{

    NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;

    PIRP pIrp;

    IO_STATUS_BLOCK IoStatusBlock = {0};

    PDEVICE_OBJECT pTdiDevice;

    LARGE_INTEGER TimeOut = {0};

    UINT NumberOfSeconds = 60*3;

    TDI_COMPLETION_CONTEXT TdiCompletionContext;

    KeInitializeEvent(&TdiCompletionContext.kCompleteEvent,

                               NotificationEvent, FALSE);

    /*

     * The TDI Device Object is required to send these

     *                       requests to the TDI Driver.

     */

    pTdiDevice = IoGetRelatedDeviceObject(pfoTdiFileObject);

    /*

     * Step 1: Build the IRP. TDI defines several macros and functions

     *         that can quickly create IRP's, etc. for variuos purposes.  

     *         While this can be done manually it's easiest to use the macros.

     *

     */

    pIrp = TdiBuildInternalDeviceControlIrp(TDI_SET_EVENT_HANDLER,

            pTdiDevice, pfoConnection, &TdiCompletionContext.kCompleteEvent,

            &IoStatusBlock);

    if(pIrp)

    {

        /*

         * Step 2: Set the IRP Parameters

         */

        TdiBuildSetEventHandler(pIrp, pTdiDevice, pfoTdiFileObject,

                NULL, NULL, InEventType, InEventHandler, InEventContext);

        NtStatus = IoCallDriver(pTdiDevice, pIrp);

       /*

         * If the status returned is STATUS_PENDING this means that

         * the IRP will not be completed synchronously and the driver has

         * queued the IRP for later processing. This is fine but we do not

         * want to return this thread, we are a synchronous call so we want

         * to wait until it has completed. The EVENT that we provided

         * will be set when the IRP completes.

         */

        if(NtStatus == STATUS_PENDING)

        {

            KeWaitForSingleObject(&TdiCompletionContext.kCompleteEvent,

                                    Executive, KernelMode, FALSE, NULL);

            /*

             * Find the Status of the completed IRP

             */

            NtStatus = IoStatusBlock.Status;

        }

    }

    return NtStatus;

}

使用這個函式實現回撥的程式碼如下:

Collapse

NtStatus = TdiFuncs_SetEventHandler(

                  pTdiExampleContext->TdiHandle.pfoTransport,

                  TDI_EVENT_RECEIVE,

                  TdiExample_ClientEventReceive,

                  (PVOID)pTdiExampleContext);

...

NTSTATUS TdiExample_ClientEventReceive(PVOID TdiEventContext,

                         CONNECTION_CONTEXT ConnectionContext,

                         ULONG ReceiveFlags,

                         ULONG BytesIndicated,

                         ULONG BytesAvailable,

                         ULONG *BytesTaken,

                         PVOID Tsdu,

                         PIRP *IoRequestPacket)

{

    NTSTATUS NtStatus = STATUS_SUCCESS;

    UINT uiDataRead = 0;

    PTDI_EXAMPLE_CONTEXT pTdiExampleContext =

                        (PTDI_EXAMPLE_CONTEXT)TdiEventContext;

    PIRP pIrp;

    DbgPrint("TdiExample_ClientEventReceive 0x%0x, %i, %i\n",

                  ReceiveFlags, BytesIndicated, BytesAvailable);

    *BytesTaken = BytesAvailable;

    /*

     * This implementation is extremely simple. We do not queue

     * data if we do not have an IRP to put it there. We also

    * assume we always get the full data packet sent every recieve.

     * These are Bells and Whistles that can easily be added to

     * any implementation but would help to make the implementation

     * more complex and harder to follow the underlying idea. Since

     * those essentially are common-sense add ons they are ignored and

     * the general implementation of how to Queue IRP's and

     * recieve data are implemented.

     *

     */

    pIrp = HandleIrp_RemoveNextIrp(pTdiExampleContext->pReadIrpListHead);

    if(pIrp)

    {

        PIO_STACK_LOCATION pIoStackLocation =

                              IoGetCurrentIrpStackLocation(pIrp);

        uiDataRead =

               BytesAvailable > pIoStackLocation->Parameters.Read.Length ?

               pIoStackLocation->Parameters.Read.Length : BytesAvailable;

        pIrp->Tail.Overlay.DriverContext[0] = NULL;

        RtlCopyMemory(pIrp->AssociatedIrp.SystemBuffer, Tsdu, uiDataRead);

        pIrp->IoStatus.Status      = NtStatus;

        pIrp->IoStatus.Information = uiDataRead;

        IoCompleteRequest(pIrp, IO_NETWORK_INCREMENT);

    }

    /*

     * The I/O Request can be used to recieve the rest of the data.  

     * We are not using it in this example however and will actually

     * be assuming that we always get all the data.

     *

     */

    *IoRequestPacket = NULL;

    return NtStatus;

}

不要對HandleIrp_RemoveNextIrp產生恐懼,我們會在本篇後面部分講怎樣排隊IRP請求。