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

📄 isorwr.c

📁 WINDDK开发代码
💻 C
📖 第 1 页 / 共 4 页
字号:
    //
    packetSize = PipeInformation->MaximumPacketSize;

    IsoUsb_DbgPrint(3, ("totalLength = %d\n", TotalLength));
    IsoUsb_DbgPrint(3, ("packetSize = %d\n", packetSize));

    //
    // there is an inherent limit on the number of packets
    // that can be passed down the stack with each 
    // irp/urb pair (255)
    // if the number of required packets is > 255,
    // we shall create "required-packets / 255 + 1" number 
    // of irp/urb pairs. 
    // Each irp/urb pair transfer is also called a stage transfer.
    //
    if(TotalLength > (packetSize * 255)) {

        stageSize = packetSize * 255;
    }
    else {

        stageSize = TotalLength;
    }

    IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::stageSize = %d\n", stageSize));

    //
    // determine how many stages of transfer needs to be done.
    // in other words, how many irp/urb pairs required. 
    // this irp/urb pair is also called the subsidiary irp/urb pair
    //
    numIrps = (TotalLength + stageSize - 1) / stageSize;

    IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::numIrps = %d\n", numIrps));

    //
    // for every read/write transfer
    // we create an ISOUSB_RW_CONTEXT
    //
    // initialize the read/write context
    //
    
    contextSize = sizeof(ISOUSB_RW_CONTEXT);

    rwContext = (PISOUSB_RW_CONTEXT) ExAllocatePool(NonPagedPool,
                                                    contextSize);

    if(rwContext == NULL) {

        IsoUsb_DbgPrint(1, ("Failed to alloc mem for rwContext\n"));

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto PerformFullSpeedIsochTransfer_Exit;
    }

    RtlZeroMemory(rwContext, contextSize);

    //
    // allocate memory for every stage context - 
    // subcontext has state information for every irp/urb pair.
    //
    rwContext->SubContext = (PSUB_CONTEXT) 
                            ExAllocatePool(NonPagedPool, 
                                           numIrps * sizeof(SUB_CONTEXT));

    if(rwContext->SubContext == NULL) {

        IsoUsb_DbgPrint(1, ("Failed to alloc mem for SubContext\n"));

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        ExFreePool(rwContext);
        goto PerformFullSpeedIsochTransfer_Exit;
    }

    RtlZeroMemory(rwContext->SubContext, numIrps * sizeof(SUB_CONTEXT));

    rwContext->RWIrp = Irp;
    rwContext->Lock = 2;
    rwContext->NumIrps = numIrps;
    rwContext->IrpsPending = numIrps;
    rwContext->DeviceExtension = deviceExtension;
    KeInitializeSpinLock(&rwContext->SpinLock);
    //
    // save the rwContext pointer in the tail union.
    //
    Irp->Tail.Overlay.DriverContext[0] = (PVOID) rwContext;

    stackSize = deviceExtension->TopOfStackDeviceObject->StackSize + 1;
    virtualAddress = (PUCHAR) MmGetMdlVirtualAddress(Irp->MdlAddress);

    for(i = 0; i < numIrps; i++) {
    
        PIRP  subIrp;
        PURB  subUrb;
        PMDL  subMdl;
        ULONG nPackets;
        ULONG siz;
        ULONG offset;

        //
        // for every stage of transfer we need to do the following
        // tasks
        // 1. allocate an irp
        // 2. allocate an urb
        // 3. allocate a mdl.
        //
        // create a subsidiary irp
        //
        subIrp = IoAllocateIrp(stackSize, FALSE);

        if(subIrp == NULL) {

            IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context irp\n"));

            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto PerformFullSpeedIsochTransfer_Free;
        }

        rwContext->SubContext[i].SubIrp = subIrp;

        nPackets = (stageSize + packetSize - 1) / packetSize;

        IsoUsb_DbgPrint(3, ("nPackets = %d for Irp/URB pair %d\n", nPackets, i));

        ASSERT(nPackets <= 255);

        siz = GET_ISO_URB_SIZE(nPackets);

        //
        // create a subsidiary urb.
        //

        subUrb = (PURB) ExAllocatePool(NonPagedPool, siz);

        if(subUrb == NULL) {

            IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context urb\n"));

            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto PerformFullSpeedIsochTransfer_Free;
        }

        rwContext->SubContext[i].SubUrb = subUrb;

        //
        // allocate a mdl.
        //
        subMdl = IoAllocateMdl((PVOID) virtualAddress, 
                            stageSize,
                            FALSE,
                            FALSE,
                            NULL);

        if(subMdl == NULL) {

            IsoUsb_DbgPrint(1, ("failed to alloc mem for sub context mdl\n"));

            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto PerformFullSpeedIsochTransfer_Free;
        }

        IoBuildPartialMdl(Irp->MdlAddress,
                          subMdl,
                          (PVOID) virtualAddress,
                          stageSize);

        rwContext->SubContext[i].SubMdl = subMdl;

        virtualAddress += stageSize;
        TotalLength -= stageSize;

        //
        // Initialize the subsidiary urb
        //
        RtlZeroMemory(subUrb, siz);

        subUrb->UrbIsochronousTransfer.Hdr.Length = (USHORT) siz;
        subUrb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER;
        subUrb->UrbIsochronousTransfer.PipeHandle = PipeInformation->PipeHandle;
        if(read) {

            IsoUsb_DbgPrint(3, ("read\n"));
            subUrb->UrbIsochronousTransfer.TransferFlags = 
                                                     USBD_TRANSFER_DIRECTION_IN;
        }
        else {

            IsoUsb_DbgPrint(3, ("write\n"));
            subUrb->UrbIsochronousTransfer.TransferFlags =
                                                     USBD_TRANSFER_DIRECTION_OUT;
        }

        subUrb->UrbIsochronousTransfer.TransferBufferLength = stageSize;
        subUrb->UrbIsochronousTransfer.TransferBufferMDL = subMdl;

/*
        This is a way to set the start frame and NOT specify ASAP flag.

        subUrb->UrbIsochronousTransfer.StartFrame = 
                        IsoUsb_GetCurrentFrame(DeviceObject, Irp) + 
                        SOME_LATENCY;
*/
        // 
        // when the client driver sets the ASAP flag, it basically
        // guarantees that it will make data available to the HC
        // and that the HC should transfer it in the next transfer frame 
        // for the endpoint.(The HC maintains a next transfer frame
        // state variable for each endpoint). By resetting the pipe,
        // we make the pipe as virgin. If the data does not get to the HC
        // fast enough, the USBD_ISO_PACKET_DESCRIPTOR - Status is 
        // USBD_STATUS_BAD_START_FRAME on uhci. On ohci it is 0xC000000E.
        //

        subUrb->UrbIsochronousTransfer.TransferFlags |=
                                    USBD_START_ISO_TRANSFER_ASAP;

        subUrb->UrbIsochronousTransfer.NumberOfPackets = nPackets;
        subUrb->UrbIsochronousTransfer.UrbLink = NULL;

        //
        // set the offsets for every packet for reads/writes
        //
        if(read) {
            
            offset = 0;

            for(j = 0; j < nPackets; j++) {
            
                subUrb->UrbIsochronousTransfer.IsoPacket[j].Offset = offset;
                subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = 0;

                if(stageSize > packetSize) {

                    offset += packetSize;
                    stageSize -= packetSize;
                }
                else {

                    offset += stageSize;
                    stageSize = 0;
                }
            }
        }
        else {

            offset = 0;

            for(j = 0; j < nPackets; j++) {

                subUrb->UrbIsochronousTransfer.IsoPacket[j].Offset = offset;

                if(stageSize > packetSize) {

                    subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = packetSize;
                    offset += packetSize;
                    stageSize -= packetSize;
                }
                else {

                    subUrb->UrbIsochronousTransfer.IsoPacket[j].Length = stageSize;
                    offset += stageSize;
                    stageSize = 0;
                    ASSERT(offset == (subUrb->UrbIsochronousTransfer.IsoPacket[j].Length + 
                                      subUrb->UrbIsochronousTransfer.IsoPacket[j].Offset));
                }
            }
        }

        IoSetNextIrpStackLocation(subIrp);
        nextStack = IoGetCurrentIrpStackLocation(subIrp);

        nextStack->DeviceObject = DeviceObject;
        nextStack->Parameters.Others.Argument1 = (PVOID) subUrb;
        nextStack->Parameters.Others.Argument2 = (PVOID) subMdl;

        nextStack = IoGetNextIrpStackLocation(subIrp);
        nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
        nextStack->Parameters.Others.Argument1 = (PVOID) subUrb;
        nextStack->Parameters.DeviceIoControl.IoControlCode = 
                                             IOCTL_INTERNAL_USB_SUBMIT_URB;

        IoSetCompletionRoutine(subIrp,
                               (PIO_COMPLETION_ROUTINE) IsoUsb_SinglePairComplete,
                               (PVOID) rwContext,
                               TRUE,
                               TRUE,
                               TRUE);

        if(TotalLength > (packetSize * 255)) {

            stageSize = packetSize * 255;
        }
        else {

            stageSize = TotalLength;
        }
    }

    //
    // while we were busy create subsidiary irp/urb pairs..
    // the main read/write irp may have been cancelled !!
    //

    KeAcquireSpinLock(&rwContext->SpinLock, &oldIrql);

    IoSetCancelRoutine(Irp, IsoUsb_CancelReadWrite);

    if(Irp->Cancel) {

        //
        // The Cancel flag for the Irp has been set. 
        //
        IsoUsb_DbgPrint(3, ("Cancel flag set\n"));

        ntStatus = STATUS_CANCELLED;

        if(IoSetCancelRoutine(Irp, NULL)) {

            //
            // But the I/O manager did not call our cancel routine.
            // we need to free the 1) irp, 2) urb and 3) mdl for every 
            // stage and complete the main Irp after releasing the lock
            //

            IsoUsb_DbgPrint(3, ("cancellation routine NOT run\n"));

            KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);

            goto PerformFullSpeedIsochTransfer_Free;
        }
        else {
            
            //
            // The cancel routine will resume the moment we release the lock.
            //
            for(j = 0; j < numIrps; j++) {

                if(rwContext->SubContext[j].SubUrb) {

                    ExFreePool(rwContext->SubContext[j].SubUrb);
                    rwContext->SubContext[j].SubUrb = NULL;
                }

                if(rwContext->SubContext[j].SubMdl) {

                    IoFreeMdl(rwContext->SubContext[j].SubMdl);
                    rwContext->SubContext[j].SubMdl = NULL;
                }
            }

            IoMarkIrpPending(Irp);

            //
            // it is the job of the cancellation routine to free
            // sub-context irps, release rwContext and complete 
            // the main readwrite irp
            //
            InterlockedDecrement(&rwContext->Lock);

            KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);

            return STATUS_PENDING;
        }
    }
    else {

        //
        // normal processing
        //

        IsoUsb_DbgPrint(3, ("normal processing\n"));

        IoMarkIrpPending(Irp);

        KeReleaseSpinLock(&rwContext->SpinLock, oldIrql);

        for(j = 0; j < numIrps; j++) {

            IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::"));
            IsoUsb_IoIncrement(deviceExtension);
            
            IoCallDriver(deviceExtension->TopOfStackDeviceObject,
                         rwContext->SubContext[j].SubIrp);
        }
        return STATUS_PENDING;
    }

