📄 isorwr.c
字号:
/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
isorwr.c
Abstract:
This file has dispatch routines for read and write.
Environment:
Kernel mode
Notes:
Copyright (c) 2000 Microsoft Corporation.
All Rights Reserved.
--*/
#include "isousb.h"
#include "isopnp.h"
#include "isopwr.h"
#include "isodev.h"
#include "isowmi.h"
#include "isousr.h"
#include "isorwr.h"
#include "isostrm.h"
NTSTATUS
IsoUsb_DispatchReadWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine does some validation and
invokes appropriate function to perform
Isoch transfer
Arguments:
DeviceObject - pointer to device object
Irp - I/O request packet
Return Value:
NT status value
--*/
{
ULONG totalLength;
ULONG packetSize;
NTSTATUS ntStatus;
PFILE_OBJECT fileObject;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
PFILE_OBJECT_CONTENT fileObjectContent;
PUSBD_PIPE_INFORMATION pipeInformation;
//
// initialize vars
//
irpStack = IoGetCurrentIrpStackLocation(Irp);
fileObject = irpStack->FileObject;
totalLength = 0;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
IsoUsb_DbgPrint(3, ("IsoUsb_DispatchReadWrite - begins\n"));
IsoUsb_DbgPrint(3, ("IsoUsb_DispatchReadWrite::"));
IsoUsb_IoIncrement(deviceExtension);
if(deviceExtension->DeviceState != Working) {
IsoUsb_DbgPrint(1, ("Invalid device state\n"));
ntStatus = STATUS_INVALID_DEVICE_STATE;
goto IsoUsb_DispatchReadWrite_Exit;
}
//
// make sure that the selective suspend request has been completed.
//
if(deviceExtension->SSEnable) {
//
// It is true that the client driver cancelled the selective suspend
// request in the dispatch routine for create Irps.
// But there is no guarantee that it has indeed completed.
// so wait on the NoIdleReqPendEvent and proceed only if this event
// is signalled.
//
IsoUsb_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n"));
KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent,
Executive,
KernelMode,
FALSE,
NULL);
}
//
// obtain the pipe information for read
// and write from the fileobject.
//
if(fileObject && fileObject->FsContext) {
fileObjectContent = (PFILE_OBJECT_CONTENT) fileObject->FsContext;
pipeInformation = (PUSBD_PIPE_INFORMATION)
fileObjectContent->PipeInformation;
}
else {
IsoUsb_DbgPrint(1, ("Invalid device state\n"));
ntStatus = STATUS_INVALID_DEVICE_STATE;
goto IsoUsb_DispatchReadWrite_Exit;
}
if((pipeInformation == NULL) ||
(UsbdPipeTypeIsochronous != pipeInformation->PipeType)) {
IsoUsb_DbgPrint(1, ("Incorrect pipe\n"));
ntStatus = STATUS_INVALID_DEVICE_STATE;
goto IsoUsb_DispatchReadWrite_Exit;
}
if(Irp->MdlAddress) {
totalLength = MmGetMdlByteCount(Irp->MdlAddress);
}
if(totalLength == 0) {
IsoUsb_DbgPrint(1, ("Transfer data length = 0\n"));
ntStatus = STATUS_SUCCESS;
goto IsoUsb_DispatchReadWrite_Exit;
}
//
// each packet can hold this much info
//
packetSize = pipeInformation->MaximumPacketSize;
if(packetSize == 0) {
IsoUsb_DbgPrint(1, ("Invalid parameter\n"));
ntStatus = STATUS_INVALID_PARAMETER;
goto IsoUsb_DispatchReadWrite_Exit;
}
//
// atleast packet worth of data to be transferred.
//
if(totalLength < packetSize) {
IsoUsb_DbgPrint(1, ("Atleast packet worth of data..\n"));
ntStatus = STATUS_INVALID_PARAMETER;
goto IsoUsb_DispatchReadWrite_Exit;
}
// perform reset. if there are some active transfers queued up
// for this endpoint then the reset pipe will fail.
//
IsoUsb_ResetPipe(DeviceObject, pipeInformation);
if(deviceExtension->IsDeviceHighSpeed) {
ntStatus = PerformHighSpeedIsochTransfer(DeviceObject,
pipeInformation,
Irp,
totalLength);
}
else {
ntStatus = PerformFullSpeedIsochTransfer(DeviceObject,
pipeInformation,
Irp,
totalLength);
}
return ntStatus;
IsoUsb_DispatchReadWrite_Exit:
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
IsoUsb_DbgPrint(3, ("IsoUsb_DispatchReadWrite::"));
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 then
1. creates a ISOUSB_RW_CONTEXT for each
read/write to be performed.
2. creates SUB_CONTEXT for each irp/urb pair.
(Each irp/urb pair can transfer a max of 1024 packets.)
3. All the irp/urb pairs are initialized
4. The subsidiary irps (of the irp/urb pair) are passed
down the stack at once.
5. The main Read/Write irp is pending
Arguments:
DeviceObject - pointer to device object
Irp - I/O request packet
Return Value:
NT status value
--*/
{
ULONG i;
ULONG j;
ULONG numIrps;
ULONG stageSize;
ULONG contextSize;
ULONG packetSize;
ULONG numberOfPackets;
ULONG actualPackets;
ULONG minDataInEachPacket;
ULONG dataLeftToBeDistributed;
ULONG numberOfPacketsFilledToBrim;
CCHAR stackSize;
KIRQL oldIrql;
PUCHAR virtualAddress;
BOOLEAN read;
NTSTATUS ntStatus;
PDEVICE_EXTENSION deviceExtension;
PIO_STACK_LOCATION irpStack;
PIO_STACK_LOCATION nextStack;
PISOUSB_RW_CONTEXT rwContext;
//
// initialize vars
//
irpStack = IoGetCurrentIrpStackLocation(Irp);
read = (irpStack->MajorFunction == IRP_MJ_READ) ? TRUE : FALSE;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
if(TotalLength < 8) {
ntStatus = STATUS_INVALID_PARAMETER;
goto PerformHighSpeedIsochTransfer_Exit;
}
//
// each packet can hold this much info
//
packetSize = PipeInformation->MaximumPacketSize;
numberOfPackets = (TotalLength + packetSize - 1) / packetSize;
if(0 == (numberOfPackets % 8)) {
actualPackets = numberOfPackets;
}
else {
//
// we need multiple of 8 packets only.
//
actualPackets = numberOfPackets +
(8 - (numberOfPackets % 8));
}
minDataInEachPacket = TotalLength / actualPackets;
if(minDataInEachPacket == packetSize) {
numberOfPacketsFilledToBrim = actualPackets;
dataLeftToBeDistributed = 0;
IsoUsb_DbgPrint(1, ("TotalLength = %d\n", TotalLength));
IsoUsb_DbgPrint(1, ("PacketSize = %d\n", packetSize));
IsoUsb_DbgPrint(1, ("Each of %d packets has %d bytes\n",
numberOfPacketsFilledToBrim,
packetSize));
}
else {
dataLeftToBeDistributed = TotalLength -
(minDataInEachPacket * actualPackets);
numberOfPacketsFilledToBrim = dataLeftToBeDistributed /
(packetSize - minDataInEachPacket);
dataLeftToBeDistributed -= (numberOfPacketsFilledToBrim *
(packetSize - minDataInEachPacket));
IsoUsb_DbgPrint(1, ("TotalLength = %d\n", TotalLength));
IsoUsb_DbgPrint(1, ("PacketSize = %d\n", packetSize));
IsoUsb_DbgPrint(1, ("Each of %d packets has %d bytes\n",
numberOfPacketsFilledToBrim,
packetSize));
if(dataLeftToBeDistributed) {
IsoUsb_DbgPrint(1, ("One packet has %d bytes\n",
minDataInEachPacket + dataLeftToBeDistributed));
IsoUsb_DbgPrint(1, ("Each of %d packets has %d bytes\n",
actualPackets - (numberOfPacketsFilledToBrim + 1),
minDataInEachPacket));
}
else {
IsoUsb_DbgPrint(1, ("Each of %d packets has %d bytes\n",
actualPackets - numberOfPacketsFilledToBrim,
minDataInEachPacket));
}
}
//
// 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 = (actualPackets + 1023) / 1024;
IsoUsb_DbgPrint(1, ("PeformHighSpeedIsochTransfer::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"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -