📄 isorwr.c
字号:
subMdl = IoAllocateMdl((PVOID) virtualAddress,
stageSize,
FALSE,
FALSE,
NULL);
if (subMdl == NULL)
{
IsoUsb_DbgPrint(1, ("failed to allocate subMdl\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto PerformFullSpeedIsochTransfer_Free;
}
subRequestContext->SubMdl = subMdl;
IoBuildPartialMdl(Irp->MdlAddress,
subMdl,
(PVOID)virtualAddress,
stageSize);
// Update loop variables for next iteration.
//
virtualAddress += stageSize;
TotalLength -= stageSize;
//
// Initialize the sub request urb.
//
RtlZeroMemory(subUrb, urbSize);
subUrb->UrbIsochronousTransfer.Hdr.Length = (USHORT)urbSize;
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;
//
// 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));
}
}
}
// Initialize the sub irp stack location
//
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_SubRequestComplete,
(PVOID)subRequestContext,
TRUE,
TRUE,
TRUE);
// Update loop variables for next iteration.
//
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 !!
//
if (!Irp->Cancel)
{
//
// normal processing
//
IsoUsb_DbgPrint(3, ("normal processing\n"));
IoMarkIrpPending(Irp);
// The cancel routine might run simultaneously as soon as it is
// set. Do not access the main request Irp in any way after
// setting the cancel routine.
//
// Note that it is still safe to access the sub requests up to
// the point where each sub request is called down the driver
// stack due to the sub request reference count which must be
// decremented by the completion routine. Do not access a sub
// request in any way after it is called down the driver stack.
//
// After setting the main request Irp cancel routine we are
// committed to calling each of the sub requests down the
// driver stack.
//
IoSetCancelRoutine(Irp, IsoUsb_CancelReadWrite);
for (i = 0; i < numIrps; i++)
{
subRequestContext = subRequestContextArray[i];
IsoUsb_DbgPrint(3, ("PerformFullSpeedIsochTransfer::"));
// Increment the device object reference before this sub
// request is called down the driver stack. The reference
// will be decremented in either the sub request completion
// routine or in the main request cancel routine, at the
// time when the sub request is freed.
//
IsoUsb_IoIncrement(deviceExtension);
IoCallDriver(deviceExtension->TopOfStackDeviceObject,
subRequestContext->SubIrp);
}
// The sub requests are freed in either the sub request
// completion routine or in the main request Irp cancel routine.
//
// Main request Irp is completed only in sub request completion
// routine.
ExFreePool(subRequestContextArray);
return STATUS_PENDING;
}
else
{
//
// The Cancel flag for the Irp has been set.
//
IsoUsb_DbgPrint(3, ("Cancel flag set\n"));
ntStatus = STATUS_CANCELLED;
}
//
// Resource allocation failure, or the main request Irp was
// cancelled before the cancel routine was set. Free any resource
// allocations and complete the main request Irp.
//
// No sub requests were ever called down the driver stack in this
// case.
//
PerformFullSpeedIsochTransfer_Free:
if (subRequestContextArray != NULL)
{
for (i = 0; i < numIrps; i++)
{
subRequestContext = subRequestContextArray[i];
if (subRequestContext != NULL)
{
if (subRequestContext->SubIrp != NULL)
{
IoFreeIrp(subRequestContext->SubIrp);
}
if (subRequestContext->SubUrb != NULL)
{
ExFreePool(subRequestContext->SubUrb);
}
if (subRequestContext->SubMdl != NULL)
{
IoFreeMdl(subRequestContext->SubMdl);
}
ExFreePool(subRequestContext);
}
}
ExFreePool(subRequestContextArray);
}
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
PerformHighSpeedIsochTransfer(
IN PDEVICE_OBJECT DeviceObject,
IN PUSBD_PIPE_INFORMATION PipeInformation,
IN PIRP Irp,
IN ULONG TotalLength
)
/*++
Routine Description:
High Speed Isoch Transfer requires packets in multiples of 8.
(Argument: 8 micro-frames per ms frame)
Another restriction is that each Irp/Urb pair can be associated
with a max of 1024 packets.
Here is one of the ways of creating Irp/Urb pairs.
Depending on the characteristics of real-world device,
the algorithm may be different
This algorithm will distribute data evenly among all the packets.
Input:
TotalLength - no. of bytes to be transferred.
Other parameters:
packetSize - max size of each packet for this pipe.
Implementation Details:
Step 1:
ASSERT(TotalLength >= 8)
Step 2:
Find the exact number of packets required to transfer all of this data
numberOfPackets = (TotalLength + packetSize - 1) / packetSize
Step 3:
Number of packets in multiples of 8.
if(0 == (numberOfPackets % 8)) {
actualPackets = numberOfPackets;
}
else {
actualPackets = numberOfPackets +
(8 - (numberOfPackets % 8));
}
Step 4:
Determine the min. data in each packet.
minDataInEachPacket = TotalLength / actualPackets;
Step 5:
After placing min data in each packet,
determine how much data is left to be distributed.
dataLeftToBeDistributed = TotalLength -
(minDataInEachPacket * actualPackets);
Step 6:
Start placing the left over data in the packets
(above the min data already placed)
numberOfPacketsFilledToBrim = dataLeftToBeDistributed /
(packetSize - minDataInEachPacket);
Step 7:
determine if there is any more data left.
dataLeftToBeDistributed -= (numberOfPacketsFilledToBrim *
(packetSize - minDataInEachPacket));
Step 8:
The "dataLeftToBeDistributed" is placed in the packet at index
"numberOfPacketsFilledToBrim"
Algorithm at play:
TotalLength = 8193
packetSize = 8
Step 1
Step 2
numberOfPackets = (8193 + 8 - 1) / 8 = 1025
Step 3
actualPackets = 1025 + 7 = 1032
Step 4
minDataInEachPacket = 8193 / 1032 = 7 bytes
Step 5
dataLeftToBeDistributed = 8193 - (7 * 1032) = 969.
Step 6
numberOfPacketsFilledToBrim = 969 / (8 - 7) = 969.
Step 7
dataLeftToBeDistributed = 969 - (969 * 1) = 0.
Step 8
Done :)
Another algorithm
Completely fill up (as far as possible) the early packets.
Place 1 byte each in the rest of them.
Ensure that the total number of packets is multiple of 8.
This routine the does the following:
1. It creates a SUB_REQUEST_CONTEXT for each irp/urb pair and
attaches it to the main request irp.
2. It intializes all of the sub request irp/urb pairs, and sub mdls
too.
3. It passes down the driver stack all of the sub request irps.
4. It leaves the completion of the main request irp as the
responsibility of the sub request irp completion routine, except
in the exception case where the main request irp is canceled
prior to passing any of the the sub request irps down the driver
stack.
Arguments:
DeviceObject - pointer to device object
Irp - I/O request packet
Return Value:
NT status value
--*/
{
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
BOOLEAN read;
ULONG packetSize;
ULONG numberOfPackets;
ULONG actualPackets;
ULONG minDataInEachPacket;
ULONG numberOfPacketsFilledToBrim;
ULONG dataLeftToBeDistributed;
ULONG numIrps;
PMAIN_REQUEST_CONTEXT mainRequestContext;
PSUB_REQUEST_CONTEXT * subRequestContextArray;
PSUB_REQUEST_CONTEXT subRequestContext;
PLIST_ENTRY subRequestEntry;
CCHAR stackSize;
PUCHAR virtualAddress;
ULONG i;
ULONG j;
KIRQL oldIrql;
NTSTATUS ntStatus;
PIO_STACK_LOCATION nextStack;
//
// initialize vars
//
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
irpStack = IoGetCurrentIrpStackLocation(Irp);
read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
if (TotalLength < 8)
{
ntStatus = STATUS_INVALID_PARAMETER;
goto PerformHighSpeedIsochTransfer_Exit;
}
//
// each packet can hold this much info
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -