Windows-driver-samples/network/modem/fakemodem/driver.c

474 строки
14 KiB
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:
Driver.c
Abstract:
This is a simple form of function driver for fakemodem device. The driver
doesn't handle any PnP and Power events because the framework provides
default behaviour for those events. This driver has enough support to
allow an user application (toast/notify.exe) to open the device
interface registered by the driver and send read, write or ioctl requests.
Environment:
Kernel mode
--*/
#include "fakemodem.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, FmEvtDeviceAdd)
#pragma alloc_text (PAGE, FmCreateDosDevicesSymbolicLink)
#pragma alloc_text (PAGE, FmDeviceCleanup)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
DriverEntry initializes the driver and is the first routine called by the
system after the driver is loaded. DriverEntry specifies the other entry
points in the function driver, such as FmAddDevice and FmUnload.
Parameters Description:
DriverObject - represents the instance of the function driver that is loaded
into memory. DriverEntry must initialize members of DriverObject before it
returns to the caller. DriverObject is allocated by the system before the
driver is loaded, and it is released by the system after the system unloads
the function driver from memory.
RegistryPath - represents the driver specific path in the Registry.
The function driver can use the path to store driver related data between
reboots. The path does not store hardware instance specific data.
Return Value:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
WDF_DRIVER_CONFIG config;
KdPrint(("Fakemode Function Driver Sample - Driver Framework Edition.\n"));
KdPrint(("Built %s %s\n", __DATE__, __TIME__));
WDF_DRIVER_CONFIG_INIT( &config, FmEvtDeviceAdd );
//
// Create a framework driver object to represent our driver.
//
status = WdfDriverCreate(
DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config, // Driver Config Info
WDF_NO_HANDLE
);
if (!NT_SUCCESS(status)) {
KdPrint( ("WdfDriverCreate failed with status 0x%x\n", status));
}
return status;
}
NTSTATUS
FmEvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
/*++
Routine Description:
FmEvtDeviceAdd is called by the framework in response to AddDevice
call from the PnP manager. We create and initialize a device object to
represent a new instance of Fm device.
Arguments:
Driver - Handle to a framework driver object created in DriverEntry
DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
Return Value:
NTSTATUS
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PFM_DEVICE_DATA fmDeviceData;
WDF_IO_QUEUE_CONFIG queueConfig;
WDF_OBJECT_ATTRIBUTES fdoAttributes;
WDFDEVICE hDevice;
WDFQUEUE defQueue;
UNREFERENCED_PARAMETER(Driver);
KdPrint( ("FmEvtDeviceAdd routine \n"));
PAGED_CODE();
//
// Modem type is serial port.
//
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_SERIAL_PORT);
//
// Use Buffered IO.
//
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);
//
// Specify the size of device extension where we track per device
// context.
//
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, FM_DEVICE_DATA);
//
// Register a cleanup callback on the device to free up some resources at the
// time the device is deleted.
//
fdoAttributes.EvtCleanupCallback = FmDeviceCleanup;
//
// By opting for SynchronizationScopeDevice, we tell the framework to
// synchronize callbacks events of all the objects directly associated
// with the device. In this driver, we will associate queues.
// By doing that we don't have to worrry about synchronizing
// access to device-context by various io Events.
// Framework will serialize them by using an internal device-lock.
//
fdoAttributes.SynchronizationScope = WdfSynchronizationScopeDevice;
//
// Create a framework device object.This call will inturn create
// a WDM deviceobject, attach to the lower stack and set the
// appropriate flags and attributes.
//
status = WdfDeviceCreate(&DeviceInit, &fdoAttributes, &hDevice);
if (!NT_SUCCESS(status)) {
KdPrint( ("WdfDeviceCreate failed with Status code 0x%x\n", status));
return status;
}
//
// Get the DeviceExtension and initialize it.
//
fmDeviceData = FmDeviceDataGet(hDevice);
//
// Tell the Framework that this device will need an interface
//
status = WdfDeviceCreateDeviceInterface(
hDevice,
(LPGUID) &GUID_DEVINTERFACE_MODEM,
NULL
);
if (!NT_SUCCESS (status)) {
KdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status));
return status;
}
fmDeviceData->Flags = 0;
status = FmCreateDosDevicesSymbolicLink(hDevice, fmDeviceData);
if (!NT_SUCCESS(status)) {
KdPrint( ("FmCreateDosDevicesSymbolicLink failed with Status code 0x%x\n", status));
return status;
}
//
// Initialize the context
//
fmDeviceData->BaudRate=1200;
fmDeviceData->LineControl = SERIAL_7_DATA | SERIAL_EVEN_PARITY | SERIAL_NONE_PARITY;
//
// Register I/O callbacks to tell the framework that you are interested
// in handling IRP_MJ_READ, IRP_MJ_WRITE, and IRP_MJ_DEVICE_CONTROL requests.
// In case a specific handler is not specified for one of these,
// the request will be dispatched to the EvtIoDefault handler, if any.
// If there is no EvtIoDefault handler, the request will be failed with
// STATUS_INVALID_DEVICE_REQUEST.
// WdfIoQueueDispatchParallel means that we are capable of handling
// all the I/O request simultaneously and we are responsible for protecting
// data that could be accessed by these callbacks simultaneously.
//
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig,
WdfIoQueueDispatchParallel);
queueConfig.EvtIoRead = FmEvtIoRead;
queueConfig.EvtIoWrite = FmEvtIoWrite;
queueConfig.EvtIoDeviceControl = FmEvtIoDeviceControl;
__analysis_assume(queueConfig.EvtIoStop != 0);
status = WdfIoQueueCreate(
hDevice,
&queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&defQueue // pointer to default queue
);
__analysis_assume(queueConfig.EvtIoStop == 0);
if (!NT_SUCCESS (status)) {
//
// We don't need to cleanup symbolic link here. The destroy callback for
// the device object will do it.
//
return status;
}
//
// Create a manual queue to hold pending read requests. By keeping
// them in the queue, framework takes care of cancelling them if the app exits
//
WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
WdfIoQueueDispatchManual);
__analysis_assume(queueConfig.EvtIoStop != 0);
status = WdfIoQueueCreate(hDevice,
&queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&fmDeviceData->FmReadQueue
);
__analysis_assume(queueConfig.EvtIoStop == 0);
if (!NT_SUCCESS (status)) {
KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status));
return status;
}
//
// Create a manual queue to hold pending ioctl wait mask requests. By keeping
// them in the queue, framework takes care of cancelling them if the app exits
//
WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
WdfIoQueueDispatchManual);
__analysis_assume(queueConfig.EvtIoStop != 0);
status = WdfIoQueueCreate(hDevice,
&queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&fmDeviceData->FmMaskWaitQueue
);
__analysis_assume(queueConfig.EvtIoStop == 0);
if (!NT_SUCCESS (status)) {
KdPrint( ("WdfIoQueueCreate failed 0x%x\n", status));
return status;
}
return status;
}
VOID
FmDeviceCleanup(
WDFOBJECT Device
)
/*++
Routine Description:
This event is called when the device object is destroyed.
Cleanup any associated data.
Arguments:
Return Value:
VOID
--*/
{
PFM_DEVICE_DATA fmData;
PAGED_CODE();
fmData = FmDeviceDataGet((WDFDEVICE)Device);
if (fmData->Flags & REG_VALUE_CREATED_FLAG) {
RtlDeleteRegistryValue(
RTL_REGISTRY_DEVICEMAP,
L"SERIALCOMM",
fmData->PdoName.Buffer
);
}
}
NTSTATUS
FmCreateDosDevicesSymbolicLink(
WDFDEVICE Device,
PFM_DEVICE_DATA FmDeviceData
)
{
NTSTATUS status;
UNICODE_STRING comPort;
UNICODE_STRING pdoName;
UNICODE_STRING symbolicLink;
WDFKEY hKey = NULL;
DECLARE_CONST_UNICODE_STRING(valueName, L"PortName");
WDFSTRING string = NULL;
WDFMEMORY memory;
WDF_OBJECT_ATTRIBUTES memoryAttributes;
size_t bufferLength;
PAGED_CODE();
symbolicLink.Buffer = NULL;
//
// Open the device registry and read the "PortName" value written by the
// class installer.
//
status = WdfDeviceOpenRegistryKey(Device,
PLUGPLAY_REGKEY_DEVICE,
STANDARD_RIGHTS_ALL,
NULL, // PWDF_OBJECT_ATTRIBUTES
&hKey);
if (!NT_SUCCESS (status)) {
goto Error;
}
status = WdfStringCreate(
NULL,
WDF_NO_OBJECT_ATTRIBUTES ,
&string
);
if (!NT_SUCCESS(status)) {
goto Error;
}
//
// Retrieve the value of ValueName from registry
//
status = WdfRegistryQueryString(
hKey,
&valueName,
string
);
if (!NT_SUCCESS (status)) {
goto Error;
}
//
// Retrieve the UNICODE_STRING from string object
//
WdfStringGetUnicodeString(
string,
&comPort
);
WdfRegistryClose(hKey);
hKey = NULL;
symbolicLink.Length=0;
symbolicLink.MaximumLength = sizeof(OBJECT_DIRECTORY) + comPort.MaximumLength;
symbolicLink.Buffer = ExAllocatePool2(POOL_FLAG_PAGED,
symbolicLink.MaximumLength + sizeof(WCHAR),
'wkaF');
if (symbolicLink.Buffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
RtlAppendUnicodeToString(&symbolicLink, OBJECT_DIRECTORY);
RtlAppendUnicodeStringToString(&symbolicLink, &comPort);
//
// This DDI will get the underlying PDO name and create a symbolic to that
// because our FDO doesn't have a name.
//
status = WdfDeviceCreateSymbolicLink(Device,
&symbolicLink);
if (!NT_SUCCESS(status)) {
goto Error;
}
WDF_OBJECT_ATTRIBUTES_INIT(&memoryAttributes);
memoryAttributes.ParentObject = Device;
status = WdfDeviceAllocAndQueryProperty(Device,
DevicePropertyPhysicalDeviceObjectName,
PagedPool,
&memoryAttributes,
&memory);
if (!NT_SUCCESS(status)) {
//
// We expect a zero length buffer. Anything else is fatal.
//
goto Error;
}
pdoName.Buffer = WdfMemoryGetBuffer(memory, &bufferLength);
if (pdoName.Buffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
pdoName.MaximumLength = (USHORT) bufferLength;
pdoName.Length = (USHORT) bufferLength - sizeof(UNICODE_NULL);
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
L"SERIALCOMM",
pdoName.Buffer,
REG_SZ,
comPort.Buffer,
comPort.Length);
if (!NT_SUCCESS(status)) {
goto Error;
}
FmDeviceData->Flags |= REG_VALUE_CREATED_FLAG;
//
// Store it so it can be deleted later.
//
FmDeviceData->PdoName = pdoName;
Error:
if (symbolicLink.Buffer != NULL) {
ExFreePool(symbolicLink.Buffer);
}
if (hKey != NULL) {
WdfRegistryClose(hKey);
}
if (string != NULL) {
WdfObjectDelete(string);
}
return status;
}