PerformFullSpeedIsochTransfer_Free:

    for(j = 0; j < numIrps; j++) {

        if(rwContext->SubContext[j].SubIrp) {

            IoFreeIrp(rwContext->SubContext[j].SubIrp);
            rwContext->SubContext[j].SubIrp = NULL;
        }

        if(rwContext->SubContext[j].SubUrb) {

            ExFreePool(rwContext->SubContext[j].SubUrb);
            rwContext->SubContext[j].SubUrb = NULL;
        }

        if(rwContext->SubContext[j].SubMdl) {

            IoFreeMdl(rwContext->SubContext[j].SubMdl);
            rwContext->SubContext[j].SubMdl = NULL;
        }
    }

    ExFreePool(rwContext->SubContext);
    ExFreePool(rwContext);


PerformFullSpeedIsochTransfer_Exit:

    Irp->IoStatus.Status = ntStatus;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::"));
    IsoUsb_IoDecrement(deviceExtension);

    IsoUsb_DbgPrint(3, ("-------------------------------\n"));

    return ntStatus;
}

NTSTATUS
IsoUsb_SinglePairComplete(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp,
    IN PVOID          Context
    )
/*++
 
Routine Description:

    This is the completion routine for the subsidiary irp.

    For every irp/urb pair, we have allocated
    1. an irp
    2. an urb
    3. a mdl.

    Case 1:
    we do NOT free the irp on its completion
    we do free the urb and the mdl.

    Case 1 is executed in Block 3.

    Case 2:
    when we complete the last of the subsidiary irp,
    we check if the cancel routine for the main Irp
    has run. If not, we free all the irps, release
    the subcontext and the context and complete the
    main Irp.we also free the urb and mdl for this
    stage.

    Case 2 is executed in Block 2.

    Case 3:
    when we complete the last of the subsidiary irp,
    we check if the cancel routine for the main Irp
    has run. If yes, we atomically decrement the
    rwContext->Lock field. (the completion routine
    is in race with Cancel routine). If the count is 1, 

⌨️ 快捷键说明

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