📄 readwrite.cpp
字号:
VOID ResetDevice(PDEVICE_OBJECT fdo)
{ // ResetDevice
PAGED_CODE();
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IO_STATUS_BLOCK iostatus;
PIRP Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_RESET_PORT,
pdx->LowerDeviceObject, NULL, 0, NULL, 0, TRUE, &event, &iostatus);
if (!Irp)
return;
NTSTATUS status = IoCallDriver(pdx->LowerDeviceObject, Irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = iostatus.Status;
}
if (!NT_SUCCESS(status))
KdPrint((DRIVERNAME " - Error %X trying to reset device\n", status));
} // ResetDevice
///////////////////////////////////////////////////////////////////////////////
// ResetDevice is called during an attempt to recover after an error in order to
// reset the pipe that had the error
#pragma PAGEDCODE
NTSTATUS ResetPipe(PDEVICE_OBJECT fdo, USBD_PIPE_HANDLE hpipe)
{ // ResetPipe
PAGED_CODE();
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
URB urb;
urb.UrbHeader.Length = (USHORT) sizeof(_URB_PIPE_REQUEST);
urb.UrbHeader.Function = URB_FUNCTION_RESET_PIPE;
urb.UrbPipeRequest.PipeHandle = hpipe;
NTSTATUS status = SendAwaitUrb(fdo, &urb);
if (!NT_SUCCESS(status))
KdPrint((DRIVERNAME " - Error %X trying to reset a pipe\n", status));
return status;
} // ResetPipe
///////////////////////////////////////////////////////////////////////////////
#pragma PAGEDCODE
NTSTATUS SendAwaitUrb(PDEVICE_OBJECT fdo, PURB urb)
{ // SendAwaitUrb
PAGED_CODE();
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IO_STATUS_BLOCK iostatus;
PIRP Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_SUBMIT_URB,
pdx->LowerDeviceObject, NULL, 0, NULL, 0, TRUE, &event, &iostatus);
if (!Irp)
{
KdPrint((DRIVERNAME " - Unable to allocate IRP for sending URB\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
stack->Parameters.Others.Argument1 = (PVOID) urb;
NTSTATUS status = IoCallDriver(pdx->LowerDeviceObject, Irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = iostatus.Status;
}
return status;
} // SendAwaitUrb
///////////////////////////////////////////////////////////////////////////////
#pragma PAGEDCODE
NTSTATUS StartDevice(PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated)
{ // StartDevice
PAGED_CODE();
NTSTATUS status;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
URB urb; // URB for use in this subroutine
// Read our device descriptor. The only real purpose to this would be to find out how many
// configurations there are so we can read their descriptors. In this simplest of examples,
// there's only one configuration.
UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_DEVICE_DESCRIPTOR_TYPE,
0, 0, &pdx->dd, NULL, sizeof(pdx->dd), NULL);
status = SendAwaitUrb(fdo, &urb);
if (!NT_SUCCESS(status))
{
KdPrint((DRIVERNAME " - Error %X trying to read device descriptor\n", status));
return status;
}
MSGUSBSTRING(fdo, DRIVERNAME " - Configuring device from %ws\n", pdx->dd.iManufacturer);
MSGUSBSTRING(fdo, DRIVERNAME " - Product is %ws\n", pdx->dd.iProduct);
MSGUSBSTRING(fdo, DRIVERNAME " - Serial number is %ws\n", pdx->dd.iSerialNumber);
// Read the descriptor of the first configuration. This requires two steps. The first step
// reads the fixed-size configuration descriptor alone. The second step reads the
// configuration descriptor plus all imbedded interface and endpoint descriptors.
USB_CONFIGURATION_DESCRIPTOR tcd;
UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE,
0, 0, &tcd, NULL, sizeof(tcd), NULL);
status = SendAwaitUrb(fdo, &urb);
if (!NT_SUCCESS(status))
{
KdPrint((DRIVERNAME " - Error %X trying to read configuration descriptor 1\n", status));
return status;
}
ULONG size = tcd.wTotalLength;
PUSB_CONFIGURATION_DESCRIPTOR pcd = (PUSB_CONFIGURATION_DESCRIPTOR) ExAllocatePool(NonPagedPool, size);
if (!pcd)
{
KdPrint((DRIVERNAME " - Unable to allocate %X bytes for configuration descriptor\n", size));
return STATUS_INSUFFICIENT_RESOURCES;
}
__try
{
UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE,
0, 0, pcd, NULL, size, NULL);
status = SendAwaitUrb(fdo, &urb);
if (!NT_SUCCESS(status))
{
KdPrint((DRIVERNAME " - Error %X trying to read configuration descriptor 1\n", status));
return status;
}
MSGUSBSTRING(fdo, DRIVERNAME " - Selecting configuration named %ws\n", pcd->iConfiguration);
// Locate the descriptor for the one and only interface we expect to find
PUSB_INTERFACE_DESCRIPTOR pid = USBD_ParseConfigurationDescriptorEx(pcd, pcd,
-1, -1, -1, -1, -1);
ASSERT(pid);
MSGUSBSTRING(fdo, DRIVERNAME " - Selecting interface named %ws\n", pid->iInterface);
// Create a URB to use in selecting a configuration.
USBD_INTERFACE_LIST_ENTRY interfaces[2] = {
{pid, NULL},
{NULL, NULL}, // fence to terminate the array
};
PURB selurb = USBD_CreateConfigurationRequestEx(pcd, interfaces);
if (!selurb)
{
KdPrint((DRIVERNAME " - Unable to create configuration request\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
__try
{
// Verify that the interface describes exactly the endpoints we expect
if (pid->bNumEndpoints != 2)
{
KdPrint((DRIVERNAME " - %d is the wrong number of endpoints\n", pid->bNumEndpoints));
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
PUSB_ENDPOINT_DESCRIPTOR ped = (PUSB_ENDPOINT_DESCRIPTOR) pid;
ped = (PUSB_ENDPOINT_DESCRIPTOR) USBD_ParseDescriptors(pcd, tcd.wTotalLength, ped, USB_ENDPOINT_DESCRIPTOR_TYPE);
if (!ped || ped->bEndpointAddress != 0x82 || ped->bmAttributes != USB_ENDPOINT_TYPE_BULK || ped->wMaxPacketSize != 64)
{
KdPrint((DRIVERNAME " - Endpoint has wrong attributes\n"));
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
++ped;
if (!ped || ped->bEndpointAddress != 0x2 || ped->bmAttributes != USB_ENDPOINT_TYPE_BULK || ped->wMaxPacketSize != 64)
{
KdPrint((DRIVERNAME " - Endpoint has wrong attributes\n"));
return STATUS_DEVICE_CONFIGURATION_ERROR;
}
++ped;
PUSBD_INTERFACE_INFORMATION pii = interfaces[0].Interface;
ASSERT(pii->NumberOfPipes == pid->bNumEndpoints);
// Initialize the maximum transfer size for each of the endpoints. The
// default would be PAGE_SIZE. The firmware itself only has a 4096-byte
// ring buffer, though. We need to restrict the test applet to that many
// bytes. In order to exercise the multi-segment aspect of the transfer code,
// therefore, reduce the maximum transfer size to 1024 bytes.
pii->Pipes[0].MaximumTransferSize = 1024;
pii->Pipes[1].MaximumTransferSize = 1024;
pdx->maxtransfer = 1024; // save for use in handling reads & writes
// Submit the set-configuration request
status = SendAwaitUrb(fdo, selurb);
if (!NT_SUCCESS(status))
{
KdPrint((DRIVERNAME " - Error %X trying to select configuration\n", status));
return status;
}
// Save the configuration and pipe handles
pdx->hconfig = selurb->UrbSelectConfiguration.ConfigurationHandle;
pdx->hinpipe = pii->Pipes[0].PipeHandle;
pdx->houtpipe = pii->Pipes[1].PipeHandle;
// Transfer ownership of the configuration descriptor to the device extension
pdx->pcd = pcd;
pcd = NULL;
}
__finally
{
ExFreePool(selurb);
}
}
__finally
{
if (pcd)
ExFreePool(pcd);
}
return STATUS_SUCCESS;
} // StartDevice
///////////////////////////////////////////////////////////////////////////////
#pragma LOCKEDCODE
VOID StartIo(PDEVICE_OBJECT fdo, PIRP Irp)
{ // StartIo
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
// Use the remove lock to guard against lower drivers disappearing while
// this IRP is active. Whoever sent us this IRP is protecting us from removal
// at least until our completion routine returns STATUS_SUCCESS, which is why
// we don't need to use IoSetCompletionRoutineEx in this driver.
NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
if (!NT_SUCCESS(status))
{
CompleteRequest(Irp, status, 0);
return;
}
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
BOOLEAN read = stack->MajorFunction == IRP_MJ_READ;
USBD_PIPE_HANDLE hpipe = read ? pdx->hinpipe : pdx->houtpipe;
// Our strategy will be to do the transfer in stages up to MAXTRANSFER bytes
// long using a single URB that we resubmit during the completion routine.
// Note that chained URBs via UrbLink are not supported in either Win98 or Win2K.
PRWCONTEXT ctx = (PRWCONTEXT) ExAllocatePool(NonPagedPool, sizeof(RWCONTEXT));
if (!ctx)
{
KdPrint((DRIVERNAME " - Can't allocate memory for context structure\n"));
StartNextPacket(&pdx->dqReadWrite, fdo);
CompleteRequest(Irp, STATUS_INSUFFICIENT_RESOURCES, 0);
IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
return;
}
RtlZeroMemory(ctx, sizeof(RWCONTEXT));
ULONG length = Irp->MdlAddress ? MmGetMdlByteCount(Irp->MdlAddress) : 0;
if (!length)
{ // zero-length read
StartNextPacket(&pdx->dqReadWrite, fdo);
CompleteRequest(Irp, STATUS_SUCCESS, 0);
IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
return;
} // zero-length read
ULONG_PTR va = (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress);
ULONG urbflags = USBD_SHORT_TRANSFER_OK | (read ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
// Calculate the length of the segment we'll transfer in the first stage. In the original
// (pre-SP-5) version of this driver, I was transferring just enough data in the first
// stage to get to a page boundary. John Hyde pointed out that this is incorrect unless the
// buffer happens to be aligned on a 64-byte boundary because (according to section 5.8.3 of
// the USB spec) all transfers except the last must be of maximum length. Therefore, I revised
// this seample to just transfer the pipe maximum each time.
ULONG seglen = length;
if (seglen > pdx->maxtransfer)
{
seglen = pdx->maxtransfer;
KdPrint((DRIVERNAME " - Read/write of %d bytes will be done in segments of %d\n",
length, seglen));
}
// Allocate an MDL for each segment of the transfer. The parameters are chosen so
// that the MDL will have room for a maximum-sized buffer in the worst case where
// it starts just before a page boundary. (Note that the virtual address argument to
// IoAllocateMdl is not actually used as an address.)
PMDL mdl = IoAllocateMdl((PVOID) (PAGE_SIZE - 1), seglen, FALSE, FALSE, NULL);
if (!mdl)
{ // can't allocate MDL
KdPrint((DRIVERNAME " - Can't allocate memory for MDL\n"));
ExFreePool(ctx);
StartNextPacket(&pdx->dqReadWrite, fdo);
CompleteRequest(Irp, STATUS_INSUFFICIENT_RESOURCES, 0);
IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
} // can't allocate MDL
// Initialize the (partial) MDL to describe the first segment's subset of the user
// buffer.
IoBuildPartialMdl(Irp->MdlAddress, mdl, (PVOID) va, seglen);
// Reader Peter Diaconesco ran across an apparent bug in the Win2K version of UHCD.SYS. Under
// heavy load conditions, UHCD was bug-checking because its internal call to MmGetSystemAddressForMdl
// was apparently returning NULL (even though it's not supposed to). We can prevent that problem
// by mapping the pages in the following "safe" manner:
if (!GenericGetSystemAddressForMdl(mdl))
{ // can't map transfer segment
KdPrint((DRIVERNAME " - Can't map memory for read or write\n"));
ExFreePool(ctx);
StartNextPacket(&pdx->dqReadWrite, fdo);
CompleteRequest(Irp, STATUS_INSUFFICIENT_RESOURCES, 0);
IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
return;
} // can't map transfer segment
UsbBuildInterruptOrBulkTransferRequest(ctx, sizeof(_URB_BULK_OR_INTERRUPT_TRANSFER),
hpipe, NULL, mdl, seglen, urbflags, NULL);
// Set context structure parameters to pick up where we just left off
ctx->va = va + seglen;
ctx->length = length - seglen;
ctx->mdl = mdl;
ctx->numxfer = 0;
// Use the original Read or Write IRP as a container for the URB
stack = IoGetNextIrpStackLocation(Irp);
stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
stack->Parameters.Others.Argument1 = (PVOID) (PURB) ctx;
stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnReadWriteComplete,
(PVOID) ctx, TRUE, TRUE, TRUE);
IoCallDriver(pdx->LowerDeviceObject, Irp);
} // StartIo
///////////////////////////////////////////////////////////////////////////////
#pragma PAGEDCODE
VOID StopDevice(IN PDEVICE_OBJECT fdo, BOOLEAN oktouch /* = FALSE */)
{ // StopDevice
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
// If it's okay to touch our hardware (i.e., we're processing an IRP_MN_STOP_DEVICE),
// deconfigure the device.
if (oktouch)
{ // deconfigure device
URB urb;
UsbBuildSelectConfigurationRequest(&urb, sizeof(_URB_SELECT_CONFIGURATION), NULL);
NTSTATUS status = SendAwaitUrb(fdo, &urb);
if (!NT_SUCCESS(status))
KdPrint((DRIVERNAME " - Error %X trying to deconfigure device\n", status));
} // deconfigure device
if (pdx->pcd)
ExFreePool(pdx->pcd);
pdx->pcd = NULL;
} // StopDevice
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -