/*++ Copyright (c) 1996 Microsoft Corporation Copyright (c) 1996 Intel Corporation Module Name: ISOPERF.c Abstract: USB device driver for ISOPERF USB Device Environment: kernel mode only Notes: THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. Copyright (c) 1996 Intel Corporation All Rights Reserved. Revision History: 8-15-96: Version 1.0 Kosar Jaff --*/ #define DRIVER #define DEADMAN_TIMEOUT 5000 //timeout in ms //use a 5 second timeout /* // Include files needed for WDM driver support */ #pragma warning(disable:4214) // bitfield nonstd #include "wdm.h" #pragma warning(default:4214) #include "stdarg.h" #include "stdio.h" /* // Include files needed for USB support */ #pragma warning(disable:4200) //non std struct used #include "usbdi.h" #pragma warning(default:4200) #include "usbdlib.h" #include "usb.h" /* // Include file for the ISOPERF Device */ #include "ioctl.h" #include "ISOPERF.h" #include "iso.h" ULONG gulInstanceNumber = 0; ULONG gIsoPerfMaxDebug = TRUE; // NOTE (old code) (kjaff) // due to WDM loading separate images for the same ven/prod device, we can't rely on a device // chain, so...if we find an iso out dev obj we put it here as a short term fix // PDEVICE_OBJECT gMyOutputDevice = NULL; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Installable driver initialization entry point. This is where the driver is called when the driver is being loaded by the I/O system. This entry point is called directly by the I/O system. Arguments: DriverObject - pointer to the driver object RegistryPath - pointer to a unicode string representing the path to driver-specific key in the registry Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_OBJECT deviceObject = NULL; ISOPERF_KdPrint (("entering (ISOPERF) DriverEntry (build time/date: %s/%s\n",__TIME__,__DATE__)); DriverObject->MajorFunction[IRP_MJ_CREATE] = ISOPERF_Create; DriverObject->MajorFunction[IRP_MJ_CLOSE] = ISOPERF_Close; DriverObject->DriverUnload = ISOPERF_Unload; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ISOPERF_ProcessIOCTL; DriverObject->MajorFunction[IRP_MJ_WRITE] = ISOPERF_Write; DriverObject->MajorFunction[IRP_MJ_READ] = ISOPERF_Read; DriverObject->MajorFunction[IRP_MJ_PNP] = ISOPERF_Dispatch; DriverObject->DriverExtension->AddDevice = ISOPERF_PnPAddDevice; ISOPERF_KdPrint (("exiting (ISOPERF) DriverEntry (%x)\n", ntStatus)); ISOPERF_KdPrint (("sizeof IsoStats: %d\n",sizeof(Config_Stat_Info))); return ntStatus; } NTSTATUS ISOPERF_Dispatch( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Process the IRPs sent to this device. Arguments: DeviceObject - pointer to a device object Irp - pointer to an I/O Request Packet Return Value: NTSTATUS --*/ { PIO_STACK_LOCATION irpStack, nextStack; PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus; Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; /* // Get a pointer to the current location in the Irp. This is where // the function codes and parameters are located. */ irpStack = IoGetCurrentIrpStackLocation (Irp); /* // Get a pointer to the device extension */ deviceExtension = DeviceObject->DeviceExtension; switch (irpStack->MajorFunction) { case IRP_MJ_PNP: /* // // This IRP is for Plug and Play and Power Management messages for your device. // // When your device is first installed, the port on the hub to which it // is attached is powered on and the USB software subsystem does some // minimal querying of the device. After the USB subsystem is done with that // basic communication, (the device ID has been determined, and the device // has been given a unique USB bus address), it is considered "powered" by // the system. The IRP's minor code gives more information about the power event. // // Similarly, when the USB device is being removed from the system, the Plug // and Play subsystem and the USB software stack interact to notify the // appropriate driver using this same IRP code, although in this case the // minor code gives more information about the exact power event. // */ ISOPERF_KdPrint (("IRP_MJ_PNP\n")); switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: ISOPERF_KdPrint (("IRP_MN_START_DEVICE\n")); /* // We pass the Irp down to the underlying PDO first since that driver // may have some configuration work to do before we can control the device */ nextStack = IoGetNextIrpStackLocation(Irp); ASSERT(nextStack != NULL); RtlCopyMemory(nextStack, irpStack, sizeof(IO_STACK_LOCATION)); ISOPERF_KdPrint (("Passing START_DEVICE Irp down\n")); // This will be deviceExtension->StackDeviceObject in future revisions of this driver ntStatus = IoCallDriver(deviceExtension->PhysicalDeviceObject, Irp); ISOPERF_KdPrint (("Back from passing START_DEVICE Irp down; status: %#X\n", ntStatus)); // Now we can begin our configuration actions on the device ntStatus = ISOPERF_StartDevice(DeviceObject); break; //IRP_MN_START_DEVICE case IRP_MN_STOP_DEVICE: ISOPERF_KdPrint (("IRP_MN_STOP_DEVICE\n")); ISOPERF_Cleanup (DeviceObject); ntStatus = ISOPERF_StopDevice(DeviceObject); break; //IRP_MN_STOP_DEVICE case IRP_MN_REMOVE_DEVICE: /* // Note: This IRP handler will change slightly in future revisions of this // sample driver. Please watch this space for updates. */ ISOPERF_KdPrint (("IRP_MN_REMOVE_DEVICE\n")) ISOPERF_Cleanup (DeviceObject); ntStatus = ISOPERF_RemoveDevice(DeviceObject); break; //IRP_MN_REMOVE_DEVICE case IRP_MN_QUERY_STOP_DEVICE: ISOPERF_KdPrint (("IRP_MN_QUERY_STOP_DEVICE\n")); break; case IRP_MN_QUERY_REMOVE_DEVICE: ISOPERF_KdPrint (("IRP_MN_QUERY_REMOVE_DEVICE\n")); break; case IRP_MN_CANCEL_STOP_DEVICE: ISOPERF_KdPrint (("IRP_MN_CANCEL_STOP_DEVICE\n")); break; case IRP_MN_CANCEL_REMOVE_DEVICE: ISOPERF_KdPrint (("IRP_MN_CANCEL_REMOVE_DEVICE\n")); break; default: // A PnP Minor Function was not handled ISOPERF_KdPrint (("PnP IOCTL not handled\n")); } /* switch MinorFunction*/ nextStack = IoGetNextIrpStackLocation(Irp); ASSERT(nextStack != NULL); RtlCopyMemory(nextStack, irpStack, sizeof(IO_STACK_LOCATION)); /* // All PNP_POWER messages get passed to the PhysicalDeviceObject // (which in future revisions of this driver will be the StackDeviceObject) // we were given in PnPAddDevice. // // This physical device object is managed by the USB software subsystem, // and so this IRP must be propagated to the owning device driver for // that physical device object, so that driver in turn can perform any // device state management (e.g., remove its device object, etc.). */ ISOPERF_KdPrint (("Passing PnP Irp down, status = %x\n", ntStatus)); // This will be deviceExtension->StackDeviceObject in future revisions of this driver ntStatus = IoCallDriver(deviceExtension->PhysicalDeviceObject, Irp); /* // If lower layer driver marked the Irp as pending then reflect that by // calling IoMarkIrpPending. */ if (ntStatus == STATUS_PENDING) { IoMarkIrpPending(Irp); ISOPERF_KdPrint (("PnP Irp came back with STATUS_PENDING (%x)\n", ntStatus)); } else { ISOPERF_KdPrint (("PnP Irp came back, status = %x\n", ntStatus)); } // if ntStatus goto ISOPERF_Dispatch_Done; break; //IRP_MJ_PNP case IRP_MJ_POWER: /* // // This IRP is for Plug and Play and Power Management messages for your device. // // When your device is first installed, the port on the hub to which it // is attached is powered on and the USB software subsystem does some // minimal querying of the device. After the USB subsystem is done with that // basic communication, (the device ID has been determined, and the device // has been given a unique USB bus address), it is considered "powered" by // the system. The IRP's minor code gives more information about the power event. // // Similarly, when the USB device is being removed from the system, the Plug // and Play subsystem and the USB software stack interact to notify the // appropriate driver using this same IRP code, although in this case the // minor code gives more information about the exact power event. // */ ISOPERF_KdPrint (("IRP_MJ_POWER\n")); switch (irpStack->MinorFunction) { case IRP_MN_SET_POWER: switch (irpStack->Parameters.Power.Type) { case SystemPowerState: break; //SystemPowerState case DevicePowerState: switch (irpStack->Parameters.Power.State.DeviceState) { case PowerDeviceD3: ISOPERF_KdPrint (("IRP_MN_SET_D3\n")); break; case PowerDeviceD2: ISOPERF_KdPrint (("IRP_MN_SET_D2\n")); break; case PowerDeviceD1: ISOPERF_KdPrint (("IRP_MN_SET_D1\n")); break; case PowerDeviceD0: ISOPERF_KdPrint (("IRP_MN_SET_D0\n")); break; } // switch on Power.State.DeviceState break; //DevicePowerState }// switch on Power.Type break; //IRP_MN_SET_POWER case IRP_MN_QUERY_POWER: // Look at what type of power query this is switch (irpStack->Parameters.Power.Type) { case SystemPowerState: break; //SystemPowerState case DevicePowerState: switch (irpStack->Parameters.Power.State.DeviceState) { case PowerDeviceD2: ISOPERF_KdPrint (("IRP_MN_QUERY_D2\n")); break; case PowerDeviceD1: ISOPERF_KdPrint (("IRP_MN_QUERY_D1\n")); break; case PowerDeviceD3: ISOPERF_KdPrint (("IRP_MN_QUERY_D3\n")); break; } //switch on Power.State.DeviceState break; //DevicePowerState }//switch on Power.Type break; //IRP_MN_QUERY_POWER default: // A Power Minor Function was not handled ISOPERF_KdPrint (("Power IOCTL not handled\n")); } /* switch MinorFunction*/ nextStack = IoGetNextIrpStackLocation(Irp); ASSERT(nextStack != NULL); RtlCopyMemory(nextStack, irpStack, sizeof(IO_STACK_LOCATION)); /* // All PNP_POWER messages get passed to the PhysicalDeviceObject // (which in future revisions of this driver will be the StackDeviceObject) // we were given in PnPAddDevice. // // This physical device object is managed by the USB software subsystem, // and so this IRP must be propagated to the owning device driver for // that physical device object, so that driver in turn can perform any // device state management (e.g., remove its device object, etc.). */ ISOPERF_KdPrint (("Passing power down, status = %x\n", ntStatus)); // This will be deviceExtension->StackDeviceObject in future revisions of this driver ntStatus = IoCallDriver(deviceExtension->PhysicalDeviceObject, Irp); /* // If lower layer driver marked the Irp as pending then reflect that by // calling IoMarkIrpPending. */ if (ntStatus == STATUS_PENDING) { IoMarkIrpPending(Irp); ISOPERF_KdPrint (("PnP Irp came back with STATUS_PENDING (%x)\n", ntStatus)); } else { ISOPERF_KdPrint (("PnP Irp came back, status = %x\n", ntStatus)); } // if ntStatus goto ISOPERF_Dispatch_Done; break; //IRP_MJ_POWER default: ISOPERF_KdPrint (("A MAJOR IOCTL Code was not handled: %#X\n", irpStack->MajorFunction)); Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; } /* switch MajorFunction */ ntStatus = Irp->IoStatus.Status; IoCompleteRequest (Irp, IO_NO_INCREMENT ); ISOPERF_Dispatch_Done: ISOPERF_KdPrint (("Exit ISOPERF_Dispatch %x\n", ntStatus)); return ntStatus; }//ISOPERF_Dispatch VOID ISOPERF_Unload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Free all the allocated resources, etc. TODO: This is a placeholder for driver writer to add code on unload Arguments: DriverObject - pointer to a driver object Return Value: None --*/ { ISOPERF_KdPrint (("enter ISOPERF_Unload\n")); /* // TODO: Free any global resources allocated in DriverEntry */ ISOPERF_KdPrint (("exit ISOPERF_Unload\n")); } NTSTATUS ISOPERF_StartDevice( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Initializes a given instance of the ISOPERF Device on the USB. TODO: Perform any device initialization and querying here. For example, this routine queries the device's descriptors. Your device can be queried in a similar fashion with more specific requests that are tailored to your device's functionality. Arguments: DeviceObject - pointer to the device object for this instance of a ISOPERF Device Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus; PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL; PURB urb; ULONG siz; ISOPERF_KdPrint (("enter ISOPERF_StartDevice\n")); deviceExtension = DeviceObject->DeviceExtension; deviceExtension->NeedCleanup = TRUE; /* // Get some memory from then non paged pool (fixed, locked system memory) // for use by the USB Request Block (urb) for the specific USB Request we // will be performing below (a USB device request). */ urb = ISOPERF_ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), &gulBytesAllocated); if (urb) { siz = sizeof(USB_DEVICE_DESCRIPTOR); // Get some non paged memory for the device descriptor contents deviceDescriptor = ISOPERF_ExAllocatePool(NonPagedPool, siz, &gulBytesAllocated); if (deviceDescriptor) { // Use a macro in the standard USB header files to build the URB UsbBuildGetDescriptorRequest(urb, (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, deviceDescriptor, NULL, siz, NULL); // Get the device descriptor ntStatus = ISOPERF_CallUSBD(DeviceObject, urb); // Dump out the descriptor info to the debugger if (NT_SUCCESS(ntStatus)) { ISOPERF_KdPrint (("Device Descriptor = %x, len %x\n", deviceDescriptor, urb->UrbControlDescriptorRequest.TransferBufferLength)); ISOPERF_KdPrint (("ISOPERF Device Descriptor:\n")); ISOPERF_KdPrint (("-------------------------\n")); ISOPERF_KdPrint (("bLength %d\n", deviceDescriptor->bLength)); ISOPERF_KdPrint (("bDescriptorType 0x%x\n", deviceDescriptor->bDescriptorType)); ISOPERF_KdPrint (("bcdUSB 0x%x\n", deviceDescriptor->bcdUSB)); ISOPERF_KdPrint (("bDeviceClass 0x%x\n", deviceDescriptor->bDeviceClass)); ISOPERF_KdPrint (("bDeviceSubClass 0x%x\n", deviceDescriptor->bDeviceSubClass)); ISOPERF_KdPrint (("bDeviceProtocol 0x%x\n", deviceDescriptor->bDeviceProtocol)); ISOPERF_KdPrint (("bMaxPacketSize0 0x%x\n", deviceDescriptor->bMaxPacketSize0)); ISOPERF_KdPrint (("idVendor 0x%x\n", deviceDescriptor->idVendor)); ISOPERF_KdPrint (("idProduct 0x%x\n", deviceDescriptor->idProduct)); ISOPERF_KdPrint (("bcdDevice 0x%x\n", deviceDescriptor->bcdDevice)); ISOPERF_KdPrint (("iManufacturer 0x%x\n", deviceDescriptor->iManufacturer)); ISOPERF_KdPrint (("iProduct 0x%x\n", deviceDescriptor->iProduct)); ISOPERF_KdPrint (("iSerialNumber 0x%x\n", deviceDescriptor->iSerialNumber)); ISOPERF_KdPrint (("bNumConfigurations 0x%x\n", deviceDescriptor->bNumConfigurations)); } } else { ntStatus = STATUS_NO_MEMORY; } if (NT_SUCCESS(ntStatus)) { /* // Put a ptr to the device descriptor in the device extension for easy // access (like a "cached" copy). We will free this memory when the // device is removed. See the "ISOPERF_RemoveDevice" code. */ deviceExtension->DeviceDescriptor = deviceDescriptor; deviceExtension->Stopped = FALSE; } if (urb) { ISOPERF_ExFreePool(urb, &gulBytesFreed); }/* if urb */ } else { ntStatus = STATUS_NO_MEMORY; } // If the Get_Descriptor call was successful, then configure the device. if (NT_SUCCESS(ntStatus)) { ntStatus = ISOPERF_ConfigureDevice(DeviceObject); } ISOPERF_KdPrint (("exit ISOPERF_StartDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS ISOPERF_RemoveDevice( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Removes a given instance of a ISOPERF Device device on the USB. Arguments: DeviceObject - pointer to the device object for this instance of a ISOPERF Device Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus = STATUS_SUCCESS; int nInterfaceNumber; ISOPERF_KdPrint (("enter ISOPERF_RemoveDevice\n")); deviceExtension = DeviceObject->DeviceExtension; // After all the outstanding transfers are done, the test routines will clear the DeviceIsBusy flag if (deviceExtension->DeviceIsBusy) { ISOPERF_KdPrint (("RemoveDevice: Device is still busy\n")); //Tell test routines to stop submitting txfers deviceExtension->StopTransfers = TRUE; ntStatus = STATUS_DEVICE_BUSY; }else{ ISOPERF_KdPrint (("Freeing up Device Descriptor in DevExtension (%X)\n",deviceExtension->DeviceDescriptor)); if (deviceExtension->DeviceDescriptor) { ISOPERF_ExFreePool(deviceExtension->DeviceDescriptor, &gulBytesFreed); } // Free up any interface structures in our device extension for (nInterfaceNumber=0;nInterfaceNumber < MAX_INTERFACE; nInterfaceNumber++) { ISOPERF_KdPrint (("Freeing USBD Interfce Objs %d in DevExt: (%X)\n", nInterfaceNumber, deviceExtension->Interface[nInterfaceNumber])); if ((deviceExtension->Interface[nInterfaceNumber]) != NULL) { ISOPERF_ExFreePool(deviceExtension->Interface[nInterfaceNumber], &gulBytesFreed); }//if }/* for all the interfaces */ // Free up the device's config and stat memory if ((deviceExtension->pConfig_Stat_Information)!=NULL) { ISOPERF_ExFreePool (deviceExtension->pConfig_Stat_Information, &gulBytesFreed); }/* if valid device config/stat space */ /* // If someone has a symbolic link open, then just indicate in the dev ext that the device is stopped // since it's possible that the device object will stick around */ deviceExtension->Stopped = TRUE; /* // Delete the link to the Stack Device Object, and delete the // Functional Device Object we created */ IoDetachDevice(deviceExtension->StackDeviceObject); IoDeleteDevice (DeviceObject); }/*else it's ok to free up parts of the device object */ ISOPERF_KdPrint (("exit ISOPERF_RemoveDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS ISOPERF_StopDevice( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Stops a given instance of a ISOPERF Device device on the USB. Arguments: DeviceObject - pointer to the device object for this instance of a ISOPERF Device Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus = STATUS_SUCCESS; PURB urb; ULONG siz; ISOPERF_KdPrint (("enter ISOPERF_StopDevice\n")); deviceExtension = DeviceObject->DeviceExtension; /* // Send the select configuration urb with a NULL pointer for the configuration // handle, this closes the configuration and puts the device in the 'unconfigured' // state. */ siz = sizeof(struct _URB_SELECT_CONFIGURATION); urb = ISOPERF_ExAllocatePool(NonPagedPool, siz, &gulBytesAllocated); if (urb) { NTSTATUS status; UsbBuildSelectConfigurationRequest(urb, (USHORT) siz, NULL); status = ISOPERF_CallUSBD(DeviceObject, urb); ISOPERF_KdPrint (("Device Configuration Closed status = %x usb status = %x.\n", status, urb->UrbHeader.Status)); ISOPERF_ExFreePool(urb, &gulBytesFreed); } else { ntStatus = STATUS_NO_MEMORY; } ISOPERF_KdPrint (("exit ISOPERF_StopDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS ISOPERF_PnPAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: This routine is called to create a new instance of the device Arguments: DriverObject - pointer to the driver object for this instance of ISOPERF PhysicalDeviceObject - pointer to a device object created by the bus Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; PDEVICE_OBJECT deviceObject = NULL; PDEVICE_EXTENSION deviceExtension; ISOPERF_KdPrint(("enter ISOPERF_PnPAddDevice\n")); // create our funtional device object (FDO) ntStatus = ISOPERF_CreateDeviceObject(DriverObject, &deviceObject, gulInstanceNumber); if (NT_SUCCESS(ntStatus)) { deviceExtension = deviceObject->DeviceExtension; deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; /* // Add more flags here if your driver supports other specific // behavior. For example, if your IRP_MJ_READ and IRP_MJ_WRITE // handlers support DIRECT_IO, you would set that flag here. // // Also, store away the Physical device Object */ deviceExtension->PhysicalDeviceObject=PhysicalDeviceObject; // // Attach to the StackDeviceObject. This is the device object that what we // use to send Irps and Urbs down the USB software stack // deviceExtension->StackDeviceObject = IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject); ASSERT (deviceExtension->StackDeviceObject != NULL); } gulInstanceNumber++; ISOPERF_KdPrint(("exit ISOPERF_PnPAddDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS ISOPERF_CreateDeviceObject( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT *DeviceObject, LONG Instance ) /*++ Routine Description: Creates a Functional DeviceObject Arguments: DriverObject - pointer to the driver object for device DeviceObject - pointer to DeviceObject pointer to return created device object. Instance - instnace of the device create. Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise --*/ { NTSTATUS ntStatus; WCHAR deviceLinkBuffer[] = L"\\DosDevices\\ISOPERF-0"; UNICODE_STRING deviceLinkUnicodeString; WCHAR deviceNameBuffer[] = L"\\Device\\ISOPERF-0"; UNICODE_STRING deviceNameUnicodeString; PDEVICE_EXTENSION deviceExtension; int i; ISOPERF_KdPrint(("enter ISOPERF_CreateDeviceObject instance = %d\n", Instance)); /* // fix up device names based on Instance // // NOTE: Watch this space for potential changes to this approach in future revisions // of this sample driver. */ deviceLinkBuffer[19] = (USHORT) ('0' + Instance); deviceNameBuffer[15] = (USHORT) ('0' + Instance); ISOPERF_KdPrint(("Create Device name (%ws)\n", deviceNameBuffer)); RtlInitUnicodeString (&deviceNameUnicodeString, deviceNameBuffer); ntStatus = IoCreateDevice (DriverObject, sizeof (DEVICE_EXTENSION), &deviceNameUnicodeString, FILE_DEVICE_UNKNOWN, 0, FALSE, DeviceObject); if (NT_SUCCESS(ntStatus)) { RtlInitUnicodeString (&deviceLinkUnicodeString, deviceLinkBuffer); ISOPERF_KdPrint(("Create DosDevice name (%ws)\n", deviceLinkBuffer)); ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString, &deviceNameUnicodeString); // Initialize our device extension deviceExtension = (PDEVICE_EXTENSION) ((*DeviceObject)->DeviceExtension); RtlCopyMemory(deviceExtension->DeviceLinkNameBuffer, deviceLinkBuffer, sizeof(deviceLinkBuffer)); deviceExtension->ConfigurationHandle = NULL; deviceExtension->DeviceDescriptor = NULL; deviceExtension->NeedCleanup = TRUE; deviceExtension->Stopped = FALSE; //init to some huge number; this doesn't get decremented by this driver unless //the user has requested a stop (in the app) deviceExtension->ulCountDownToStop = 0xFFFFFFFF; // Initialize our interface for (i=0;iInterface[i] = NULL; }//for } ISOPERF_KdPrint(("exit ISOPERF_CreateDeviceObject (%x)\n", ntStatus)); return ntStatus; } VOID ISOPERF_Cleanup( PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Cleans up certain elements of the device object. This is called when the device is being removed from the system Arguments: DeviceObject - pointer to DeviceObject Return Value: None. --*/ { PDEVICE_EXTENSION deviceExtension; UNICODE_STRING deviceLinkUnicodeString; deviceExtension = DeviceObject->DeviceExtension; if (deviceExtension->NeedCleanup) { deviceExtension->NeedCleanup = FALSE; deviceExtension->StopTransfers = TRUE; //Let test thread know it should stop transfers deviceExtension->Stopped = TRUE; //Let everyone know this device is not working RtlInitUnicodeString (&deviceLinkUnicodeString, deviceExtension->DeviceLinkNameBuffer); IoDeleteSymbolicLink(&deviceLinkUnicodeString); } } NTSTATUS ISOPERF_CallUSBD( IN PDEVICE_OBJECT DeviceObject, IN PURB Urb ) /*++ Routine Description: Passes a Usb Request Block (URB) to the USB class driver (USBD) Note that we create our own IRP here and use it to send the request to the USB software subsystem. This means that this routine is essentially independent of the IRP that caused this driver to be called in the first place. The IRP for this transfer is created, used, and then destroyed in this routine. However, note that this routine uses the Usb Request Block (urb) passed in by the caller as the request block for the USB software stack. Implementation of this routine may be changed depending on the specific requirements of your driver. For example, while this routine issues a synchronous request to the USB stack, you may wish to implement this as an asynchronous request in which you set an IoCompletionRoutine to be called when the request is complete. Arguments: DeviceObject - pointer to the device object for this instance of an ISOPERF Device Urb - pointer to Urb request block Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise --*/ { NTSTATUS ntStatus, status = STATUS_SUCCESS; PDEVICE_EXTENSION deviceExtension; PIRP irp; KEVENT event; IO_STATUS_BLOCK ioStatus; PIO_STACK_LOCATION nextStack; ISOPERF_KdPrint (("enter ISOPERF_CallUSBD\n")); deviceExtension = DeviceObject->DeviceExtension; // issue a synchronous request (see notes above) KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest( IOCTL_INTERNAL_USB_SUBMIT_URB, deviceExtension->StackDeviceObject, NULL, 0, NULL, 0, TRUE, /* INTERNAL */ &event, &ioStatus); // Prepare for calling the USB driver stack nextStack = IoGetNextIrpStackLocation(irp); ASSERT(nextStack != NULL); // Set up the URB ptr to pass to the USB driver stack nextStack->Parameters.Others.Argument1 = Urb; ISOPERF_KdPrint (("Calling USB Driver Stack\n")); /* // Call the USB class driver to perform the operation. If the returned status // is PENDING, wait for the request to complete. */ ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, irp); ISOPERF_KdPrint (("return from IoCallDriver USBD %x\n", ntStatus)); if (ntStatus == STATUS_PENDING) { ISOPERF_KdPrint (("Wait for single object\n")); status = KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL); ISOPERF_KdPrint (("Wait for single object, returned %x\n", status)); } else { ioStatus.Status = ntStatus; } ISOPERF_KdPrint (("URB status = %x status = %x irp status %x\n", Urb->UrbHeader.Status, status, ioStatus.Status)); ntStatus = ioStatus.Status; ISOPERF_KdPrint(("exit ISOPERF_CallUSBD (%x)\n", ntStatus)); return ntStatus; } NTSTATUS ISOPERF_ConfigureDevice( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Configures the USB device via USB-specific device requests and interaction with the USB software subsystem. Arguments: DeviceObject - pointer to the device object for this instance of the ISOPERF Device Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus; PURB urb = NULL; ULONG siz; PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL; ISOPERF_KdPrint (("enter ISOPERF_ConfigureDevice\n")); deviceExtension = DeviceObject->DeviceExtension; // Get the config descriptor from the device // Get memory for the USB Request Block (urb) for the Getconfigdesc request. urb = ISOPERF_ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), &gulBytesAllocated); if (urb != NULL) { /* // Set size of the data buffer. Note we add padding to cover hardware faults // that may cause the device to go past the end of the data buffer */ siz = sizeof(USB_CONFIGURATION_DESCRIPTOR) + 16; // Get the nonpaged pool memory for the data buffer configurationDescriptor = ISOPERF_ExAllocatePool(NonPagedPool, siz, &gulBytesAllocated); if (configurationDescriptor != NULL) { UsbBuildGetDescriptorRequest(urb, (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, configurationDescriptor, NULL, sizeof (USB_CONFIGURATION_DESCRIPTOR),/* Get only the configuration descriptor */ NULL); ntStatus = ISOPERF_CallUSBD(DeviceObject, urb); if (NT_SUCCESS(ntStatus)) { ISOPERF_KdPrint (("Configuration Descriptor is at %x, bytes txferred: %d\n\ Configuration Descriptor Actual Length: %d\n", configurationDescriptor, urb->UrbControlDescriptorRequest.TransferBufferLength, configurationDescriptor->wTotalLength)); }//if } else { ntStatus = STATUS_NO_MEMORY; goto Exit_ISOPERFConfigureDevice; }//if-else // Determine how much data is in the entire configuration descriptor // and add extra room to protect against accidental overrun siz = configurationDescriptor->wTotalLength + 16; if (configurationDescriptor) { // Free up the data buffer memory just used ISOPERF_ExFreePool(configurationDescriptor, &gulBytesFreed); } configurationDescriptor = NULL; // Get nonpaged pool memory for the data buffer configurationDescriptor = ISOPERF_ExAllocatePool(NonPagedPool, siz, &gulBytesAllocated); // Now get the entire Configuration Descriptor if (configurationDescriptor != NULL) { UsbBuildGetDescriptorRequest(urb, (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, configurationDescriptor, NULL, siz, // Get all the descriptor data NULL); ntStatus = ISOPERF_CallUSBD(DeviceObject, urb); if (NT_SUCCESS(ntStatus)) { ISOPERF_KdPrint (("Entire Configuration Descriptor is at %x, bytes txferred: %d\n", configurationDescriptor, urb->UrbControlDescriptorRequest.TransferBufferLength)); } else { //Error in getting configuration descriptor goto Exit_ISOPERFConfigureDevice; }//else } else { // Failed getting data buffer (configurationDescriptor) memory ntStatus = STATUS_NO_MEMORY; goto Exit_ISOPERFConfigureDevice; }//if-else } else { // failed getting urb memory ntStatus = STATUS_NO_MEMORY; goto Exit_ISOPERFConfigureDevice; }//if-else /* // We have the configuration descriptor for the configuration // we want. // // Now we issue the SelectConfiguration command to get // the pipes associated with this configuration. */ if (configurationDescriptor) { // Get our pipes ntStatus = ISOPERF_SelectInterfaces(DeviceObject, configurationDescriptor, NULL // Device not yet configured ); } //if Exit_ISOPERFConfigureDevice: // Clean up and exit this routine if (urb != NULL) { ISOPERF_ExFreePool(urb, &gulBytesFreed); // Free urb memory }//if if (configurationDescriptor != NULL) { ISOPERF_ExFreePool(configurationDescriptor,&gulBytesFreed);// Free data buffer }//if ISOPERF_KdPrint (("exit ISOPERF_ConfigureDevice (%x)\n", ntStatus)); return ntStatus; }//ISOPERF_ConfigureDevice NTSTATUS ISOPERF_SelectInterfaces( IN PDEVICE_OBJECT DeviceObject, IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN PUSBD_INTERFACE_INFORMATION Interface ) /*++ Routine Description: Initializes an ISOPERF Device with multiple interfaces Note: This routine initializes some memory for the Interface objects in the Device Extension that must be freed when the device is torn down. So, we do NOT free that memory here, but rather we free it in the RemoveDevice code path. This note is intended to help anyone who is maintaining this code. --kjaff 12/20/96 Arguments: DeviceObject - pointer to the device object for this instance of the ISOPERF Device ConfigurationDescriptor - pointer to the USB configuration descriptor containing the interface and endpoint descriptors. Interface - pointer to a USBD Interface Information Object - If this is NULL, then this driver must choose its interface based on driver-specific criteria, and the driver must also CONFIGURE the device. - If it is NOT NULL, then the driver has already been given an interface and the device has already been configured by the parent of this device driver. Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus; PURB urb = NULL; ULONG siz, numberOfInterfaces, j, nInterfaceNumber, numberOfPipes; UCHAR alternateSetting, MyInterfaceNumber; PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor; PUSBD_INTERFACE_INFORMATION interfaceObject; ISOPERF_KdPrint (("enter ISOPERF_SelectInterfaces\n")); deviceExtension = DeviceObject->DeviceExtension; MyInterfaceNumber = SAMPLE_INTERFACE_NBR; if (Interface == NULL) { /* // This example driver only supports one interface. This can be extended // to be a dynamically allocated array by your driver. */ numberOfInterfaces = ConfigurationDescriptor->bNumInterfaces; ISOPERF_KdPrint (("Device has %d Interfaces\n",numberOfInterfaces)); numberOfPipes = 0; // Initialize to zero /* // We use alternate interface setting 0 for all interfaces */ alternateSetting = 0; /* // Call a USBD helper function that returns a ptr to a USB Interface Descriptor given // a USB Configuration Descriptor, an Inteface Number, and an Alternate Setting for that Interface */ interfaceDescriptor = USBD_ParseConfigurationDescriptor(ConfigurationDescriptor, MyInterfaceNumber, //interface number (this is bInterfaceNumber from interface descr) alternateSetting); ASSERT(interfaceDescriptor != NULL); if (interfaceDescriptor != NULL) { ISOPERF_KdPrint (("Device has %d Interface(s) | MyInterface (%d) is at: (%#X)\n", numberOfInterfaces, MyInterfaceNumber, interfaceDescriptor)); } /* if there was a valid interfacedesc */ numberOfPipes = ISOPERF_Internal_GetNumberOfPipes(ConfigurationDescriptor); ISOPERF_KdPrint (("config has %d pipes total on all the interfaces\n",numberOfPipes)); /* // Now that we have looked at the interface, we configure the device so that the remainder // of the USBD objects will come into existence (ie., pipes, etc.) as a result of the configuration, // thus completing the configuration process for the USB device. // // Allocate a URB big enough for this Select Configuration request // // NOTE: The new service USBD_CreateConfigurationRequest will replace some of the // code below. Future releases of this driver will demonstrate how to use // that service. // */ siz = GET_SELECT_CONFIGURATION_REQUEST_SIZE(numberOfInterfaces, numberOfPipes); ISOPERF_KdPrint (("size of config request Urb = %d for %d interfaces & %d pipes\n", siz, numberOfInterfaces, numberOfPipes)); urb = ISOPERF_ExAllocatePool(NonPagedPool, siz, &gulBytesAllocated); if (urb) { interfaceObject = (PUSBD_INTERFACE_INFORMATION) (&(urb->UrbSelectConfiguration.Interface)); // set up the input parameters in our interface request structure. interfaceObject->Length = GET_USBD_INTERFACE_SIZE(interfaceDescriptor->bNumEndpoints); ISOPERF_KdPrint (("size of interface request = %d\n", interfaceObject->Length)); ISOPERF_KdPrint (("Selecting interface Number: %d Alternate Setting: %d NumEndpoints %d\n", interfaceDescriptor->bInterfaceNumber, interfaceDescriptor->bAlternateSetting, interfaceDescriptor->bNumEndpoints)); interfaceObject->InterfaceNumber = interfaceDescriptor->bInterfaceNumber; interfaceObject->AlternateSetting = interfaceDescriptor->bAlternateSetting; interfaceObject->NumberOfPipes = interfaceDescriptor->bNumEndpoints; /* // We set up a default max transfer size for the endpoints. Your driver will // need to change this to reflect the capabilities of your device's endpoints. */ for (j=0; jbNumEndpoints; j++) { interfaceObject->Pipes[j].MaximumTransferSize = USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; } /* for */ ISOPERF_KdPrint (("InterfaceObj Inteface Nbr: %d | InterfaceObj AltSett: %d |NbrPip: %d\n", interfaceObject->InterfaceNumber, interfaceObject->AlternateSetting, interfaceObject->NumberOfPipes)); UsbBuildSelectConfigurationRequest(urb, (USHORT) siz, ConfigurationDescriptor); ntStatus = ISOPERF_CallUSBD(DeviceObject, urb); if (NT_SUCCESS(ntStatus) && USBD_SUCCESS(urb->UrbSelectConfiguration.Status)) { // Get mem for the config and stat space (initialize it later; we need it now since some // of the info in this struct depends on the endpoint number and we walk thru the ep nbrs below) deviceExtension->pConfig_Stat_Information= ISOPERF_ExAllocatePool( NonPagedPool, sizeof (Config_Stat_Info), &gulBytesAllocated); deviceExtension->dtTestDeviceType = Unknown_Device_Type; ISOPERF_KdPrint (("Trying to figure out what type of device (%x) this is...\n", DeviceObject)); // Figure out what type of device this is based on the class/subclass/protocol codes and store that // in the device extension variable that holds that information if (interfaceDescriptor->bInterfaceClass == USB_CLASS_CODE_TEST_CLASS_DEVICE) { ISOPERF_KdPrint (("This is a Test Class Device\n")); ISOPERF_KdPrint (("Checking Subclass Code (%#X)\n",interfaceDescriptor->bInterfaceSubClass)); if (interfaceDescriptor->bInterfaceSubClass == USB_SUBCLASS_CODE_TEST_CLASS_ISO) { ISOPERF_KdPrint (("This is an ISO Test SubClass Device\n")); ISOPERF_KdPrint (("Checking Protocol Code (%#X)\n",interfaceDescriptor->bInterfaceProtocol)); switch (interfaceDescriptor->bInterfaceProtocol) { case USB_PROTOCOL_CODE_TEST_CLASS_INPATTERN: case USB_PROTOCOL_CODE_TEST_CLASS_INPATTERN_ALT: ISOPERF_KdPrint (("This is an Iso_In_With_Pattern device\n")); deviceExtension->dtTestDeviceType = Iso_In_With_Pattern; break; case USB_PROTOCOL_CODE_TEST_CLASS_OUTINTERR: deviceExtension->dtTestDeviceType = Iso_Out_With_Interrupt_Feedback; ISOPERF_KdPrint (("This is an Iso_Out_With_Interrupt_Feedback device\n")); gMyOutputDevice = DeviceObject; //SHORT term workaround for wdm loading issue break; case USB_PROTOCOL_CODE_TEST_CLASS_OUTNOCHEK: deviceExtension->dtTestDeviceType = Iso_Out_Without_Feedback; ISOPERF_KdPrint (("This is an Iso_Out_WithOut_Feedback device\n")); gMyOutputDevice = DeviceObject; //SHORT term workaround for wdm loading issue break; default: deviceExtension->dtTestDeviceType = Unknown_Device_Type; ISOPERF_KdPrint (("This is an Unknown Protocol Iso test class device type\n")); break; }//switch on protocol code } else { ISOPERF_KdPrint (("This is not a Iso Test SubClass Device\n")); }//else not a test iso dev sub class code }else{ ISOPERF_KdPrint(("This is not a test class device\n")); }//else not a test class dev // For each of the interfaces on this device, save away the interface information structure // that USBD returned on the SelectConfiguration request for (nInterfaceNumber=0; nInterfaceNumber < numberOfInterfaces; nInterfaceNumber++) { // Save the configuration handle for this device deviceExtension->ConfigurationHandle = urb->UrbSelectConfiguration.ConfigurationHandle; deviceExtension->Interface[nInterfaceNumber] = ISOPERF_ExAllocatePool(NonPagedPool, interfaceObject->Length, &gulBytesAllocated); if (deviceExtension->Interface[nInterfaceNumber]) { // save a copy of the interfaceObject information returned RtlCopyMemory(deviceExtension->Interface[nInterfaceNumber], interfaceObject, interfaceObject->Length); // Dump the interfaceObject to the debugger ISOPERF_KdPrint (("---------\n")); ISOPERF_KdPrint (("NumberOfPipes 0x%x\n", deviceExtension->Interface[nInterfaceNumber]->NumberOfPipes)); ISOPERF_KdPrint (("Length 0x%x\n", deviceExtension->Interface[nInterfaceNumber]->Length)); ISOPERF_KdPrint (("Alt Setting 0x%x\n", deviceExtension->Interface[nInterfaceNumber]->AlternateSetting)); ISOPERF_KdPrint (("Interface Number 0x%x\n", deviceExtension->Interface[nInterfaceNumber]->InterfaceNumber)); // Dump the pipe info for (j=0; jNumberOfPipes; j++) { PUSBD_PIPE_INFORMATION pipeInformation; pipeInformation = &deviceExtension->Interface[nInterfaceNumber]->Pipes[j]; ISOPERF_KdPrint (("---------\n")); ISOPERF_KdPrint (("PipeType 0x%x\n", pipeInformation->PipeType)); ISOPERF_KdPrint (("EndpointAddress 0x%x\n", pipeInformation->EndpointAddress)); ISOPERF_KdPrint (("MaxPacketSize 0x%x\n", pipeInformation->MaximumPacketSize)); ISOPERF_KdPrint (("Interval 0x%x\n", pipeInformation->Interval)); ISOPERF_KdPrint (("Handle 0x%x\n", pipeInformation->PipeHandle)); ISOPERF_KdPrint (("MaximumTransferSize 0x%x\n", pipeInformation->MaximumTransferSize)); //NOTE: we only care about the first 2 endpoints on these devices (they must be the first 2 endpoints!) if ((USB_ENDPOINT_DIRECTION_IN(pipeInformation->EndpointAddress)) && (j<2)) { deviceExtension->pConfig_Stat_Information->ulMaxPacketSize_IN =pipeInformation->MaximumPacketSize; deviceExtension->pConfig_Stat_Information->ulMaxPacketSize_OUT =0; ISOPERF_KdPrint (("Endpoint %d is IN\n", j+1)); } else if ((USB_ENDPOINT_DIRECTION_OUT(pipeInformation->EndpointAddress)) && (j<2)) { deviceExtension->pConfig_Stat_Information->ulMaxPacketSize_OUT =pipeInformation->MaximumPacketSize; deviceExtension->pConfig_Stat_Information->ulMaxPacketSize_IN =0; ISOPERF_KdPrint (("Endpoint %d is OUT\n", j+1)); }//else it is an OUT endpoint }/* for all the pipes in this interface */ ISOPERF_KdPrint (("---------\n")); } /*If ExAllocate passed */ }/* for all the interfaces on this device */ // Init the rest of the config and stat space deviceExtension->pConfig_Stat_Information->ulNumberOfFrames =10; deviceExtension->pConfig_Stat_Information->ulMax_Urbs_Per_Pipe =4; deviceExtension->pConfig_Stat_Information->ulSuccessfulIrps =0; deviceExtension->pConfig_Stat_Information->ulUnSuccessfulIrps =0; deviceExtension->pConfig_Stat_Information->ulBytesTransferredIn =0; deviceExtension->pConfig_Stat_Information->ulBytesTransferredOut =0; deviceExtension->pConfig_Stat_Information->erError =NoError; deviceExtension->pConfig_Stat_Information->bStopped =0; deviceExtension->pConfig_Stat_Information->ulFrameOffset =200; deviceExtension->pConfig_Stat_Information->ulStartingFrameNumber =0; deviceExtension->pConfig_Stat_Information->ulFrameNumberAtStart =0; deviceExtension->pConfig_Stat_Information->UsbdPacketStatCode =0; deviceExtension->pConfig_Stat_Information->UrbStatusCode =0; deviceExtension->pConfig_Stat_Information->ulFrameOffsetMate =0; deviceExtension->pConfig_Stat_Information->bDeviceRunning =0; }/* if selectconfiguration request was successful */ else { ISOPERF_KdPrint (("Select Configuration Request Failed (urb status: %x)\n", urb->UrbSelectConfiguration.Status)); }/* else selectconfiguration request failed */ } else { ntStatus = STATUS_NO_MEMORY; }/* if urb alloc passed */ }//if Interface was not NULL // Clean up if (urb != NULL) { ISOPERF_ExFreePool (urb, &gulBytesFreed); }/* if valid Urb */ ISOPERF_KdPrint (("exit ISOPERF_SelectInterfaces (%x)\n", ntStatus)); return ntStatus; }/* ISOPERF_SelectInterfaces */ NTSTATUS ISOPERF_CallUSBDEx ( IN PDEVICE_OBJECT DeviceObject, IN PURB Urb, IN BOOLEAN fBlock, PIO_COMPLETION_ROUTINE CompletionRoutine, PVOID pvContext, BOOLEAN fWantTimeOut ) /************************************************************************** Routine Description: Passes a URB to the USBD class driver NOTE: Creates an IRP to do this. Doesn't use the IRP that is passed down from user mode app (it's not passed to this routine at all). Arguments: DeviceObject - pointer to the device object for this instance of a UTB Urb - pointer to Urb request block fBlock - bool indicating if this fn should wait for IRP to return from USBD CompletionRoutine - fn to set as the completionroutine for this transfer ONLY IF the fBlock is set to FALSE, indicating that the caller wants to handle completion on their own and this fn should not block pvContext - Context to be set in setting up the completion routine for the Irp created in this function. This is passed in by caller and is just a pass-thru to the IoSetCompletionRoutine call. If this is NULL but there is a CompletionRoutine specified, then the Irp that is created in this app is used as the context. fWantTimeOut - If caller wants this function to use a deadman timeout and cancel the Irp after the timeout expires. TRUE means this function will use the timeout mechanism, and FALSE means this function will block indefinitely and wait for the Irp/Urb to return from the USB stack. Return Value: STATUS_SUCCESS if successful, STATUS_UNSUCCESSFUL otherwise **************************************************************************/ { NTSTATUS ntStatus, status; PDEVICE_EXTENSION deviceExtension; PIRP irp; KEVENT event; PIO_STATUS_BLOCK ioStatus; PIO_STACK_LOCATION nextStack; BOOLEAN haveTimer=FALSE; LARGE_INTEGER dueTime; ISOPERF_KdPrint_MAXDEBUG (("enter ISOPERF_CallUSBDEx\n")); /* { KIRQL irql; irql = KeGetCurrentIrql(); ASSERT(irql <= PASSIVE_LEVEL); } */ deviceExtension = DeviceObject->DeviceExtension; ASSERT (deviceExtension != NULL); ASSERT ((deviceExtension->StackDeviceObject) != NULL); if ((deviceExtension) && (deviceExtension->StackDeviceObject)) { if (fBlock) { // issue a synchronous request to read the USB Device Ctrl pipe KeInitializeEvent(&event, NotificationEvent, FALSE); }/* if block requested */ // Create the memory for the Io Status Block ioStatus = ISOPERF_ExAllocatePool (NonPagedPool, sizeof (IO_STATUS_BLOCK), &gulBytesAllocated); // Create the IRP that we'll use to submit this URB to the USB stack irp = IoBuildDeviceIoControlRequest( IOCTL_INTERNAL_USB_SUBMIT_URB, deviceExtension->StackDeviceObject, NULL, 0, NULL, 0, TRUE, /* INTERNAL */ fBlock ? &event : NULL, ioStatus); //the status codes in NT IRPs go here // remove this Irp from the current thread's list RemoveEntryList(&irp->ThreadListEntry); // Call the class driver to perform the operation. If the returned status // is PENDING, wait for the request to complete. nextStack = IoGetNextIrpStackLocation(irp); ASSERT(nextStack != NULL); // pass the URB to the USBD 'class driver' nextStack->Parameters.Others.Argument1 = Urb; // If caller doesn't want this fn to block, then set the iocompletion routine up // for them if (fBlock == FALSE) { ASSERT (CompletionRoutine != NULL); ISOPERF_KdPrint_MAXDEBUG (("Setting compl routine\n")); // If the user didn't supply a context, then we'll use the irp as the context if (pvContext==NULL) { ISOPERF_KdPrint (("No context supplied...setting Irp as context\n")); pvContext = (PVOID)irp; } IoSetCompletionRoutine(irp, CompletionRoutine, pvContext, TRUE, //InvokeOnSuccess TRUE, //InvokeOnError, FALSE); //InvokeOnCancel }//if ISOPERF_KdPrint_MAXDEBUG (("calling USBD\n")); ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, irp); // Register that another Irp was put into the stack by incrementing a counter in the dev extension if (ntStatus == STATUS_PENDING) (deviceExtension->ulNumberOfOutstandingIrps)++; // If the Irp came back w/ SUCCESS, then it finished synchronously, so free the IoStatus Block if ((ntStatus == STATUS_SUCCESS) && (fBlock == FALSE)) { //Free the IoStatus block that we created for this call if (irp->UserIosb) { ISOPERF_ExFreePool (irp->UserIosb, &gulBytesFreed); }else{ //Bogus iostatus pointer. Bad. ISOPERF_KdPrint (("ERROR: Irp's IoStatus block is apparently NULL!\n")); TRAP(); }//else bad iostatus pointer }//if successful (sync) Irp ISOPERF_KdPrint_MAXDEBUG (("return from IoCallDriver USBD in ISOPERF_CallUSBDEx %x\n", ntStatus)); // After calling USBD only block if the caller requested a block if ((ntStatus == STATUS_PENDING) && (fBlock == TRUE)) { if (fWantTimeOut == TRUE) { // Setup timer stuff so we can timeout commands to unresponsive devices KeInitializeTimer(&(deviceExtension->TimeoutTimer)); //build the timer object KeInitializeDpc(&(deviceExtension->TimeoutDpc), //set up the DPC call based ISOPERF_SyncTimeoutDPC, //DPC func irp); //context ptr to pass into DPC func dueTime.QuadPart = -10000 * DEADMAN_TIMEOUT; //NOTE: (kjaff) KeSetTimer returns FALSE for unknown reason so we will hack for now // and pretend we were successful in setting the timer up KeSetTimer(&(deviceExtension->TimeoutTimer), //Set the timer params up dueTime, //This is how long to wait &(deviceExtension->TimeoutDpc)); //This is the DPC object we created haveTimer = TRUE; //our own local var to finger out if we } // if fWantTimeOut == TRUE ISOPERF_KdPrint_MAXDEBUG (("Waiting for single object\n")); status = KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL); // Since we waited on the Irp we can safely say it's not outstanding anymore if the Wait completes (deviceExtension -> ulNumberOfOutstandingIrps)--; ISOPERF_KdPrint_MAXDEBUG (("Wait for single object, returned %x\n", status)); // Now free the IoStatus block since we're done with this Irp (we blocked) if (irp->UserIosb) { ISOPERF_ExFreePool (irp->UserIosb, &gulBytesFreed); }else{ //Bogus iostatus pointer. Bad. ISOPERF_KdPrint (("ERROR: Irp's IoStatus block is apparently NULL!\n")); TRAP(); }//else bad iostatus pointer } //if pending and we want to block else { ISOPERF_KdPrint_MAXDEBUG (("Didn't block calling usbd\n")); ioStatus->Status = ntStatus; }//else we didn't want to block or we didn't get a status pending (it completed) ISOPERF_KdPrint_MAXDEBUG (("URB status = %x status = %x irp status %x\n", Urb->UrbHeader.Status, status, ioStatus->Status)); // USBD maps the error code for us ntStatus = ioStatus->Status; ISOPERF_KdPrint_MAXDEBUG(("URB TransferBufferLength OUT is: %d\n",Urb->UrbControlDescriptorRequest.TransferBufferLength)); if ((haveTimer==TRUE) && (ntStatus!=STATUS_CANCELLED)) { ISOPERF_KdPrint_MAXDEBUG (("Irp wasn't cancelled, so cancelling Timer (%x)\n", (&(deviceExtension->TimeoutTimer)))); KeCancelTimer(&(deviceExtension->TimeoutTimer)); ISOPERF_KdPrint_MAXDEBUG (("Done cancelling timer\n")); }//if }//if valid deviceExtension and StackDevObjects else { // Invalid extension or stackdevobj received ntStatus = STATUS_INVALID_PARAMETER; ISOPERF_KdPrint_MAXDEBUG (("Invalid deviceExtension or StackDeviceObject\n")); } //else invalid devExt or stackdevobj ISOPERF_KdPrint_MAXDEBUG(("exiting ISOPERF_CallUSBDEx w/ URB/ntStatus: %x\n", ntStatus)); return ntStatus; }//ISOPERF_CallUSBDEx VOID ISOPERF_SyncTimeoutDPC( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This routine runs at DISPATCH_LEVEL IRQL. Arguments: Dpc - Pointer to the DPC object. DeferredContext - passed in to IOS by caller as context (we use it as pIrp) SystemArgument1 - not used. SystemArgument2 - not used. Return Value: None. --*/ { BOOLEAN status; PIRP irp = DeferredContext; TRAP(); // The cancel Irp call below will return immediately, but that doesn't mean things are all // cleaned up in the USB stack. The only way to be assured of that is when the // WaitForSingleObject that blocked in the first place returns (due to the USB stack // completing the Irp, either due to normal completion or due to this cancel Irp call. status = IoCancelIrp(irp); //NOTE: (kosar) We don't do anything if the cancel fails, and we probably should. // (like maybe reschedule or something) return; } ULONG ISOPERF_Internal_GetNumberOfPipes( PUSB_CONFIGURATION_DESCRIPTOR pCD ) /*++ Routine Description: Tallies up the total number of pipes in a given configuration descriptor Arguments: Pointer to a Configuration Descriptor Return Value: Non-zero number indicating total number of pipes in the given configuration. A value of FFFFFFFF indicates an invalid Configuration Descriptor was received. --*/ { int nInterfaceNumber = 0; char * pcWorkerPtr = NULL; ULONG ulNumberOfPipes = 0; PUSB_INTERFACE_DESCRIPTOR pID = NULL; ISOPERF_KdPrint (("Enter GetNumberOfPipes (%X)\n",pCD)); //Check if the given descriptor is a valid config descriptor if (pCD->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) { ISOPERF_KdPrint (("GetNumberOfPipes got a bogus ConfDesc: (%X) (type=%d)\n",pCD,pCD->bDescriptorType)); return (0xFFFFFFFF); }//if bad config descriptor //Point to the interface descriptor pID = (PUSB_INTERFACE_DESCRIPTOR) ((((char*)pCD) + pCD->bLength)); for (nInterfaceNumber = 0; nInterfaceNumber < pCD->bNumInterfaces; nInterfaceNumber++) { //Check if this an Interface Descriptor if ((pID->bDescriptorType) != USB_INTERFACE_DESCRIPTOR_TYPE) { //NOTE right now we just give out and return an error. This should be fixed //to look for a class specific descriptor and try to step over it and go on to find //the interface descriptor, but since our 82930 boards will not have class descriptors //embedded in there for now, we'll just abort to safeguard against bogus descriptors. ISOPERF_KdPrint (("GetNumberOfPipes got a bogus InterfDesc: (%X) (type=%d)\n",pID,pID->bDescriptorType)); return (0xFFFFFFFF); }//if not an interface descriptor //Add to tally of number of pipes for this interface ulNumberOfPipes += pID->bNumEndpoints; //Go to next interface pcWorkerPtr = (char*)pID; pcWorkerPtr += (pID->bLength) + (pID->bNumEndpoints * sizeof (USB_ENDPOINT_DESCRIPTOR)); // Now you should be pointing to the next interface descriptor pID = (PUSB_INTERFACE_DESCRIPTOR) pcWorkerPtr; }// for all the interfaces on this config ISOPERF_KdPrint (("Exit GetNumberOfPipes: (%d)\n",ulNumberOfPipes)); return (ulNumberOfPipes); }//ISOPERF_Internal_GetNumberOfPipes #ifndef TOM_MEM PVOID ISOPERF_ExAllocatePool( IN POOL_TYPE PoolType, IN ULONG NumberOfBytes, IN PULONG pTallyOfBytesAllocated ) { PUCHAR pch; PULONG length; NumberOfBytes += sizeof(ULONG); if (pTallyOfBytesAllocated) *pTallyOfBytesAllocated += NumberOfBytes; pch = ExAllocatePool(PoolType, NumberOfBytes); if (pch) { length = (PULONG) pch; *length = NumberOfBytes; ISOPERF_KdPrint_MAXDEBUG (("Allocated %d bytes at %x | TotBytesAlloc: %d\n",NumberOfBytes, pch, *pTallyOfBytesAllocated)); return (pch+(sizeof(ULONG))); } else { return NULL; } } VOID ISOPERF_ExFreePool( IN PVOID P, IN PULONG pTallyOfBytesFreed ) { PUCHAR pch; ULONG length; pch = P; pch = pch-sizeof(ULONG); length = *(PULONG)(pch); if (pTallyOfBytesFreed) *pTallyOfBytesFreed += (length); ISOPERF_KdPrint_MAXDEBUG (("Freeing %d bytes at %x | TotBytesFreed %d\n",length, pch, *pTallyOfBytesFreed));; memset(pch, 0xff, length); ExFreePool(pch); } #endif #ifdef TOM_MEM #define MEM_SIGNATURE ((ULONG) 'CLLA') #define MEM_FREED_SIGNATURE ((ULONG) 'EERF') PVOID ISOPERF_ExAllocatePool( IN POOL_TYPE PoolType, IN ULONG NumberOfBytes, IN PULONG pTallyOfBytesAllocated ) { PULONG pMem; // allocate memory plus a little extra for our own use pMem = ExAllocatePool(PoolType, NumberOfBytes + (2 * sizeof(ULONG))); // see if we actually allocated any memory if(pMem) { // keep track of how much we allocated *pTallyOfBytesAllocated += NumberOfBytes; // store number of bytes allocated at start of memory allocated *pMem++ = NumberOfBytes; // now we are pointing at the memory allocated for caller // put signature word at end // get new pointer that points to end of buffer - ULONG pMem = (PULONG) (((PUCHAR) pMem) + NumberOfBytes); // write signature *pMem = MEM_SIGNATURE; // get back pointer to return to caller pMem = (PULONG) (((PUCHAR) pMem) - NumberOfBytes); } //debug only ISOPERF_KdPrint_MAXDEBUG (("Allocated %d bytes | returning pv = %x\n",NumberOfBytes,pMem)); //end debug only return (PVOID) pMem; } VOID ISOPERF_ExFreePool( IN PVOID P, IN PULONG pTallyOfBytesFreed ) { PULONG pTmp = (PULONG) P; ULONG buffSize; PULONG pSav=pTmp; // point at size ULONG at start of buffer, and address to free pTmp--; // get the size of memory allocated by caller buffSize = *pTmp; // point at signature and make sure it's O.K. ((PCHAR) P) += buffSize; if(*((PULONG) P) == MEM_SIGNATURE) { // let's go ahead and get rid of signature in case we get called // with this pointer again and memory is still paged in *((PULONG) P) = MEM_FREED_SIGNATURE; // adjust amount of memory allocated *pTallyOfBytesFreed += buffSize; // free real pointer ExFreePool(pTmp); ISOPERF_KdPrint_MAXDEBUG (("Freed %d bytes at %x (pvBuff: %x)\n",buffSize, pTmp, (((PUCHAR)pTmp)+sizeof(buffSize)) )); } else { //debug only ISOPERF_KdPrint (("ISOPERF_ExFreePool found incorrect mem signature: %x at %x (orig buff Ptr: %x)\n",*((PULONG)P), P, pSav)); ISOPERF_KdPrint (("BufferSize: %d\n",buffSize)); //end debug only TRAP(); }//else } #endif NTSTATUS ISOPERF_Create( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This is the Entry point for CreateFile calls from user mode apps (apps may open "\\.\ISOPERF-x\yyzz" where yy is the interface number and zz is the endpoint address). Here is where you would add code to create symbolic links between endpoints (i.e., pipes in USB software terminology) and User Mode file names. You are free to use any convention you wish to create these links, although the above convention offers a way to identify resources on a device by familiar file and directory structure nomenclature. Arguments: DeviceObject - pointer to the device object for this instance of the ISOPERF device Return Value: NT status code --*/ { NTSTATUS ntStatus; Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; ISOPERF_KdPrint_MAXDEBUG (("In ISOPERF_Create\n")); ntStatus = Irp->IoStatus.Status; IoCompleteRequest (Irp, IO_NO_INCREMENT ); ISOPERF_KdPrint_MAXDEBUG (("Exit ISOPERF_Create (%x)\n", ntStatus)); return ntStatus; }//ISOPERF_Create NTSTATUS ISOPERF_Close( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Entry point for CloseHandle calls from user mode apps to close handles they have opened Arguments: DeviceObject - pointer to the device object for this instance of the ISOPERF device Irp - pointer to an irp Return Value: NT status code --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; ISOPERF_KdPrint_MAXDEBUG (("In ISOPERF_Close\n")); IoCompleteRequest (Irp, IO_NO_INCREMENT ); ISOPERF_KdPrint_MAXDEBUG (("Exit ISOPERF_Close (%x)\n", ntStatus)); return ntStatus; }//ISOPERF_Close NTSTATUS ISOPERF_Read( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function is called for a IRP_MJ_READ. TODO: Add functionality here for your device driver if it handles that IRP code. Arguments: DeviceObject - pointer to the device object for this instance of the ISOPERF device. Irp - pointer to IRP Return Value: NT status code --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; return (ntStatus); } NTSTATUS ISOPERF_Write( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This function is called for a IRP_MJ_WRITE. TODO: Add functionality here for your device driver if it handles that IRP code. Arguments: DeviceObject - pointer to the device object for this instance of the ISOPERF device. Irp - pointer to IRP Return Value: NT status code --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; return (ntStatus); }