/*++ Copyright (c) 1996 Microsoft Corporation Copyright (c) 1996 Intel Corporation Module Name: Sample.c Abstract: USB device driver for Sample 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 Created 10-20-96: Version 1.1 Kosar Jaff Added changes to support new PnP IOCTLs 11-29-96: Version 1.2 Kosar Jaff Added support for IOCTLs from companion Sample Application 12-10-96: Version 1.3 Kosar Jaff Added Bulk Write/Read functions and corresponding IOCTLs Cleaned up device removal code 01-08-97: Version 1.4 Kosar Jaff Changed IoAttachDeviceByPointer (obsolete) to IoAttachDeviceToDeviceStack Cleaned up comments --*/ #define DRIVER /* // Include files needed for WDM driver support */ #include #include "stdarg.h" #include "stdio.h" /* // Include files needed for USB support */ #include "usbdi.h" #include "usbdlib.h" #include "usb.h" /* // Include file for the Sample Device */ #include "Sample.h" #define DEADMAN_TIMEOUT 5000 //timeout in ms; we use a 5 second timeout 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; Sample_KdPrint (("entering (Sample) DriverEntry (Build: %s/%s\n",__DATE__,__TIME__)); /* // Create dispatch points for the various events handled by this // driver. For example, device I/O control calls (e.g., when a Win32 // application calls the DeviceIoControl function) will be dispatched to // routine specified below in the IRP_MJ_DEVICE_CONTROL case. // // For more information about the IRP_XX_YYYY codes, please consult the // Windows NT DDK documentation. // */ DriverObject->MajorFunction[IRP_MJ_CREATE] = Sample_Create; DriverObject->MajorFunction[IRP_MJ_CLOSE] = Sample_Create; DriverObject->DriverUnload = Sample_Unload; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Sample_ProcessIOCTL; DriverObject->MajorFunction[IRP_MJ_PNP] = Sample_Dispatch; DriverObject->MajorFunction[IRP_MJ_POWER] = Sample_Dispatch; DriverObject->DriverExtension->AddDevice = Sample_PnPAddDevice; Sample_KdPrint (("exiting (Sample) DriverEntry (%x)\n", ntStatus)); return ntStatus; } NTSTATUS Sample_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. // */ Sample_KdPrint (("IRP_MJ_PNP\n")); switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: Sample_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)); Sample_KdPrint (("Passing START_DEVICE Irp down\n")); ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, Irp); Sample_KdPrint (("Back from passing START_DEVICE Irp down; status: %#X\n", ntStatus)); // Now we can begin our configuration actions on the device ntStatus = Sample_StartDevice(DeviceObject); break; //IRP_MN_START_DEVICE case IRP_MN_STOP_DEVICE: Sample_KdPrint (("IRP_MN_STOP_DEVICE\n")); Sample_Cleanup (DeviceObject); ntStatus = Sample_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. */ Sample_KdPrint (("IRP_MN_REMOVE_DEVICE\n")) Sample_Cleanup (DeviceObject); ntStatus = Sample_RemoveDevice(DeviceObject); /* // Delete the link to the Stack Device Object, and delete the // Functional Device Object we created */ IoDetachDevice(deviceExtension->StackDeviceObject); IoDeleteDevice (DeviceObject); break; //IRP_MN_REMOVE_DEVICE case IRP_MN_QUERY_STOP_DEVICE: Sample_KdPrint (("IRP_MN_QUERY_STOP_DEVICE\n")); break; case IRP_MN_QUERY_REMOVE_DEVICE: Sample_KdPrint (("IRP_MN_QUERY_REMOVE_DEVICE\n")); break; case IRP_MN_CANCEL_STOP_DEVICE: Sample_KdPrint (("IRP_MN_CANCEL_STOP_DEVICE\n")); break; case IRP_MN_CANCEL_REMOVE_DEVICE: Sample_KdPrint (("IRP_MN_CANCEL_REMOVE_DEVICE\n")); break; default: // A PnP Minor Function was not handled Sample_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 StackDeviceObject that // we were given in PnPAddDevice. // // This stack device object is managed by the USB software subsystem, // and so this IRP must be propagated to the owning device driver for // that stack device object, so that driver in turn can perform any // device state management (e.g., remove its device object, etc.). */ Sample_KdPrint (("Passing PnP Irp down, status = %x\n", ntStatus)); ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, Irp); /* // If lower layer driver marked the Irp as pending then reflect that by // calling IoMarkIrpPending. */ if (ntStatus == STATUS_PENDING) { IoMarkIrpPending(Irp); Sample_KdPrint (("PnP Irp came back with STATUS_PENDING (%x)\n", ntStatus)); } else { Sample_KdPrint (("PnP Irp came back, status = %x\n", ntStatus)); } // if ntStatus is PENDING goto Sample_Dispatch_Done; break; //IRP_MJ_PNP_POWER 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. // */ Sample_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: Sample_KdPrint (("IRP_MN_SET_D3\n")); break; case PowerDeviceD2: Sample_KdPrint (("IRP_MN_SET_D2\n")); break; case PowerDeviceD1: Sample_KdPrint (("IRP_MN_SET_D1\n")); break; case PowerDeviceD0: Sample_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: Sample_KdPrint (("IRP_MN_QUERY_D2\n")); break; case PowerDeviceD1: Sample_KdPrint (("IRP_MN_QUERY_D1\n")); break; case PowerDeviceD3: Sample_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 PnP Minor Function was not handled Sample_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 StackDeviceObject that // we were given in PnPAddDevice. // // This stack device object is managed by the USB software subsystem, // and so this IRP must be propagated to the owning device driver for // that stack device object, so that driver in turn can perform any // device state management (e.g., remove its device object, etc.). */ Sample_KdPrint (("Passing Power Irp down, status = %x\n", ntStatus)); ntStatus = IoCallDriver(deviceExtension->StackDeviceObject, Irp); /* // If lower layer driver marked the Irp as pending then reflect that by // calling IoMarkIrpPending. */ if (ntStatus == STATUS_PENDING) { IoMarkIrpPending(Irp); Sample_KdPrint (("Power Irp came back with STATUS_PENDING (%x)\n", ntStatus)); } else { Sample_KdPrint (("Power Irp came back, status = %x\n", ntStatus)); } // if ntStatus is PENDING goto Sample_Dispatch_Done; break; //IRP_MJ_POWER default: Sample_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 ); Sample_Dispatch_Done: Sample_KdPrint (("Exit Sample_Dispatch %x\n", ntStatus)); return ntStatus; }//Sample_Dispatch VOID Sample_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 --*/ { Sample_KdPrint (("enter Sample_Unload\n")); /* // TODO: Free any global resources allocated in DriverEntry */ Sample_KdPrint (("exit Sample_Unload\n")); } NTSTATUS Sample_StartDevice( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Initializes a given instance of the Sample 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 Sample Device Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus; PUSB_DEVICE_DESCRIPTOR deviceDescriptor = NULL; PURB urb; ULONG siz; Sample_KdPrint (("enter Sample_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 = ExAllocatePool( NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST)); if (urb) { siz = sizeof(USB_DEVICE_DESCRIPTOR); // Get some non paged memory for the device descriptor contents deviceDescriptor = ExAllocatePool(NonPagedPool, siz); 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 = Sample_CallUSBD(DeviceObject, urb); // Dump out the descriptor info to the debugger if (NT_SUCCESS(ntStatus)) { Sample_KdPrint (("Device Descriptor = %x, len %x\n", deviceDescriptor, urb->UrbControlDescriptorRequest.TransferBufferLength)); Sample_KdPrint (("Sample Device Descriptor:\n")); Sample_KdPrint (("-------------------------\n")); Sample_KdPrint (("bLength %d\n", deviceDescriptor->bLength)); Sample_KdPrint (("bDescriptorType 0x%x\n", deviceDescriptor->bDescriptorType)); Sample_KdPrint (("bcdUSB 0x%x\n", deviceDescriptor->bcdUSB)); Sample_KdPrint (("bDeviceClass 0x%x\n", deviceDescriptor->bDeviceClass)); Sample_KdPrint (("bDeviceSubClass 0x%x\n", deviceDescriptor->bDeviceSubClass)); Sample_KdPrint (("bDeviceProtocol 0x%x\n", deviceDescriptor->bDeviceProtocol)); Sample_KdPrint (("bMaxPacketSize0 0x%x\n", deviceDescriptor->bMaxPacketSize0)); Sample_KdPrint (("idVendor 0x%x\n", deviceDescriptor->idVendor)); Sample_KdPrint (("idProduct 0x%x\n", deviceDescriptor->idProduct)); Sample_KdPrint (("bcdDevice 0x%x\n", deviceDescriptor->bcdDevice)); Sample_KdPrint (("iManufacturer 0x%x\n", deviceDescriptor->iManufacturer)); Sample_KdPrint (("iProduct 0x%x\n", deviceDescriptor->iProduct)); Sample_KdPrint (("iSerialNumber 0x%x\n", deviceDescriptor->iSerialNumber)); Sample_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 "Sample_RemoveDevice" code. */ deviceExtension->DeviceDescriptor = deviceDescriptor; deviceExtension->Stopped = FALSE; } else if (deviceDescriptor) { /* // If the bus transaction failed, then free up the memory created to hold // the device descriptor, since the device is probably non-functional */ ExFreePool(deviceDescriptor); deviceExtension->DeviceDescriptor = NULL; } ExFreePool(urb); } else { // Failed getting memory for the Urb ntStatus = STATUS_NO_MEMORY; } // If the Get_Descriptor call was successful, then configure the device. if (NT_SUCCESS(ntStatus)) { ntStatus = Sample_ConfigureDevice(DeviceObject); } Sample_KdPrint (("exit Sample_StartDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS Sample_RemoveDevice( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Removes a given instance of a Sample Device device on the USB. Arguments: DeviceObject - pointer to the device object for this instance of a Sample Device Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus = STATUS_SUCCESS; Sample_KdPrint (("enter Sample_RemoveDevice\n")); deviceExtension = DeviceObject->DeviceExtension; if (deviceExtension->DeviceDescriptor) { ExFreePool(deviceExtension->DeviceDescriptor); } /* // Free up any interface structures in our device extension */ if (deviceExtension->Interface != NULL) { ExFreePool(deviceExtension->Interface); }//if Sample_KdPrint (("exit Sample_RemoveDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS Sample_StopDevice( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: Stops a given instance of a Sample Device device on USB. Arguments: DeviceObject - pointer to the device object for this instance of a Sample Device Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus = STATUS_SUCCESS; PURB urb; ULONG siz; Sample_KdPrint (("enter Sample_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 = ExAllocatePool(NonPagedPool, siz); if (urb) { NTSTATUS status; UsbBuildSelectConfigurationRequest(urb, (USHORT) siz, NULL); status = Sample_CallUSBD(DeviceObject, urb); Sample_KdPrint (("Device Configuration Closed status = %x usb status = %x.\n", status, urb->UrbHeader.Status)); ExFreePool(urb); } else { ntStatus = STATUS_NO_MEMORY; } Sample_KdPrint (("exit Sample_StopDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS Sample_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 Sample 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; Sample_KdPrint(("enter Sample_PnPAddDevice\n")); // create our functional device object (FDO) ntStatus = Sample_CreateDeviceObject(DriverObject, &deviceObject, 0); 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); } Sample_KdPrint(("exit Sample_PnPAddDevice (%x)\n", ntStatus)); return ntStatus; } NTSTATUS Sample_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\\Sample-0"; UNICODE_STRING deviceLinkUnicodeString; WCHAR deviceNameBuffer[] = L"\\Device\\Sample-0"; UNICODE_STRING deviceNameUnicodeString; PDEVICE_EXTENSION deviceExtension; STRING deviceName; Sample_KdPrint(("enter Sample_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); Sample_KdPrint(("Create Device name (%ws)\n", deviceNameBuffer)); RtlInitUnicodeString (&deviceNameUnicodeString, deviceNameBuffer); /* //Print out the unicode string //NOTE: We must first convert the string to Unicode due to a bug in the Debugger that does not allow // Unicode Strings to be printed to the debug device. */ deviceName.Buffer = NULL; ntStatus = RtlUnicodeStringToAnsiString (&deviceName, &deviceNameUnicodeString, TRUE); if (NT_SUCCESS(ntStatus)) { Sample_KdPrint(("Create Device Name (%s)\n", deviceName.Buffer)); RtlFreeAnsiString (&deviceName); } else { Sample_KdPrint(("Unicode to Ansi str failed w/ ntStatus: 0x%x\n",ntStatus)); } ntStatus = IoCreateDevice (DriverObject, sizeof (DEVICE_EXTENSION), &deviceNameUnicodeString, FILE_DEVICE_UNKNOWN, 0, FALSE, DeviceObject); if (NT_SUCCESS(ntStatus)) { RtlInitUnicodeString (&deviceLinkUnicodeString, deviceLinkBuffer); Sample_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 = FALSE; // Initialize our interface deviceExtension->Interface = NULL; } Sample_KdPrint(("exit Sample_CreateDeviceObject (%x)\n", ntStatus)); return ntStatus; } VOID Sample_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; RtlInitUnicodeString (&deviceLinkUnicodeString, deviceExtension->DeviceLinkNameBuffer); IoDeleteSymbolicLink(&deviceLinkUnicodeString); } } NTSTATUS Sample_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 Sample 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; Sample_KdPrint (("enter Sample_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; Sample_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); Sample_KdPrint (("return from IoCallDriver USBD %x\n", ntStatus)); if (ntStatus == STATUS_PENDING) { LARGE_INTEGER dueTime; Sample_KdPrint (("Wait for single object\n")); dueTime.QuadPart = -10000 * DEADMAN_TIMEOUT; ntStatus = KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, &dueTime); Sample_KdPrint (("Wait for single object, returned %x\n", status)); if(ntStatus == STATUS_TIMEOUT) { ioStatus.Status = ntStatus = STATUS_IO_TIMEOUT; ioStatus.Information = 0; // We've waited long enough and we're not going to take // it anymore...cancel it IoCancelIrp(irp); // Wait for the stack to signal the _real_ completion of the cancel // Note we wait forever here, so we depend on the USB stack to signal this // event when it completes the cancelled Irp. Gosh, I hope this works. (kjaff 4-16-97) ntStatus = KeWaitForSingleObject( &event, Suspended, KernelMode, FALSE, NULL); Sample_KdPrint (("Wait for single object, returned %x\n", status)); } } else { ioStatus.Status = ntStatus; } Sample_KdPrint (("URB status = %x status = %x irp status %x\n", Urb->UrbHeader.Status, status, ioStatus.Status)); /* // USBD maps the error code for us. USBD uses error codes in its URB // structure that are more insightful into USB behavior. In order to // match the NT Status codes, USBD maps its error codes into more general NT // error categories so higher level drivers can decipher the error codes // based on standard NT error code definitions. To allow more insight into // the specific USB error that occurred, your driver may wish to examine the // URB's status code (Urb->UrbHeader.Status) as well. */ ntStatus = ioStatus.Status; Sample_KdPrint(("exit Sample_CallUSBD (%x)\n", ntStatus)); return ntStatus; } NTSTATUS Sample_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 Sample Device Return Value: NT status code --*/ { PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus; PURB urb = NULL; ULONG siz; PUSB_CONFIGURATION_DESCRIPTOR configurationDescriptor = NULL; Sample_KdPrint (("enter Sample_ConfigureDevice\n")); deviceExtension = DeviceObject->DeviceExtension; /* // Get memory for the USB Request Block (urb). */ urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST)); 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 = ExAllocatePool(NonPagedPool, siz); 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 = Sample_CallUSBD(DeviceObject, urb); if (NT_SUCCESS(ntStatus)) { Sample_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_SampleConfigureDevice; }//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; // Free up the data buffer memory just used ExFreePool(configurationDescriptor); configurationDescriptor = NULL; // Get nonpaged pool memory for the data buffer configurationDescriptor = ExAllocatePool(NonPagedPool, siz); // 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 = Sample_CallUSBD(DeviceObject, urb); if (NT_SUCCESS(ntStatus)) { Sample_KdPrint (("Entire Configuration Descriptor is at %x, bytes txferred: %d\n", configurationDescriptor, urb->UrbControlDescriptorRequest.TransferBufferLength)); } else { //Error in getting configuration descriptor goto Exit_SampleConfigureDevice; }//else } else { // Failed getting data buffer (configurationDescriptor) memory ntStatus = STATUS_NO_MEMORY; goto Exit_SampleConfigureDevice; }//if-else } else { // failed getting urb memory ntStatus = STATUS_NO_MEMORY; goto Exit_SampleConfigureDevice; }//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 = Sample_SelectInterfaces(DeviceObject, configurationDescriptor, NULL // Device not yet configured ); } //if Exit_SampleConfigureDevice: // Clean up and exit this routine if (urb != NULL) { ExFreePool(urb); // Free urb memory }//if if (configurationDescriptor != NULL) { ExFreePool(configurationDescriptor);// Free data buffer }//if Sample_KdPrint (("exit Sample_ConfigureDevice (%x)\n", ntStatus)); return ntStatus; }//Sample_ConfigureDevice NTSTATUS Sample_SelectInterfaces( IN PDEVICE_OBJECT DeviceObject, IN PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor, IN PUSBD_INTERFACE_INFORMATION Interface ) /*++ Routine Description: Initializes an Sample Device with multiple interfaces Arguments: DeviceObject - pointer to the device object for this instance of the Sample 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; ULONG siz, numberOfInterfaces, j; UCHAR numberOfPipes, alternateSetting, MyInterfaceNumber; PUSB_INTERFACE_DESCRIPTOR interfaceDescriptor; PUSBD_INTERFACE_INFORMATION interfaceObject; Sample_KdPrint (("enter Sample_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; Sample_KdPrint (("Device has %d Interfaces\n",numberOfInterfaces)); numberOfInterfaces =1; // Fixed for this sample driver in this revision numberOfPipes = 0; // Initialize to zero /* // We use alternate interface setting 0 for all interfaces // NOTE: This is a simplification and is due to change in future releases of this driver. If // your driver supports alternate settings, you will have to do more work to switch between // alternate settings. */ alternateSetting = 0; /* // Call a USBD helper function that returns a ptr to a USB Interface Descriptor given // a USB Configuration Descriptor, an Interface 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) { Sample_KdPrint (("Device has %d Interface(s) | MyInterface (%d) is at: (%#X)\n", numberOfInterfaces, MyInterfaceNumber, interfaceDescriptor)); } /* if there was a valid interfacedesc */ /* Add to the tally of pipes in this configuration */ numberOfPipes += interfaceDescriptor->bNumEndpoints; Sample_KdPrint (("Interface has %d endpoints\n", interfaceDescriptor->bNumEndpoints)); /* // 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 // This example driver supports only 1 interface // // 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); Sample_KdPrint (("size of config request Urb = %d\n", siz)); urb = ExAllocatePool(NonPagedPool, siz); if (urb) { interfaceObject = (PUSBD_INTERFACE_INFORMATION) (&(urb->UrbSelectConfiguration.Interface)); Sample_KdPrint (("urb.Interface=%#X\n", &(urb->UrbSelectConfiguration.Interface))); // set up the input parameters in our interface request structure. interfaceObject->Length = GET_USBD_INTERFACE_SIZE(interfaceDescriptor->bNumEndpoints); Sample_KdPrint (("size of interface request = %d\n", interfaceObject->Length)); 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 */ Sample_KdPrint (("InterfaceObj Inteface Nbr: %d | InterfaceObj AltSett: %d |NbrPip: %d\n", interfaceObject->InterfaceNumber, interfaceObject->AlternateSetting, interfaceObject->NumberOfPipes)); UsbBuildSelectConfigurationRequest(urb, (USHORT) siz, ConfigurationDescriptor); ntStatus = Sample_CallUSBD(DeviceObject, urb); if (NT_SUCCESS(ntStatus) && USBD_SUCCESS(urb->UrbSelectConfiguration.Status)) { // Save the configuration handle for this device deviceExtension->ConfigurationHandle = urb->UrbSelectConfiguration.ConfigurationHandle; deviceExtension->Interface = ExAllocatePool(NonPagedPool, interfaceObject->Length); if (deviceExtension->Interface) { // save a copy of the interfaceObject information returned RtlCopyMemory(deviceExtension->Interface, interfaceObject, interfaceObject->Length); // Dump the interfaceObject to the debugger Sample_KdPrint (("---------\n")); Sample_KdPrint (("NumberOfPipes 0x%x\n", deviceExtension->Interface->NumberOfPipes)); Sample_KdPrint (("Length 0x%x\n", deviceExtension->Interface->Length)); Sample_KdPrint (("Alt Setting 0x%x\n", deviceExtension->Interface->AlternateSetting)); Sample_KdPrint (("Interface Number 0x%x\n", deviceExtension->Interface->InterfaceNumber)); // Dump the pipe info for (j=0; jNumberOfPipes; j++) { PUSBD_PIPE_INFORMATION pipeInformation; pipeInformation = &deviceExtension->Interface->Pipes[j]; Sample_KdPrint (("---------\n")); Sample_KdPrint (("PipeType 0x%x\n", pipeInformation->PipeType)); Sample_KdPrint (("EndpointAddress 0x%x\n", pipeInformation->EndpointAddress)); Sample_KdPrint (("MaxPacketSize 0x%x\n", pipeInformation->MaximumPacketSize)); Sample_KdPrint (("Interval 0x%x\n", pipeInformation->Interval)); Sample_KdPrint (("Handle 0x%x\n", pipeInformation->PipeHandle)); Sample_KdPrint (("MaximumTransferSize 0x%x\n", pipeInformation->MaximumTransferSize)); }/* for all the pipes in this interface */ Sample_KdPrint (("---------\n")); } /*If ExAllocate passed */ }/* if selectconfiguration request was successful */ } else { ntStatus = STATUS_NO_MEMORY; }/* if urb alloc passed */ }//if Interface was not NULL Sample_KdPrint (("exit Sample_SelectInterfaces (%x)\n", ntStatus)); return ntStatus; }/* Sample_SelectInterfaces */ NTSTATUS Sample_Read_Write( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN BOOLEAN Read ) /*++ Routine Description: This function is called for IOCTLs to Read or Write bulk or interrupt endpoints. By agreement between the driver and application, the endpoint on which the READ or WRITE is to be performed is supplied by the caller (the User-Mode application that performed the IOCTL puts this value in the buffer supplied in the DeviceIoControl call). This number is an INDEX into an array of pipes, which are ordered exactly like the order of the endpoints as they appear in the device's configuration descriptor. The endpoint list is ordered as the endpoint descriptors appear in the device. The list is a zero-index based ordered list. That is, when the application specifies: Endpoint index 0 = the endpoint described by the FIRST endpoint descriptor in the configuration descriptor Endpoint index 1 = the endpoint described by the SECOND endpoint descriptor in the configuration descriptor etc. Also by agreement between this driver and the application, this INDEX is specified in the first DWORD (32-bits) of the input buffer. Note that more sophisticated structures can be passed between the application and the driver to communicate the desired operation. For READs, this data received from the device is put into the SystemBuffer. This routine sets the "information" field in the Irp to tell the IOS to how many bytes to copy back to user space (in the user's lpOutputBuffer). The actual bytes copied back is reflected in the lpBytesReturned field in the DeviceIoControl() call, which the application can examine. For WRITEs, the data is retrieved from the SystemBuffer and sent to the device. Arguments: DeviceObject - pointer to the device object for this instance of the Sample device. Irp - pointer to IRP Read - if TRUE this is a Device-to-Host (Read from device) transfer if FALSE this is a Host-to-Device (Write to device) transfer Return Value: NT status code STATUS_SUCCESS: Read was done successfully STATUS_INVALID_PARAMETER_3: The Endpoint Index does not specify an IN pipe STATUS_NO_MEMORY: Insufficient data memory was supplied to perform the READ This routine fills the status code into the Irp --*/ { USBD_INTERFACE_INFORMATION * pInterfaceInfo; USBD_PIPE_INFORMATION * pPipeInfo; PIO_STACK_LOCATION irpStack; PDEVICE_EXTENSION deviceExtension; NTSTATUS ntStatus; PVOID ioBuffer; PCHAR pcTempBuffer; ULONG length; PULONG pPipeNum; ULONG inputBufferLength; ULONG outputBufferLength; ULONG siz; PURB urb; Sample_KdPrint(("enter READ\n")); irpStack = IoGetCurrentIrpStackLocation (Irp); ASSERT (irpStack != NULL); deviceExtension = DeviceObject->DeviceExtension; ASSERT (deviceExtension != NULL); ioBuffer = Irp->AssociatedIrp.SystemBuffer; inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; //DEBUG ONLY if ((ioBuffer == NULL) || (inputBufferLength == 0) || (outputBufferLength==0)) { Sample_KdPrint (("ERROR ioBuffer %X | inBufLen: %d | outBufLen %d\n", ioBuffer, inputBufferLength, outputBufferLength)); Irp->IoStatus.Information = 0; return (STATUS_NO_MEMORY); } //DEBUG ONLY pInterfaceInfo = deviceExtension->Interface; ASSERT (pInterfaceInfo != NULL); pPipeNum = (PULONG) ioBuffer; ASSERT (*pPipeNum <= pInterfaceInfo->NumberOfPipes); pPipeInfo = &(pInterfaceInfo->Pipes[*pPipeNum]); ASSERT (pPipeInfo != NULL); ASSERT ((pPipeInfo->PipeHandle) != NULL); Sample_KdPrint (("PipeNum: %d | ioBuffer %X | inBufLen: %d | outBufLen %d\n", *pPipeNum, ioBuffer, inputBufferLength, outputBufferLength)); siz = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); // allocate urb urb = ExAllocatePool(NonPagedPool, siz); // By convention, the first dword of the buffer is reserved for the pipe index, so // skip over the first dword when specifying the transferbuffer pcTempBuffer = (((char*)ioBuffer) + sizeof (ULONG)); if (Read==TRUE) { // A READ operation implies that the data is placed in the User's Output Data Buffer length = outputBufferLength - sizeof(ULONG); } else { // A WRITE operation implies that the data is gotten from the User's Input Data Buffer length = inputBufferLength - sizeof(ULONG); }/* else */ // set up urb UsbBuildInterruptOrBulkTransferRequest(urb, //ptr to urb (USHORT) siz, //siz of urb pPipeInfo->PipeHandle, //usbd pipe handle pcTempBuffer, //TransferBuffer NULL, //mdl (unused) length, //bufferlength USBD_SHORT_TRANSFER_OK, //flags NULL); //link // call the usb stack ntStatus = Sample_CallUSBD(DeviceObject, urb); // The Information field tells IOM how much to copy back into the // usermode buffer in the BUFFERED method if (NT_SUCCESS(ntStatus) && Read==TRUE) { Sample_KdPrint (("Sucessfully Transferred %d Bytes\n", urb->UrbBulkOrInterruptTransfer.TransferBufferLength)); // We fill in the actual length transferred in the first DWORD (this overwrites the pipe number) *((PULONG)ioBuffer) = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; // Tell the IOS to copy whatever the device returned, as well as the first DWORD that was skipped over Irp->IoStatus.Information = (urb->UrbBulkOrInterruptTransfer.TransferBufferLength + sizeof(ULONG)); ASSERT (Irp->IoStatus.Information <= outputBufferLength); }else if (NT_SUCCESS(ntStatus) && Read==FALSE) { // We fill in the actual length transferred in the first DWORD (this overwrites the pipe number) *((PULONG)ioBuffer) = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; // Tell the IOS to copy whatever the device returned, as well as the first DWORD that was skipped over Irp->IoStatus.Information = sizeof (ULONG); ASSERT (Irp->IoStatus.Information <= outputBufferLength); }/* else */ Irp->IoStatus.Status = ntStatus; // free allocated urb ExFreePool(urb); Sample_KdPrint(("exit READ\n")); return (ntStatus); } NTSTATUS Sample_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 "\\.\Sample-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 Sample device Return Value: NT status code --*/ { NTSTATUS ntStatus; Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; // Create all the symbolic links here ntStatus = Irp->IoStatus.Status; IoCompleteRequest (Irp, IO_NO_INCREMENT ); return ntStatus; }//Sample_Create NTSTATUS Sample_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 Sample device Irp - pointer to an irp Return Value: NT status code --*/ { NTSTATUS ntStatus = STATUS_SUCCESS; return ntStatus; }//Sample_Close NTSTATUS Sample_ProcessIOCTL( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This where all the DeviceIoControl codes are handled. You can add more code here to handle IOCTL codes that are specific to your device driver. Arguments: DeviceObject - pointer to the device object for this instance of the Sample device. Return Value: NT status code --*/ { PIO_STACK_LOCATION irpStack; PVOID ioBuffer; ULONG inputBufferLength; ULONG outputBufferLength; PDEVICE_EXTENSION deviceExtension; ULONG ioControlCode; NTSTATUS ntStatus; ULONG length; PUCHAR pch; Sample_KdPrint (("IRP_MJ_DEVICE_CONTROL\n")); /* // Get a pointer to the current location in the Irp. This is where // the function codes and parameters are located. */ irpStack = IoGetCurrentIrpStackLocation (Irp); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; /* // Get a pointer to the device extension */ deviceExtension = DeviceObject->DeviceExtension; ioBuffer = Irp->AssociatedIrp.SystemBuffer; inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; /* // Handle Ioctls from User mode */ switch (ioControlCode) { case IOCTL_Sample_GET_PIPE_INFO: /* // inputs - none // outputs - we copy the interface information structure that we have // stored in our device extension area to the output buffer which // will be reflected to the user mode application by the IOS. */ length = 0; pch = (PUCHAR) ioBuffer; if (deviceExtension->Interface) { RtlCopyMemory(pch+length, (PUCHAR) deviceExtension->Interface, deviceExtension->Interface->Length); length += deviceExtension->Interface->Length; } /* if */ Irp->IoStatus.Information = length; Irp->IoStatus.Status = STATUS_SUCCESS; break; case IOCTL_Sample_GET_DEVICE_DESCRIPTOR: /* // inputs - pointer to a buffer in which to place descriptor data // outputs - we put the device descriptor data, if any is returned by the device // in the system buffer and then we set the length inthe Information field // in the Irp, which will then cause the system to copy the buffer back // to the user's buffer */ length = Sample_GetDeviceDescriptor (DeviceObject, ioBuffer); Sample_KdPrint(("Get Device Descriptor returned %d bytes\n", length)); Irp->IoStatus.Information = length; Irp->IoStatus.Status = STATUS_SUCCESS; break; case IOCTL_Sample_GET_CONFIGURATION_DESCRIPTOR: /* // inputs - pointer to a buffer in which to place descriptor data // outputs - we put the configuration descriptor data, if any is returned by the device // in the system buffer and then we set the length in the Information field // in the Irp, which will then cause the system to copy the buffer back // to the user's buffer */ length = Sample_GetConfigDescriptor (DeviceObject, ioBuffer, outputBufferLength); Sample_KdPrint(("Get Config Descriptor returned %d bytes\n", length)); Irp->IoStatus.Information = length; Irp->IoStatus.Status = STATUS_SUCCESS; break; case IOCTL_Sample_BULK_OR_INTERRUPT_WRITE: /* // inputs - pointer to the parameter block for this IOCTL. // // outputs - A status field is filled in the header of the parameter block. // // assumptions: // When we configured the device, we made a list of pipes to represent the endpoints // on the device, in the order that the endpoint descriptors appeared in the configuration // descriptor. In this IOCTL, the application specifies the offset into that list of // pipes (this is an array, so it's zero-based indexing) and that is the pipe we will // use for this transfer. So, if the device has an OUTPUT endpoint as the second endpoint // descriptor (ie., offset = 1 in the array) the app would specify a "1" in the pipe offset // parameter for this IOCTL. // // NOTE: It is currently ILLEGAL to perform interrupt OUT transfers on USB. This IOCTL is named as it is // just for symmetry with its IN counterpart's name. */ if ((ioBuffer) && (inputBufferLength>=0) && (outputBufferLength>=0)) { Sample_KdPrint (("IOCTL_Sample_BULK_OR_INTERRUPT_WRITE\n")); Sample_Read_Write (DeviceObject, Irp, FALSE); }else { Sample_KdPrint (("IOCTL_Sample_BULK_OR_INTERRUPT_WRITE got INVALID buffer(s)!\n")); Sample_KdPrint (("ioBuffer: %x | inputBufferLength: %d | outputBufferLength: %d\n", ioBuffer, inputBufferLength, outputBufferLength)); }/*else bad pointer(s) received */ break; case IOCTL_Sample_BULK_OR_INTERRUPT_READ: /* // inputs - pointer to the parameter block for this IOCTL. // // outputs - A status field is filled in the header of the parameter block. The data // returned from the device is copied into the data buffer section of the parameter block. // // assumptions: // When we configured the device, we made a list of pipes to represent the endpoints // on the device, in the order that the endpoint descriptors appeared in the configuration // descriptor. In this IOCTL, the application specifies the offset into that list of // pipes (this is an array, so it's zero-based indexing) and that is the pipe we will // use for this transfer. So, if the device has an INPUT endpoint as the first endpoint // descriptor (ie., offset = 0 in the array) the app would specify a "0" in the pipe offset // parameter for this IOCTL. */ if ((ioBuffer) && (inputBufferLength>=0) && (outputBufferLength>=0)) { Sample_KdPrint (("IOCTL_Sample_BULK_OR_INTERRUPT_READ\n")); Sample_Read_Write (DeviceObject, Irp, TRUE); }else { Sample_KdPrint (("IOCTL_Sample_BULK_OR_INTERRUPT_READ got INVALID buffer(s)!\n")); Sample_KdPrint (("ioBuffer: %x | inputBufferLength: %d | outputBufferLength: %d\n", ioBuffer, inputBufferLength, outputBufferLength)); }/*else bad pointer(s) received */ break; default: Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; }/* switch on ioControlCode */ ntStatus = Irp->IoStatus.Status; IoCompleteRequest (Irp, IO_NO_INCREMENT ); return ntStatus; } ULONG Sample_GetDeviceDescriptor( IN PDEVICE_OBJECT DeviceObject, PVOID pvOutputBuffer ) /*++ Routine Description: Gets a device descriptor from the given device object Arguments: DeviceObject - pointer to the sample device object Return Value: Number of valid bytes in data buffer --*/ { PDEVICE_EXTENSION deviceExtension = NULL; NTSTATUS ntStatus = STATUS_SUCCESS; PURB urb = NULL; ULONG length = 0; Sample_KdPrint (("Enter Sample_GetDeviceDescriptor\n")); deviceExtension = DeviceObject->DeviceExtension; urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST)); if (urb) { if (pvOutputBuffer) { UsbBuildGetDescriptorRequest(urb, (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_DEVICE_DESCRIPTOR_TYPE, //descriptor type 0, //index 0, //language ID pvOutputBuffer, //transfer buffer NULL, //MDL sizeof(USB_DEVICE_DESCRIPTOR), //buffer length NULL); //link ntStatus = Sample_CallUSBD(DeviceObject, urb); } else { ntStatus = STATUS_NO_MEMORY; } // Get the length from the Urb length = urb->UrbControlDescriptorRequest.TransferBufferLength; Sample_KdPrint (("%d bytes of dev descriptor received\n",length)); ExFreePool(urb); } else { ntStatus = STATUS_NO_MEMORY; } Sample_KdPrint (("Leaving Sample_GetDeviceDescriptor\n")); return length; } ULONG Sample_GetConfigDescriptor( IN PDEVICE_OBJECT DeviceObject, PVOID pvOutputBuffer, ULONG ulLength ) /*++ Routine Description: Gets a configuration descriptor from the given device object Arguments: DeviceObject - pointer to the sample device object pvOutputBuffer - pointer to the buffer where the data is to be placed ulLength - length of the buffer Return Value: Number of valid bytes in data buffer --*/ { PDEVICE_EXTENSION deviceExtension = NULL; NTSTATUS ntStatus = STATUS_SUCCESS; PURB urb = NULL; ULONG length = 0; Sample_KdPrint (("Enter Sample_GetConfigurationDescriptor\n")); deviceExtension = DeviceObject->DeviceExtension; urb = ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST)); if (urb) { if (pvOutputBuffer) { UsbBuildGetDescriptorRequest(urb, (USHORT) sizeof (struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, //descriptor type 0, //index 0, //language ID pvOutputBuffer, //transfer buffer NULL, //MDL ulLength, //buffer length NULL); //link ntStatus = Sample_CallUSBD(DeviceObject, urb); } else { ntStatus = STATUS_NO_MEMORY; } // Get the length from the Urb length = urb->UrbControlDescriptorRequest.TransferBufferLength; Sample_KdPrint (("%d bytes of cfg descriptor received\n",length)); ExFreePool(urb); } else { ntStatus = STATUS_NO_MEMORY; } Sample_KdPrint (("Leaving Sample_GetConfigurationDescriptor\n")); return length; }