windows-nt/Source/XPSP1/NT/drivers/wdm/usb/driver/sample/sample.c
2020-09-26 16:20:57 +08:00

1897 lines
67 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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 <wdm.h>
#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; j<interfaceDescriptor->bNumEndpoints; 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; j<interfaceObject->NumberOfPipes; 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;
}