Sample Code
Windows Driver Samples/ NONPNP/ C++/ sys/ nonpnp.c/
/*++ Copyright (c) Microsoft Corporation. All rights reserved. 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. Module Name: nonpnp.c Abstract: Purpose of this driver is to demonstrate how to write a legacy (NON WDM) driver using framework, show how to handle 4 different ioctls - METHOD_NEITHER - in particular and also show how to read & write to file from KernelMode using Zw functions. For a non-framework version of sample on how to handle IOCTLs in driver, study src\general\IOCTL in the DDK. Environment: Kernel mode only. --*/ #include "nonpnp.h" // // The trace message header file must be included in a source file // before any WPP macro calls and after defining a WPP_CONTROL_GUIDS // macro. During the compilation, WPP scans the source files for // TraceEvents() calls and builds a .tmh file which stores a unique // data GUID for each message, the text resource string for each message, // and the data types of the variables passed in for each message. // This file is automatically generated and used during post-processing. // #include "nonpnp.tmh" #ifdef ALLOC_PRAGMA #pragma alloc_text( INIT, DriverEntry ) #pragma alloc_text( PAGE, NonPnpDeviceAdd) #pragma alloc_text( PAGE, NonPnpEvtDriverContextCleanup) #pragma alloc_text( PAGE, NonPnpEvtDriverUnload) #pragma alloc_text( PAGE, NonPnpEvtDeviceIoInCallerContext) #pragma alloc_text( PAGE, NonPnpEvtDeviceFileCreate) #pragma alloc_text( PAGE, NonPnpEvtFileClose) #pragma alloc_text( PAGE, FileEvtIoRead) #pragma alloc_text( PAGE, FileEvtIoWrite) #pragma alloc_text( PAGE, FileEvtIoDeviceControl) #endif // ALLOC_PRAGMA NTSTATUS DriverEntry( IN OUT PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine is called by the Operating System to initialize the driver. It creates the device object, fills in the dispatch entry points and completes the initialization. Arguments: DriverObject - a pointer to the object that represents this device driver. RegistryPath - a pointer to our Services key in the registry. Return Value: STATUS_SUCCESS if initialized; an error otherwise. --*/ { NTSTATUS status; WDF_DRIVER_CONFIG config; WDFDRIVER hDriver; PWDFDEVICE_INIT pInit = NULL; WDF_OBJECT_ATTRIBUTES attributes; KdPrint(("Driver Frameworks NONPNP Legacy Driver Example\n")); WDF_DRIVER_CONFIG_INIT( &config, WDF_NO_EVENT_CALLBACK // This is a non-pnp driver. ); // // Tell the framework that this is non-pnp driver so that it doesn't // set the default AddDevice routine. // config.DriverInitFlags |= WdfDriverInitNonPnpDriver; // // NonPnp driver must explicitly register an unload routine for // the driver to be unloaded. // config.EvtDriverUnload = NonPnpEvtDriverUnload; // // Register a cleanup callback so that we can call WPP_CLEANUP when // the framework driver object is deleted during driver unload. // WDF_OBJECT_ATTRIBUTES_INIT(&attributes); attributes.EvtCleanupCallback = NonPnpEvtDriverContextCleanup; // // Create a framework driver object to represent our driver. // status = WdfDriverCreate(DriverObject, RegistryPath, &attributes, &config, &hDriver); if (!NT_SUCCESS(status)) { KdPrint (("NonPnp: WdfDriverCreate failed with status 0x%x\n", status)); return status; } // // Since we are calling WPP_CLEANUP in the DriverContextCleanup // callback we should initialize WPP Tracing after WDFDRIVER // object is created to ensure that we cleanup WPP properly // if we return failure status from DriverEntry. This // eliminates the need to call WPP_CLEANUP in every path // of DriverEntry. // WPP_INIT_TRACING( DriverObject, RegistryPath ); // // On Win2K system, you will experience some delay in getting trace events // due to the way the ETW is activated to accept trace messages. // KdPrint(("NonPnp: DriverEntry: tracing enabled\n")); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Driver Frameworks NONPNP Legacy Driver Example"); // // // In order to create a control device, we first need to allocate a // WDFDEVICE_INIT structure and set all properties. // pInit = WdfControlDeviceInitAllocate( hDriver, &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R ); if (pInit == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; return status; } // // Call NonPnpDeviceAdd to create a deviceobject to represent our // software device. // status = NonPnpDeviceAdd(hDriver, pInit); return status; } NTSTATUS NonPnpDeviceAdd( IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit ) /*++ Routine Description: Called by the DriverEntry to create a control-device. This call is responsible for freeing the memory for DeviceInit. Arguments: DriverObject - a pointer to the object that represents this device driver. DeviceInit - Pointer to a driver-allocated WDFDEVICE_INIT structure. Return Value: STATUS_SUCCESS if initialized; an error otherwise. --*/ { NTSTATUS status; WDF_OBJECT_ATTRIBUTES attributes; WDF_IO_QUEUE_CONFIG ioQueueConfig; WDF_FILEOBJECT_CONFIG fileConfig; WDFQUEUE queue; WDFDEVICE controlDevice; DECLARE_CONST_UNICODE_STRING(ntDeviceName, NTDEVICE_NAME_STRING) ; DECLARE_CONST_UNICODE_STRING(symbolicLinkName, SYMBOLIC_NAME_STRING) ; UNREFERENCED_PARAMETER( Driver ); PAGED_CODE(); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NonPnpDeviceAdd DeviceInit %p\n", DeviceInit); // // Set exclusive to TRUE so that no more than one app can talk to the // control device at any time. // WdfDeviceInitSetExclusive(DeviceInit, TRUE); WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered); status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceInitAssignName failed %!STATUS!", status); goto End; } WdfControlDeviceInitSetShutdownNotification(DeviceInit, NonPnpShutdown, WdfDeviceShutdown); // // Initialize WDF_FILEOBJECT_CONFIG_INIT struct to tell the // framework whether you are interested in handling Create, Close and // Cleanup requests that gets generated when an application or another // kernel component opens an handle to the device. If you don't register // the framework default behaviour would be to complete these requests // with STATUS_SUCCESS. A driver might be interested in registering these // events if it wants to do security validation and also wants to maintain // per handle (fileobject) context. // WDF_FILEOBJECT_CONFIG_INIT( &fileConfig, NonPnpEvtDeviceFileCreate, NonPnpEvtFileClose, WDF_NO_EVENT_CALLBACK // not interested in Cleanup ); WdfDeviceInitSetFileObjectConfig(DeviceInit, &fileConfig, WDF_NO_OBJECT_ATTRIBUTES); // // In order to support METHOD_NEITHER Device controls, or // NEITHER device I/O type, we need to register for the // EvtDeviceIoInProcessContext callback so that we can handle the request // in the calling threads context. // WdfDeviceInitSetIoInCallerContextCallback(DeviceInit, NonPnpEvtDeviceIoInCallerContext); // // Specify the size of device context // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, CONTROL_DEVICE_EXTENSION); status = WdfDeviceCreate(&DeviceInit, &attributes, &controlDevice); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceCreate failed %!STATUS!", status); goto End; } // // Create a symbolic link for the control object so that usermode can open // the device. // status = WdfDeviceCreateSymbolicLink(controlDevice, &symbolicLinkName); if (!NT_SUCCESS(status)) { // // Control device will be deleted automatically by the framework. // TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfDeviceCreateSymbolicLink failed %!STATUS!", status); goto End; } // // Configure a default queue so that requests that are not // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto // other queues get dispatched here. // WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential); ioQueueConfig.EvtIoRead = FileEvtIoRead; ioQueueConfig.EvtIoWrite = FileEvtIoWrite; ioQueueConfig.EvtIoDeviceControl = FileEvtIoDeviceControl; WDF_OBJECT_ATTRIBUTES_INIT(&attributes); // // Since we are using Zw function set execution level to passive so that // framework ensures that our Io callbacks called at only passive-level // even if the request came in at DISPATCH_LEVEL from another driver. // //attributes.ExecutionLevel = WdfExecutionLevelPassive; // // By default, Static Driver Verifier (SDV) displays a warning if it // doesn't find the EvtIoStop callback on a power-managed queue. // The 'assume' below causes SDV to suppress this warning. If the driver // has not explicitly set PowerManaged to WdfFalse, the framework creates // power-managed queues when the device is not a filter driver. Normally // the EvtIoStop is required for power-managed queues, but for this driver // it is not needed b/c the driver doesn't hold on to the requests or // forward them to other drivers. This driver completes the requests // directly in the queue's handlers. If the EvtIoStop callback is not // implemented, the framework waits for all driver-owned requests to be // done before moving in the Dx/sleep states or before removing the // device, which is the correct behavior for this type of driver. // If the requests were taking an indeterminate amount of time to complete, // or if the driver forwarded the requests to a lower driver/another stack, // the queue should have an EvtIoStop/EvtIoResume. // __analysis_assume(ioQueueConfig.EvtIoStop != 0); status = WdfIoQueueCreate(controlDevice, &ioQueueConfig, &attributes, &queue // pointer to default queue ); __analysis_assume(ioQueueConfig.EvtIoStop == 0); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "WdfIoQueueCreate failed %!STATUS!", status); goto End; } // // Control devices must notify WDF when they are done initializing. I/O is // rejected until this call is made. // WdfControlFinishInitializing(controlDevice); End: // // If the device is created successfully, framework would clear the // DeviceInit value. Otherwise device create must have failed so we // should free the memory ourself. // if (DeviceInit != NULL) { WdfDeviceInitFree(DeviceInit); } return status; } VOID NonPnpEvtDriverContextCleanup( IN WDFOBJECT Driver ) /*++ Routine Description: Called when the driver object is deleted during driver unload. You can free all the resources created in DriverEntry that are not automatically freed by the framework. Arguments: Driver - Handle to a framework driver object created in DriverEntry Return Value: NTSTATUS --*/ { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Entered NonPnpEvtDriverContextCleanup\n"); PAGED_CODE(); // // No need to free the controldevice object explicitly because it will // be deleted when the Driver object is deleted due to the default parent // child relationship between Driver and ControlDevice. // WPP_CLEANUP( WdfDriverWdmGetDriverObject( (WDFDRIVER)Driver ) ); } VOID NonPnpEvtDeviceFileCreate ( IN WDFDEVICE Device, IN WDFREQUEST Request, IN WDFFILEOBJECT FileObject ) /*++ Routine Description: The framework calls a driver's EvtDeviceFileCreate callback when it receives an IRP_MJ_CREATE request. The system sends this request when a user application opens the device to perform an I/O operation, such as reading or writing a file. This callback is called synchronously, in the context of the thread that created the IRP_MJ_CREATE request. Arguments: Device - Handle to a framework device object. FileObject - Pointer to fileobject that represents the open handle. CreateParams - Parameters of IO_STACK_LOCATION for create Return Value: NT status code --*/ { PUNICODE_STRING fileName; UNICODE_STRING absFileName, directory; OBJECT_ATTRIBUTES fileAttributes; IO_STATUS_BLOCK ioStatus; PCONTROL_DEVICE_EXTENSION devExt; NTSTATUS status; USHORT length = 0; UNREFERENCED_PARAMETER( FileObject ); PAGED_CODE (); devExt = ControlGetData(Device); // // Assume the directory is a temp directory under %windir% // RtlInitUnicodeString(&directory, L"\\SystemRoot\\temp"); // // Parsed filename has "\" in the begining. The object manager strips // of all "\", except one, after the device name. // fileName = WdfFileObjectGetFileName(FileObject); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NonPnpEvtDeviceFileCreate %wZ%wZ", &directory, fileName); // // Find the total length of the directory + filename // length = directory.Length + fileName->Length; absFileName.Buffer = ExAllocatePoolWithTag(PagedPool, length, POOL_TAG); if(absFileName.Buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "ExAllocatePoolWithTag failed"); goto End; } absFileName.Length = 0; absFileName.MaximumLength = length; status = RtlAppendUnicodeStringToString(&absFileName, &directory); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "RtlAppendUnicodeStringToString failed with status %!STATUS!", status); goto End; } status = RtlAppendUnicodeStringToString(&absFileName, fileName); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "RtlAppendUnicodeStringToString failed with status %!STATUS!", status); goto End; } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Absolute Filename %wZ", &absFileName); InitializeObjectAttributes( &fileAttributes, &absFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, // RootDirectory NULL // SecurityDescriptor ); status = ZwCreateFile ( &devExt->FileHandle, SYNCHRONIZE | GENERIC_WRITE | GENERIC_READ, &fileAttributes, &ioStatus, NULL,// alloc size = none FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT |FILE_NON_DIRECTORY_FILE, NULL,// eabuffer 0// ealength ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_INIT, "ZwCreateFile failed with status %!STATUS!", status); devExt->FileHandle = NULL; } End: if(absFileName.Buffer != NULL) { ExFreePool(absFileName.Buffer); } WdfRequestComplete(Request, status); return; } VOID NonPnpEvtFileClose ( IN WDFFILEOBJECT FileObject ) /*++ Routine Description: EvtFileClose is called when all the handles represented by the FileObject is closed and all the references to FileObject is removed. This callback may get called in an arbitrary thread context instead of the thread that called CloseHandle. If you want to delete any per FileObject context that must be done in the context of the user thread that made the Create call, you should do that in the EvtDeviceCleanp callback. Arguments: FileObject - Pointer to fileobject that represents the open handle. Return Value: VOID --*/ { PCONTROL_DEVICE_EXTENSION devExt; PAGED_CODE (); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "NonPnpEvtFileClose\n"); devExt = ControlGetData(WdfFileObjectGetDevice(FileObject)); if(devExt->FileHandle) { TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Closing File Handle %p", devExt->FileHandle); ZwClose(devExt->FileHandle); } return; } VOID FileEvtIoRead( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length ) /*++ Routine Description: This event is called when the framework receives IRP_MJ_READ requests. We will just read the file. Arguments: Queue - Handle to the framework queue object that is associated with the I/O request. Request - Handle to a framework request object. Length - number of bytes to be read. Queue is by default configured to fail zero length read & write requests. Return Value: None. --*/ { NTSTATUS status = STATUS_SUCCESS; PVOID outBuf; IO_STATUS_BLOCK ioStatus; PCONTROL_DEVICE_EXTENSION devExt; FILE_POSITION_INFORMATION position; ULONG_PTR bytesRead = 0; size_t bufLength; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_RW, "FileEvtIoRead: Request: 0x%p, Queue: 0x%p\n", Request, Queue); PAGED_CODE (); // // Get the request buffer. Since the device is set to do buffered // I/O, this function will retrieve Irp->AssociatedIrp.SystemBuffer. // status = WdfRequestRetrieveOutputBuffer(Request, 0, &outBuf, &bufLength); if(!NT_SUCCESS(status)) { WdfRequestComplete(Request, status); return; } devExt = ControlGetData(WdfIoQueueGetDevice(Queue)); if(devExt->FileHandle) { // // Set the file position to the beginning of the file. // position.CurrentByteOffset.QuadPart = 0; status = ZwSetInformationFile(devExt->FileHandle, &ioStatus, &position, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation); if (NT_SUCCESS(status)) { status = ZwReadFile (devExt->FileHandle, NULL,// Event, NULL,// PIO_APC_ROUTINE ApcRoutine NULL,// PVOID ApcContext &ioStatus, outBuf, (ULONG)Length, 0, // ByteOffset NULL // Key ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_RW, "ZwReadFile failed with status 0x%x", status); } status = ioStatus.Status; bytesRead = ioStatus.Information; } } WdfRequestCompleteWithInformation(Request, status, bytesRead); } VOID FileEvtIoWrite( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t Length ) /*++ Routine Description: This event is called when the framework receives IRP_MJ_WRITE requests. Arguments: Queue - Handle to the framework queue object that is associated with the I/O request. Request - Handle to a framework request object. Length - number of bytes to be written. Queue is by default configured to fail zero length read & write requests. Return Value: None --*/ { NTSTATUS status = STATUS_SUCCESS; PVOID inBuf; IO_STATUS_BLOCK ioStatus; PCONTROL_DEVICE_EXTENSION devExt; FILE_POSITION_INFORMATION position; ULONG_PTR bytesWritten = 0; size_t bufLength; TraceEvents(TRACE_LEVEL_VERBOSE, DBG_RW, "FileEvtIoWrite: Request: 0x%p, Queue: 0x%p\n", Request, Queue); PAGED_CODE (); // // Get the request buffer. Since the device is set to do buffered // I/O, this function will retrieve Irp->AssociatedIrp.SystemBuffer. // status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufLength); if(!NT_SUCCESS(status)) { WdfRequestComplete(Request, status); return; } devExt = ControlGetData(WdfIoQueueGetDevice(Queue)); if(devExt->FileHandle) { // // Set the file position to the beginning of the file. // position.CurrentByteOffset.QuadPart = 0; status = ZwSetInformationFile(devExt->FileHandle, &ioStatus, &position, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation); if (NT_SUCCESS(status)) { status = ZwWriteFile(devExt->FileHandle, NULL,// Event, NULL,// PIO_APC_ROUTINE ApcRoutine NULL,// PVOID ApcContext &ioStatus, inBuf, (ULONG)Length, 0, // ByteOffset NULL // Key ); if (!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_RW, "ZwWriteFile failed with status 0x%x", status); } status = ioStatus.Status; bytesWritten = ioStatus.Information; } } WdfRequestCompleteWithInformation(Request, status, bytesWritten); } VOID FileEvtIoDeviceControl( IN WDFQUEUE Queue, IN WDFREQUEST Request, IN size_t OutputBufferLength, IN size_t InputBufferLength, IN ULONG IoControlCode ) /*++ Routine Description: This event is called when the framework receives IRP_MJ_DEVICE_CONTROL requests from the system. Arguments: Queue - Handle to the framework queue object that is associated with the I/O request. Request - Handle to a framework request object. OutputBufferLength - length of the request's output buffer, if an output buffer is available. InputBufferLength - length of the request's input buffer, if an input buffer is available. IoControlCode - the driver-defined or system-defined I/O control code (IOCTL) that is associated with the request. Return Value: VOID --*/ { NTSTATUS status = STATUS_SUCCESS;// Assume success PCHAR inBuf = NULL, outBuf = NULL; // pointer to Input and output buffer PCHAR data = "this String is from Device Driver !!!"; ULONG datalen = (ULONG) strlen(data)+1;//Length of data including null PCHAR buffer = NULL; PREQUEST_CONTEXT reqContext = NULL; size_t bufSize; UNREFERENCED_PARAMETER( Queue ); PAGED_CODE(); if(!OutputBufferLength || !InputBufferLength) { WdfRequestComplete(Request, STATUS_INVALID_PARAMETER); return; } // // Determine which I/O control code was specified. // switch (IoControlCode) { case IOCTL_NONPNP_METHOD_BUFFERED: TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_BUFFERED\n"); // // For bufffered ioctls WdfRequestRetrieveInputBuffer & // WdfRequestRetrieveOutputBuffer return the same buffer // pointer (Irp->AssociatedIrp.SystemBuffer), so read the // content of the buffer before writing to it. // status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize); if(!NT_SUCCESS(status)) { status = STATUS_INSUFFICIENT_RESOURCES; break; } ASSERT(bufSize == InputBufferLength); // // Read the input buffer content. // We are using the following function to print characters instead // TraceEvents with %s format because the string we get may or // may not be null terminated. The buffer may contain non-printable // characters also. // Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User : %!HEXDUMP!\n", log_xstr(inBuf, (USHORT)InputBufferLength))); PrintChars(inBuf, InputBufferLength ); status = WdfRequestRetrieveOutputBuffer(Request, 0, &outBuf, &bufSize); if(!NT_SUCCESS(status)) { status = STATUS_INSUFFICIENT_RESOURCES; break; } ASSERT(bufSize == OutputBufferLength); // // Writing to the buffer over-writes the input buffer content // RtlCopyMemory(outBuf, data, OutputBufferLength); Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data to User : %!HEXDUMP!\n", log_xstr(outBuf, (USHORT)datalen))); PrintChars(outBuf, datalen ); // // Assign the length of the data copied to IoStatus.Information // of the request and complete the request. // WdfRequestSetInformation(Request, OutputBufferLength < datalen? OutputBufferLength:datalen); // // When the request is completed the content of the SystemBuffer // is copied to the User output buffer and the SystemBuffer is // is freed. // break; case IOCTL_NONPNP_METHOD_IN_DIRECT: TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_IN_DIRECT\n"); // // Get the Input buffer. WdfRequestRetrieveInputBuffer returns // Irp->AssociatedIrp.SystemBuffer. // status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize); if(!NT_SUCCESS(status)) { status = STATUS_INSUFFICIENT_RESOURCES; break; } ASSERT(bufSize == InputBufferLength); Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User : %!HEXDUMP!\n", log_xstr(inBuf, (USHORT)InputBufferLength))); PrintChars(inBuf, InputBufferLength); // // Get the output buffer. Framework calls MmGetSystemAddressForMdlSafe // on the Irp->MdlAddress and returns the system address. // Oddity: For this method, this buffer is intended for transfering data // from the application to the driver. // status = WdfRequestRetrieveOutputBuffer(Request, 0, &buffer, &bufSize); if(!NT_SUCCESS(status)) { break; } ASSERT(bufSize == OutputBufferLength); Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User in OutputBuffer: %!HEXDUMP!\n", log_xstr(buffer, (USHORT)OutputBufferLength))); PrintChars(buffer, OutputBufferLength); // // Return total bytes read from the output buffer. // Note OutputBufferLength = MmGetMdlByteCount(Irp->MdlAddress) // WdfRequestSetInformation(Request, OutputBufferLength); // // NOTE: Changes made to the SystemBuffer are not copied // to the user input buffer by the I/O manager // break; case IOCTL_NONPNP_METHOD_OUT_DIRECT: TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_OUT_DIRECT\n"); // // Get the Input buffer. WdfRequestRetrieveInputBuffer returns // Irp->AssociatedIrp.SystemBuffer. // status = WdfRequestRetrieveInputBuffer(Request, 0, &inBuf, &bufSize); if(!NT_SUCCESS(status)) { status = STATUS_INSUFFICIENT_RESOURCES; break; } ASSERT(bufSize == InputBufferLength); Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User : %!HEXDUMP!\n", log_xstr(inBuf, (USHORT)InputBufferLength))); PrintChars(inBuf, InputBufferLength); // // Get the output buffer. Framework calls MmGetSystemAddressForMdlSafe // on the Irp->MdlAddress and returns the system address. // For this method, this buffer is intended for transfering data from the // driver to the application. // status = WdfRequestRetrieveOutputBuffer(Request, 0, &buffer, &bufSize); if(!NT_SUCCESS(status)) { break; } ASSERT(bufSize == OutputBufferLength); // // Write data to be sent to the user in this buffer // RtlCopyMemory(buffer, data, OutputBufferLength); Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data to User : %!HEXDUMP!\n", log_xstr(buffer, (USHORT)datalen))); PrintChars(buffer, datalen); WdfRequestSetInformation(Request, OutputBufferLength < datalen? OutputBufferLength: datalen); // // NOTE: Changes made to the SystemBuffer are not copied // to the user input buffer by the I/O manager // break; case IOCTL_NONPNP_METHOD_NEITHER: { size_t inBufLength, outBufLength; // // The NonPnpEvtDeviceIoInCallerContext has already probe and locked the // pages and mapped the user buffer into system address space and // stored memory buffer pointers in the request context. We can get the // buffer pointer by calling WdfMemoryGetBuffer. // TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Called IOCTL_NONPNP_METHOD_NEITHER\n"); reqContext = GetRequestContext(Request); inBuf = WdfMemoryGetBuffer(reqContext->InputMemoryBuffer, &inBufLength); outBuf = WdfMemoryGetBuffer(reqContext->OutputMemoryBuffer, &outBufLength); if(inBuf == NULL || outBuf == NULL) { status = STATUS_INVALID_PARAMETER; } ASSERT(inBufLength == InputBufferLength); ASSERT(outBufLength == OutputBufferLength); // // Now you can safely read the data from the buffer in any arbitrary // context. // Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data from User : %!HEXDUMP!\n", log_xstr(inBuf, (USHORT)inBufLength))); PrintChars(inBuf, inBufLength); // // Write to the buffer in any arbitrary context. // RtlCopyMemory(outBuf, data, outBufLength); Hexdump((TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Data to User : %!HEXDUMP!\n", log_xstr(outBuf, (USHORT)datalen))); PrintChars(outBuf, datalen); // // Assign the length of the data copied to IoStatus.Information // of the Irp and complete the Irp. // WdfRequestSetInformation(Request, outBufLength < datalen? outBufLength:datalen); break; } default: // // The specified I/O control code is unrecognized by this driver. // status = STATUS_INVALID_DEVICE_REQUEST; TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "ERROR: unrecognized IOCTL %x\n", IoControlCode); break; } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Completing Request %p with status %X", Request, status ); WdfRequestComplete( Request, status); } VOID NonPnpEvtDeviceIoInCallerContext( IN WDFDEVICE Device, IN WDFREQUEST Request ) /*++ Routine Description: This I/O in-process callback is called in the calling threads context/address space before the request is subjected to any framework locking or queueing scheme based on the device pnp/power or locking attributes set by the driver. The process context of the calling app is guaranteed as long as this driver is a top-level driver and no other filter driver is attached to it. This callback is only required if you are handling method-neither IOCTLs, or want to process requests in the context of the calling process. Driver developers should avoid defining neither IOCTLs and access user buffers, and use much safer I/O tranfer methods such as buffered I/O or direct I/O. Arguments: Device - Handle to a framework device object. Request - Handle to a framework request object. Framework calls PreProcess callback only for Read/Write/ioctls and internal ioctl requests. Return Value: VOID --*/ { NTSTATUS status = STATUS_SUCCESS; PREQUEST_CONTEXT reqContext = NULL; WDF_OBJECT_ATTRIBUTES attributes; WDF_REQUEST_PARAMETERS params; size_t inBufLen, outBufLen; PVOID inBuf, outBuf; PAGED_CODE(); WDF_REQUEST_PARAMETERS_INIT(¶ms); WdfRequestGetParameters(Request, ¶ms ); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "Entered NonPnpEvtDeviceIoInCallerContext %p \n", Request); // // Check to see whether we have recevied a METHOD_NEITHER IOCTL. if not // just send the request back to framework because we aren't doing // any pre-processing in the context of the calling thread process. // if(!(params.Type == WdfRequestTypeDeviceControl && params.Parameters.DeviceIoControl.IoControlCode == IOCTL_NONPNP_METHOD_NEITHER)) { // // Forward it for processing by the I/O package // status = WdfDeviceEnqueueRequest(Device, Request); if( !NT_SUCCESS(status) ) { TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Error forwarding Request 0x%x", status); goto End; } return; } TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "EvtIoPreProcess: received METHOD_NEITHER ioctl \n"); // // In this type of transfer, the I/O manager assigns the user input // to Type3InputBuffer and the output buffer to UserBuffer of the Irp. // The I/O manager doesn't copy or map the buffers to the kernel // buffers. // status = WdfRequestRetrieveUnsafeUserInputBuffer(Request, 0, &inBuf, &inBufLen); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Error WdfRequestRetrieveUnsafeUserInputBuffer failed 0x%x", status); goto End; } status = WdfRequestRetrieveUnsafeUserOutputBuffer(Request, 0, &outBuf, &outBufLen); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Error WdfRequestRetrieveUnsafeUserOutputBuffer failed 0x%x", status); goto End; } // // Allocate a context for this request so that we can store the memory // objects created for input and output buffer. // WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, REQUEST_CONTEXT); status = WdfObjectAllocateContext(Request, &attributes, &reqContext); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Error WdfObjectAllocateContext failed 0x%x", status); goto End; } // // WdfRequestProbleAndLockForRead/Write function checks to see // whether the caller in the right thread context, creates an MDL, // probe and locks the pages, and map the MDL to system address // space and finally creates a WDFMEMORY object representing this // system buffer address. This memory object is associated with the // request. So it will be freed when the request is completed. If we // are accessing this memory buffer else where, we should store these // pointers in the request context. // #pragma prefast(suppress:6387, "If inBuf==NULL at this point, then inBufLen==0") status = WdfRequestProbeAndLockUserBufferForRead(Request, inBuf, inBufLen, &reqContext->InputMemoryBuffer); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Error WdfRequestProbeAndLockUserBufferForRead failed 0x%x", status); goto End; } #pragma prefast(suppress:6387, "If outBuf==NULL at this point, then outBufLen==0") status = WdfRequestProbeAndLockUserBufferForWrite(Request, outBuf, outBufLen, &reqContext->OutputMemoryBuffer); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Error WdfRequestProbeAndLockUserBufferForWrite failed 0x%x", status); goto End; } // // Finally forward it for processing by the I/O package // status = WdfDeviceEnqueueRequest(Device, Request); if(!NT_SUCCESS(status)) { TraceEvents(TRACE_LEVEL_ERROR, DBG_IOCTL, "Error WdfDeviceEnqueueRequest failed 0x%x", status); goto End; } return; End: TraceEvents(TRACE_LEVEL_VERBOSE, DBG_IOCTL, "EvtIoPreProcess failed %x \n", status); WdfRequestComplete(Request, status); return; } VOID NonPnpShutdown( WDFDEVICE Device ) /*++ Routine Description: Callback invoked when the machine is shutting down. If you register for a last chance shutdown notification you cannot do the following: o Call any pageable routines o Access pageable memory o Perform any file I/O operations If you register for a normal shutdown notification, all of these are available to you. This function implementation does nothing, but if you had any outstanding file handles open, this is where you would close them. Arguments: Device - The device which registered the notification during init Return Value: None --*/ { UNREFERENCED_PARAMETER(Device); return; } VOID NonPnpEvtDriverUnload( IN WDFDRIVER Driver ) /*++ Routine Description: Called by the I/O subsystem just before unloading the driver. You can free the resources created in the DriverEntry either in this routine or in the EvtDriverContextCleanup callback. Arguments: Driver - Handle to a framework driver object created in DriverEntry Return Value: NTSTATUS --*/ { UNREFERENCED_PARAMETER(Driver); PAGED_CODE(); TraceEvents(TRACE_LEVEL_VERBOSE, DBG_INIT, "Entered NonPnpDriverUnload\n"); return; } VOID PrintChars( _In_reads_(CountChars) PCHAR BufferAddress, _In_ size_t CountChars ) { if (CountChars) { while (CountChars--) { if (*BufferAddress > 31 && *BufferAddress != 127) { KdPrint (( "%c", *BufferAddress) ); } else { KdPrint(( ".") ); } BufferAddress++; } KdPrint (("\n")); } return; }
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials