1897 lines
67 KiB
C
1897 lines
67 KiB
C
|
/*++
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
|