2025-04-27 07:49:33 -04:00

745 lines
23 KiB
C

/*--
Copyright (c) 1998, 1999 Microsoft Corporation
Module Name:
ioctli.c
Abstract:
Environment:
Kernel mode only.
Notes:
--*/
#include "usbverfy.h"
#include "stdio.h"
#ifdef ALLOC_PRAGMA
//#pragma alloc_text (PAGE, UsbVerify_InternIoCtl)
#endif
NTSTATUS
UsbVerify_InternIoCtl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch routine for internal device control requests.
Arguments:
DeviceObject - Pointer to the device object.
Irp - Pointer to the request packet.
Return Value:
Status is returned.
--*/
{
PIO_STACK_LOCATION stack;
KEVENT event;
NTSTATUS status = STATUS_SUCCESS;
stack = IoGetCurrentIrpStackLocation(Irp);
switch (stack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_INTERNAL_USB_SUBMIT_URB:
return UsbVerify_UsbSubmitUrb(DeviceObject, Irp);
default:
break;
}
return UsbVerify_DispatchPassThrough(DeviceObject, Irp);
}
VOID
UsbVerify_SelectConfiguration(
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
PIRP Irp
)
{
PUSBD_INTERFACE_INFORMATION interface;
PURB urb;
KIRQL irql;
UCHAR interfaceIndex;
urb = URB_FROM_IRP(Irp);
DeviceExtension->ValidConfigurationHandle =
urb->UrbSelectConfiguration.ConfigurationHandle;
interface = &urb->UrbSelectConfiguration.Interface;
UsbVerify_Print(DeviceExtension, PRINT_LIST_TRACE, ("begin select config\n"));
UsbVerify_Print(DeviceExtension, PRINT_LIST_NOISE,
("select config, initializing inteface list\n"));
UsbVerify_LockInterfaceList(DeviceExtension, irql);
UsbVerify_InitializeInterfaceList(
DeviceExtension,
urb->UrbSelectConfiguration.ConfigurationDescriptor->bNumInterfaces,
TRUE);
UsbVerify_Print(DeviceExtension, PRINT_LIST_NOISE, ("adding %d interfaces\n",
(urb->UrbSelectConfiguration.ConfigurationDescriptor->bNumInterfaces
)));
//
// Add each interface to the list
//
for (interfaceIndex = 0;
interfaceIndex < urb->UrbSelectConfiguration.ConfigurationDescriptor->bNumInterfaces;
interfaceIndex++) {
UsbVerify_InterfaceListAddInterface(DeviceExtension, interface);
interface = (PUSBD_INTERFACE_INFORMATION)
(((PUCHAR) interface) + interface->Length);
}
UsbVerify_UnlockInterfaceList(DeviceExtension, irql);
UsbVerify_Print(DeviceExtension, PRINT_LIST_TRACE, ("end select config\n"));
}
VOID
UsbVerify_SelectInterface(
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
PIRP Irp
)
{
PUSBD_INTERFACE_INFORMATION interface;
PURB urb;
KIRQL irql;
UsbVerify_Print(DeviceExtension, PRINT_LIST_TRACE,
("begin select interface\n"));
urb = URB_FROM_IRP(Irp);
interface = &urb->UrbSelectInterface.Interface;
UsbVerify_ASSERT(DeviceExtension->ValidConfigurationHandle ==
urb->UrbSelectInterface.ConfigurationHandle,
DeviceExtension->Self,
Irp,
urb);
//
// Add the pipes in this interface to our pipe list
//
UsbVerify_LockInterfaceList(DeviceExtension, irql);
UsbVerify_Print(DeviceExtension, PRINT_LIST_INFO,
("Adding new interface %p from select interface\n", interface));
//
// This will handle the removal of the old interface if it exists
//
UsbVerify_InterfaceListAddInterface(DeviceExtension, interface);
UsbVerify_UnlockInterfaceList(DeviceExtension, irql);
UsbVerify_Print(DeviceExtension, PRINT_LIST_TRACE, ("end select interface\n"));
}
NTSTATUS
UsbVerify_FrameLength(
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
PIRP Irp,
PURB OrigUrb,
PBOOLEAN Completed
)
{
NTSTATUS status;
PURB urb = URB_FROM_IRP(Irp);
switch (urb->UrbHeader.Function) {
case URB_FUNCTION_SET_FRAME_LENGTH:
UsbVerify_ASSERT(DeviceExtension->HasFrameLengthControl == TRUE,
DeviceExtension->Self,
Irp,
urb
);
status = STATUS_SUCCESS;
*Completed = FALSE;
break;
case URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL:
case URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL:
//
// This will send the URB down and perform any generic post processing
// we would perform on the URB
//
status = UsbVerify_SendUrbSynchronously(DeviceExtension, Irp, OrigUrb);
//
// Because we may replace URBs with our own copies, the memory pointed
// to by "urb" may have been freed, so get the urb again
//
urb = URB_FROM_IRP(Irp);
if (NT_SUCCESS(status)) {
if (urb->UrbHeader.Function == URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL) {
DeviceExtension->HasFrameLengthControl = TRUE;
}
else {
DeviceExtension->HasFrameLengthControl = FALSE;
}
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
*Completed = TRUE;
}
return status;
}
VOID
UsbVerify_ControlTransfer(
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
PIRP Irp
)
{
PURB urb = URB_FROM_IRP(Irp);
PUSBD_PIPE_INFORMATION pPipeInfo;
KIRQL irql;
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_PIPES)) {
UsbVerify_LockInterfaceList(DeviceExtension, irql);
pPipeInfo = UsbVerify_ValidatePipe(DeviceExtension,
urb->UrbControlTransfer.PipeHandle);
UsbVerify_ASSERT(pPipeInfo != NULL,
DeviceExtension->Self,
Irp,
urb);
if (!pPipeInfo) {
UsbVerify_LogError(IDS_INVALID_PIPE, DeviceExtension, Irp, urb);
}
else {
UsbVerify_ASSERT(pPipeInfo->PipeType == UsbdPipeTypeControl,
DeviceExtension->Self,
Irp,
urb);
}
UsbVerify_UnlockInterfaceList(DeviceExtension, irql);
}
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_NOT_IMPLEMENTED)) {
if (urb->UrbControlTransfer.UrbLink != NULL) {
UsbVerify_LogError(IDS_URB_LINK_NOT_IMPLEMENTED, DeviceExtension, Irp, urb);
}
}
//
// TODO: figure out if we need to validate anything else in the URB
//
}
VOID
UsbVerify_BulkOrInterruptTransfer(
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
PIRP Irp
)
{
PURB urb = URB_FROM_IRP(Irp);
PUSBD_PIPE_INFORMATION pPipeInfo;
KIRQL irql;
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_PIPES)) {
UsbVerify_LockInterfaceList(DeviceExtension, irql);
pPipeInfo =
UsbVerify_ValidatePipe(DeviceExtension,
urb->UrbBulkOrInterruptTransfer.PipeHandle);
UsbVerify_ASSERT(pPipeInfo != NULL,
DeviceExtension->Self,
Irp,
urb);
if (!pPipeInfo) {
UsbVerify_LogError(IDS_INVALID_PIPE, DeviceExtension, Irp, urb);
}
else {
UsbVerify_ASSERT(pPipeInfo->PipeType == UsbdPipeTypeBulk ||
pPipeInfo->PipeType == UsbdPipeTypeInterrupt,
DeviceExtension->Self,
Irp,
urb);
}
UsbVerify_UnlockInterfaceList(DeviceExtension, irql);
}
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_NOT_IMPLEMENTED)) {
if (urb->UrbBulkOrInterruptTransfer.UrbLink != NULL) {
UsbVerify_LogError(IDS_URB_LINK_NOT_IMPLEMENTED, DeviceExtension, Irp, urb);
}
}
//
// TODO: figure out if we need to validate anything else in the URB
//
}
VOID
UsbVerify_IsochTransfer(
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
PIRP Irp
)
{
PURB urb = URB_FROM_IRP(Irp);
PUSBD_PIPE_INFORMATION pPipeInfo;
KIRQL irql;
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_PIPES)) {
UsbVerify_LockInterfaceList(DeviceExtension, irql);
pPipeInfo =
UsbVerify_ValidatePipe(DeviceExtension,
urb->UrbIsochronousTransfer.PipeHandle);
UsbVerify_ASSERT(pPipeInfo != NULL,
DeviceExtension->Self,
Irp,
urb);
if (!pPipeInfo) {
UsbVerify_LogError(IDS_INVALID_PIPE, DeviceExtension, Irp, urb);
}
else {
UsbVerify_ASSERT(pPipeInfo->PipeType == UsbdPipeTypeIsochronous,
DeviceExtension->Self,
Irp,
urb);
}
UsbVerify_UnlockInterfaceList(DeviceExtension, irql);
}
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_NOT_IMPLEMENTED)) {
if (urb->UrbIsochronousTransfer.UrbLink != NULL) {
UsbVerify_LogError(IDS_URB_LINK_NOT_IMPLEMENTED, DeviceExtension, Irp, urb);
}
}
//
// TODO: figure out if we need to validate anything else in the URB
//
}
VOID
UsbVerify_UrbPipeRequest(
PUSB_VERIFY_DEVICE_EXTENSION DeviceExtension,
PIRP Irp
)
{
CHAR dbgBuf[256];
PUSBD_PIPE_INFORMATION pPipeInfo;
PURB urb, verifyUrb;
PUSB_VERIFY_TRACK_URB verify;
PLIST_ENTRY entry;
KIRQL irql;
ULONG pipeFlag;
USBD_PIPE_HANDLE pipe;
urb = URB_FROM_IRP(Irp);
if (urb->UrbHeader.Function == URB_FUNCTION_RESET_PIPE &&
UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_TRACK_URBS)) {
//
// Make sure there are not pending URBs on this pipe
//
UsbVerify_LockUrbList(DeviceExtension, irql);
for (entry = DeviceExtension->UrbList.Flink;
entry != &DeviceExtension->UrbList;
entry = entry->Flink) {
pipe = NULL;
verify = CONTAINING_RECORD(entry, USB_VERIFY_TRACK_URB, Link);
verifyUrb = verify->Urb;
//
// If any of these ASSERT fire, that means that a reset or abort was
// sent down a pipe that had pending requests on it. BAD BAD BAD.
//
switch (verify->Urb->UrbHeader.Function) {
case URB_FUNCTION_ISOCH_TRANSFER:
if (verifyUrb->UrbIsochronousTransfer.PipeHandle == urb->UrbPipeRequest.PipeHandle) {
sprintf(dbgBuf,
"Reset sent on a pipe with a pending Isoch transfer, !urb 0x%x",
verifyUrb
);
UsbVerify_ASSERT_MSG(
verifyUrb->UrbIsochronousTransfer.PipeHandle != urb->UrbPipeRequest.PipeHandle,
dbgBuf,
DeviceExtension->Self,
Irp,
urb);
}
break;
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
if (verifyUrb->UrbBulkOrInterruptTransfer.PipeHandle == urb->UrbPipeRequest.PipeHandle) {
sprintf(dbgBuf,
"Reset sent on a pipe with a pending Bulk or Interrupt transfer, !urb 0x%x",
verifyUrb
);
UsbVerify_ASSERT_MSG(
verifyUrb->UrbBulkOrInterruptTransfer.PipeHandle != urb->UrbPipeRequest.PipeHandle,
dbgBuf,
DeviceExtension->Self,
Irp,
urb);
}
break;
case URB_FUNCTION_CONTROL_TRANSFER:
if (verifyUrb->UrbControlTransfer.PipeHandle == urb->UrbPipeRequest.PipeHandle) {
sprintf(dbgBuf,
"Reset sent on a pipe with a pending Control transfer, !urb 0x%x",
verifyUrb
);
UsbVerify_ASSERT_MSG(
verifyUrb->UrbControlTransfer.PipeHandle != urb->UrbPipeRequest.PipeHandle,
dbgBuf,
DeviceExtension->Self,
Irp,
urb);
}
break;
default:
;
}
}
UsbVerify_UnlockUrbList(DeviceExtension, irql);
}
if (UsbVerify_CheckVerifyFlags(DeviceExtension, VERIFY_PIPES)) {
if (urb->UrbHeader.Function == URB_FUNCTION_RESET_PIPE) {
pipeFlag = USB_VERIFY_PF_RESETTING;
}
else {
pipeFlag = USB_VERIFY_PF_ABORTING;
}
UsbVerify_LockInterfaceList(DeviceExtension, irql);
pPipeInfo = UsbVerify_ValidatePipe(DeviceExtension,
urb->UrbPipeRequest.PipeHandle);
if (pPipeInfo == NULL) {
UsbVerify_ASSERT_MSG(
pPipeInfo != NULL,
"Abort or reset sent down an invalid pipe",
DeviceExtension->Self,
Irp,
urb);
}
else {
UsbVerify_ASSERT_MSG(
(pPipeInfo->PipeFlags & pipeFlag) == 0,
(pipeFlag == USB_VERIFY_PF_ABORTING)
? "Abort sent on a pipe with an abort already pending"
: "Reset sent on a pipe with a reset already pending",
DeviceExtension->Self,
Irp,
urb);
pPipeInfo->PipeFlags |= pipeFlag;
}
UsbVerify_UnlockInterfaceList(DeviceExtension, irql);
}
}
NTSTATUS
UsbVerify_UsbSubmitUrb(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PUSB_VERIFY_DEVICE_EXTENSION devExt;
NTSTATUS status = STATUS_SUCCESS;
PURB origUrb, urb;
KIRQL irql;
devExt = GetExtension(DeviceObject);
//
// This function can change the URB embedded in the irp so we grab the urb
// AFTER calling this function. The original urb will be stored in origUrb
// if the urb was replaced.
//
UsbVerify_PreProcessUrb(devExt, Irp, &origUrb);
urb = URB_FROM_IRP(Irp);
UsbVerify_Print(devExt, PRINT_URB_NOISE,
("Urb 0x%x from irp 0x%x, function is %x\n",
urb, Irp, (ULONG) urb->UrbHeader.Function));
switch (urb->UrbHeader.Function) {
case URB_FUNCTION_SELECT_CONFIGURATION:
//
// We always maintain the InterfaceList, not matter if VERIFY_PIPES is set
// or not. VERIFY_PIPES just controls if check against the list or not
//
UsbVerify_LockInterfaceList(devExt, irql);
UsbVerify_ClearInterfaceList (
devExt,
((urb->UrbSelectConfiguration.ConfigurationDescriptor == NULL)
? RemoveSelectConfigZero
: RemoveSelectConfig) );
UsbVerify_UnlockInterfaceList(devExt, irql);
//
// Only create a new pipe list if the request has a valid ConfigDesc
//
if (urb->UrbSelectConfiguration.ConfigurationDescriptor != NULL) {
//
// This will send the URB down and perform any generic post processing
// we would perform on the URB
//
status = UsbVerify_SendUrbSynchronously(devExt, Irp, origUrb);
if (NT_SUCCESS(status)) {
UsbVerify_SelectConfiguration(devExt, Irp);
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
else {
devExt->ValidConfigurationHandle = NULL;
}
break;
case URB_FUNCTION_SELECT_INTERFACE:
//
// This will send the URB down and perform any generic post processing
// we would perform on the URB
//
status = UsbVerify_SendUrbSynchronously(devExt, Irp, origUrb);
if (NT_SUCCESS(status)) {
//
// This will remove the old interface if exists and add the new one
//
UsbVerify_SelectInterface(devExt, Irp);
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
case URB_FUNCTION_RESET_PIPE:
case URB_FUNCTION_ABORT_PIPE:
UsbVerify_UrbPipeRequest(devExt, Irp);
// send the irp asynch
break;
case URB_FUNCTION_CONTROL_TRANSFER:
UsbVerify_ControlTransfer(devExt, Irp);
break;
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
UsbVerify_BulkOrInterruptTransfer(devExt, Irp);
break;
case URB_FUNCTION_ISOCH_TRANSFER:
UsbVerify_IsochTransfer(devExt, Irp);
break;
//
// Frame length related URBs
//
case URB_FUNCTION_SET_FRAME_LENGTH:
case URB_FUNCTION_TAKE_FRAME_LENGTH_CONTROL:
case URB_FUNCTION_RELEASE_FRAME_LENGTH_CONTROL:
{
BOOLEAN completed;
status = UsbVerify_FrameLength(devExt, Irp, origUrb, &completed);
if (completed) {
return status;
}
}
break;
//
// These functions correspond
// to the standard commands on the default pipe
//
// direction is implied
//
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
case URB_FUNCTION_GET_DESCRIPTOR_FROM_ENDPOINT:
case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:
case URB_FUNCTION_SET_DESCRIPTOR_TO_DEVICE:
case URB_FUNCTION_SET_DESCRIPTOR_TO_ENDPOINT:
case URB_FUNCTION_SET_DESCRIPTOR_TO_INTERFACE:
if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_NOT_IMPLEMENTED)) {
if (urb->UrbControlDescriptorRequest.UrbLink != NULL) {
UsbVerify_LogError(IDS_URB_LINK_NOT_IMPLEMENTED, devExt, Irp, urb);
}
}
break;
//
// TODO: figure out if we can check anything on the URB other than UrbLink
//
case URB_FUNCTION_SET_FEATURE_TO_DEVICE:
case URB_FUNCTION_SET_FEATURE_TO_INTERFACE:
case URB_FUNCTION_SET_FEATURE_TO_ENDPOINT:
case URB_FUNCTION_SET_FEATURE_TO_OTHER:
case URB_FUNCTION_CLEAR_FEATURE_TO_DEVICE:
case URB_FUNCTION_CLEAR_FEATURE_TO_INTERFACE:
case URB_FUNCTION_CLEAR_FEATURE_TO_ENDPOINT:
case URB_FUNCTION_CLEAR_FEATURE_TO_OTHER:
if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_NOT_IMPLEMENTED)) {
if (urb->UrbControlFeatureRequest.UrbLink != NULL) {
UsbVerify_LogError(IDS_URB_LINK_NOT_IMPLEMENTED, devExt, Irp, urb);
}
}
break;
case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
case URB_FUNCTION_GET_STATUS_FROM_INTERFACE:
case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT:
case URB_FUNCTION_GET_STATUS_FROM_OTHER:
if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_NOT_IMPLEMENTED)) {
if (urb->UrbControlGetStatusRequest.UrbLink != NULL) {
UsbVerify_LogError(IDS_URB_LINK_NOT_IMPLEMENTED, devExt, Irp, urb);
}
}
break;
case URB_FUNCTION_VENDOR_DEVICE:
case URB_FUNCTION_VENDOR_INTERFACE:
case URB_FUNCTION_VENDOR_ENDPOINT:
case URB_FUNCTION_VENDOR_OTHER:
case URB_FUNCTION_CLASS_DEVICE:
case URB_FUNCTION_CLASS_INTERFACE:
case URB_FUNCTION_CLASS_ENDPOINT:
case URB_FUNCTION_CLASS_OTHER:
if (UsbVerify_CheckVerifyFlags(devExt, VERIFY_NOT_IMPLEMENTED)) {
if (urb->UrbControlVendorClassRequest.UrbLink != NULL) {
UsbVerify_LogError(IDS_URB_LINK_NOT_IMPLEMENTED, devExt, Irp, urb);
}
}
break;
case URB_FUNCTION_GET_FRAME_LENGTH:
case URB_FUNCTION_GET_CURRENT_FRAME_NUMBER:
default:
break;
}
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE) UsbVerify_UrbComplete,
origUrb,
TRUE,
TRUE,
TRUE); // No need for Cancel
return IoCallDriver(devExt->TopOfStack, Irp);
}
VOID
UsbVerify_PostVerifyUrb(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PUSB_VERIFY_DEVICE_EXTENSION devExt;
PUSBD_PIPE_INFORMATION pPipeInfo;
PURB urb;
KIRQL irql;
ULONG pipeFlag = 0x0;
urb = URB_FROM_IRP(Irp);
devExt = GetExtension(DeviceObject);
switch (urb->UrbHeader.Function) {
case URB_FUNCTION_RESET_PIPE:
pipeFlag = USB_VERIFY_PF_RESETTING;
case URB_FUNCTION_ABORT_PIPE:
if (pipeFlag == 0x0) {
pipeFlag = USB_VERIFY_PF_ABORTING;
}
UsbVerify_LockInterfaceList(devExt, irql);
pPipeInfo = UsbVerify_ValidatePipe(devExt,
urb->UrbPipeRequest.PipeHandle);
UsbVerify_ASSERT(pPipeInfo != NULL, DeviceObject, Irp, urb);
if (pPipeInfo) {
//
// Make sure that the flag was set on the way down, otherwise there
// is an internal logic error.
//
UsbVerify_ASSERT((pPipeInfo->PipeFlags & pipeFlag) == pipeFlag,
DeviceObject,
Irp,
urb);
pPipeInfo->PipeFlags &= ~pipeFlag;
}
UsbVerify_UnlockInterfaceList(devExt, irql);
break;
default:
// do nothing
;
}
}