www.pudn.com > VSer.rar > main.c
/*++
Copyright (c) 2005 Changzhi Zhou All Rights Reserved
Module Name:
main.c
Abstract:
This module is main module that deals with
DriverEntry, AddDevice, Unload, handles to device name.
SampleIoCrement and SampleIoDecrement.
Environment:
Kernel mode
Revision History:
Changzhi Zhou Dec 20 2004
--*/
#include
#include
#include
#include "main.h"
#include "..\inc\wdmioctl.h"
#include
#include
#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, AddDevice)
#pragma alloc_text (PAGE, SamplePnpDispatch)
#pragma alloc_text (PAGE, Unload)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
Installable driver initialization entry point.
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 status;
UNREFERENCED_PARAMETER (RegistryPath);
DbgPrint("------ VirtualSerial Build on %s %s -------\n", __DATE__, __TIME__ );
status = STATUS_SUCCESS;
DriverObject->MajorFunction[IRP_MJ_PNP] = SamplePnpDispatch;
DriverObject->MajorFunction[IRP_MJ_POWER] = SamplePowerDispatch;
DriverObject->MajorFunction[IRP_MJ_CREATE] = SampleCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SampleClose;
DriverObject->MajorFunction[IRP_MJ_CLEANUP ] = SampleCleanup;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SampleIoControl;
DriverObject->MajorFunction[IRP_MJ_WRITE] = SampleWrite;
DriverObject->MajorFunction[IRP_MJ_READ] = SampleRead;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ToasterSystemControl;
DriverObject->DriverExtension->AddDevice = AddDevice;
DriverObject->DriverUnload = Unload;
return status;
}
NTSTATUS
AddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
The Plug & Play subsystem is handing us a brand new PDO, for which we
(by means of INF registration) have been asked to provide a driver.
We need to determine if we need to be in the driver stack for the device.
Create a function device object to attach to the stack
Initialize that device object
Return status success.
Remember: We can NOT actually send ANY non pnp IRPS to the given driver
stack, UNTIL we have received an IRP_MN_START_DEVICE.
Arguments:
DeviceObject - pointer to a device object.
PhysicalDeviceObject - pointer to a device object created by the
underlying bus driver.
Return Value:
NT status code.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT deviceObject = NULL;
PDEVICE_EXTENSION deviceExtension;
UNICODE_STRING deviceObjName;
PAGED_CODE ();
DbgPrint("------- AddDevice build on %s %s ---------\n", __DATE__, __TIME__);
if( FALSE == InitializeSerialDevName( &deviceObjName ) )
return STATUS_INSUFFICIENT_RESOURCES;
status = IoCreateDevice (DriverObject,
sizeof (DEVICE_EXTENSION),
&deviceObjName,
FILE_DEVICE_SERIAL_PORT,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&deviceObject);
if (!NT_SUCCESS (status)) {
DebugPrint(("IoCreateDevice failed\n"));
if ( deviceObjName.Buffer != NULL )
ExFreePool ( deviceObjName.Buffer );
return status;
}
DebugPrint (("AddDevice PDO (0x%x) FDO (0x%x)\n", PhysicalDeviceObject, deviceObject));
deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension;
deviceExtension->DeviceName.MaximumLength = deviceObjName.MaximumLength;
deviceExtension->DeviceName.Length = deviceObjName.Length;
deviceExtension->DeviceName.Buffer = deviceObjName.Buffer;
deviceExtension->CreatedSerialCommEntry = FALSE;
deviceExtension->CreatedSymbolicLink = FALSE;
// for working thread
KeInitializeSpinLock( &deviceExtension->ThreadSpinLock );
deviceExtension->NextLowerDriver = IoAttachDeviceToDeviceStack (
deviceObject,
PhysicalDeviceObject);
if(NULL == deviceExtension->NextLowerDriver) {
IoDeleteDevice(deviceObject);
return STATUS_UNSUCCESSFUL;
}
DebugPrint(("AddDevice: %x to %x->%x \n", deviceObject,
deviceExtension->NextLowerDriver,
PhysicalDeviceObject));
// Initialize the remove event to not-signaled.
//
KeInitializeEvent(&deviceExtension->RemoveEvent,
SynchronizationEvent,
FALSE);
//
// Initialize the stop event to signaled.
// This event is signaled when the OutstandingIO becomes 1
//
KeInitializeEvent(&deviceExtension->StopEvent,
SynchronizationEvent,
TRUE);
deviceExtension->OutStandingIO = 1;
deviceExtension->bIsOpen = FALSE;
KeInitializeSpinLock( &deviceExtension->IOCountLock );
if ( PhysicalDeviceObject->Flags & DO_POWER_PAGABLE) {
deviceObject->Flags |= DO_POWER_PAGABLE;
}
deviceObject->Flags |= DO_DIRECT_IO;
deviceExtension->Self = deviceObject;
// Initialize serial-related parameters
deviceExtension->WaitOnMaskIrp = NULL;
deviceExtension->CurrentBaud = 0;
deviceExtension->PendingReadIrp = NULL;
KeInitializeTimer ( &deviceExtension->ReadTimer );
KeInitializeDpc ( &deviceExtension->ReadDpc, ReadDpcRoutine, (PVOID)deviceExtension );
RtlZeroMemory( &deviceExtension->SerialStatus, sizeof( SERIAL_STATUS ) );
// initialize members relevant to TDI.
deviceExtension->RxBuffer = ExAllocatePoolWithTag( NonPagedPool, RINGBUFFER_SIZE, 'RBuf' );
if ( deviceExtension->RxBuffer == NULL )
goto Error_Exit;
deviceExtension->lpRead = deviceExtension->RxBuffer;
deviceExtension->lpRx = deviceExtension->RxBuffer;
deviceExtension->hTransAddr = NULL;
deviceExtension->lpTransAddrFileObject = NULL;
deviceExtension->TDILowerDeviceObject = NULL;
deviceExtension->RemoteAddress = 0x601a8c0; // 192.168.1.6
deviceExtension->RemotePort = 0x7217; // 6002
RtlZeroMemory( (PVOID)&deviceExtension->recvContext, sizeof( RECV_CONTEXT ) );
deviceExtension->recvContext.RemainderBuffer = ExAllocatePoolWithTag ( NonPagedPool, RECVREMAINDER_BUFFER_SIZE, 'Rrem' );
if ( deviceExtension->recvContext.RemainderBuffer == NULL )
goto Error_Exit;
KeInitializeEvent(&deviceExtension->recvContext.Event,
SynchronizationEvent,
FALSE);
RtlZeroMemory ( (PVOID)&deviceExtension->recvContext.ReceiveDatagramInfo, sizeof ( TDI_CONNECTION_INFORMATION ) );
RtlZeroMemory ( (PVOID)&deviceExtension->recvContext.ReturnInfo, sizeof ( TDI_CONNECTION_INFORMATION ) );
INITIALIZE_PNP_STATE(deviceExtension);
//++ Register device interface for win32app
status = IoRegisterDeviceInterface( PhysicalDeviceObject,
(LPGUID)&GUID_CLASS_COMPORT,
NULL,
&deviceExtension->InterfaceName );
DebugPrint(("InterfaceName: %ws\n", deviceExtension->InterfaceName.Buffer ));
if( !NT_SUCCESS ( status ) ){
DebugPrint(( "AddDevice: IoRegisterDeviceInterface failed (%x) \n", status ));
goto Error_Exit;
}
//--
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return STATUS_SUCCESS;
Error_Exit:
if( deviceExtension->RxBuffer )
{
ExFreePool ( deviceExtension->RxBuffer );
deviceExtension->RxBuffer = NULL;
}
if ( deviceExtension->recvContext.RemainderBuffer )
{
ExFreePool ( deviceExtension->recvContext.RemainderBuffer );
deviceExtension->recvContext.RemainderBuffer = NULL;
}
IoDetachDevice( deviceExtension->NextLowerDriver );
IoDeleteDevice( deviceObject );
return status;
}
NTSTATUS
SerialDoExternalNaming(
IN PDEVICE_EXTENSION deviceExtension,
IN LONG ComX
)
/*++
Routine Description:
Create external name, SymbolicLinkName and DOS name.
Arguments:
deviceExtension - pointer to a device object extension.
ComX - indicates the number of Serial Ports.
Return Value:
NT status code.
--*/
{
NTSTATUS status;
ULONG bufLen;
WCHAR ComXBuffer[ 4 ];
UNICODE_STRING instanceStr;
DebugPrint(("Enter SerialdoExternalNaming routine...\n"));
if( deviceExtension->CreatedSymbolicLink || deviceExtension->CreatedSerialCommEntry ){
DebugPrint(("Already create symboliclink or serial commentry\n"));
return STATUS_UNSUCCESSFUL;
}
ASSERT( deviceExtension->SymbolicLinkName.Buffer == NULL );
ASSERT( deviceExtension->DosName.Buffer == NULL );
RtlInitUnicodeString(&instanceStr, NULL);
instanceStr.MaximumLength = sizeof( ComXBuffer );
instanceStr.Buffer = ComXBuffer; // 4 WCHAR
RtlIntegerToUnicodeString( ComX, 10, &instanceStr );
// 将SymbolicLinkName设置为 \DosDevices\COMn 的形式。其中n = ComX
RtlZeroMemory( &deviceExtension->SymbolicLinkName, sizeof( UNICODE_STRING ) );
deviceExtension->SymbolicLinkName.MaximumLength = DEVICE_OBJECT_NAME_LENGTH + sizeof( WCHAR );
deviceExtension->SymbolicLinkName.Buffer = ExAllocatePoolWithTag( PagedPool, deviceExtension->SymbolicLinkName.MaximumLength, 'SymL' );
if( deviceExtension->SymbolicLinkName.Buffer == NULL ){
DebugPrint(("SERIAL: Couldn't allocate memory for symbolic link name\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto SerialDoExternalNamingError;
}
RtlZeroMemory( deviceExtension->SymbolicLinkName.Buffer, deviceExtension->SymbolicLinkName.MaximumLength );
RtlAppendUnicodeToString( &deviceExtension->SymbolicLinkName, DOS_DEVICE_NAME ); //L"\\DosDevices\\COM"
RtlAppendUnicodeStringToString( &deviceExtension->SymbolicLinkName, &instanceStr);
DebugPrint(("SymbolicLinkName: %wZ\n", deviceExtension->SymbolicLinkName ));
// 将DosName初始化 COMn 的形式, 其中 n = ComX
deviceExtension->DosName.MaximumLength = 64 + sizeof(WCHAR);
deviceExtension->DosName.Buffer = ExAllocatePoolWithTag( PagedPool, 64 + sizeof( WCHAR ), 'Name' );
if( deviceExtension->DosName.Buffer == NULL ){
DebugPrint(("SERIAL: Couldn't allocate memory for Dos name\n"));
status = STATUS_INSUFFICIENT_RESOURCES;
goto SerialDoExternalNamingError;
}
deviceExtension->DosName.Length = 0;
RtlZeroMemory( deviceExtension->DosName.Buffer, deviceExtension->DosName.MaximumLength );
RtlAppendUnicodeToString( &deviceExtension->DosName, L"COM" );
RtlAppendUnicodeStringToString( &deviceExtension->DosName, &instanceStr);
DebugPrint(("DosName: %wZ\n", &deviceExtension->DosName ));
DebugPrint(("DeviceName: %wZ\n", &deviceExtension->DeviceName ));
// 生成符号连接,至此本设备对win32应用表现的Dos设备名为 \DosDevices\COMn
status = IoCreateSymbolicLink( &deviceExtension->SymbolicLinkName, &deviceExtension->DeviceName );
if( !NT_SUCCESS( status )){
DebugPrint(("SERIAL: Couldn't create the symbolic link with error %d\n", status));
goto SerialDoExternalNamingError;
}else{
DebugPrint(("Create the symbolic link OK\n"));
deviceExtension->CreatedSymbolicLink = TRUE;
}
// 在注册表的 HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM 中,添加ComX的键值
// 若不进行这一步,则超级终端程序无法检测到本虚拟串口设备
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP,
deviceExtension->DeviceName.Buffer, REG_SZ,
deviceExtension->DosName.Buffer,
deviceExtension->DosName.Length + sizeof(WCHAR));
if( !NT_SUCCESS( status )){
DebugPrint(("SERIAL: Couldn't create the device map entry\n------- for port %wZ\n", deviceExtension->DeviceName ));
goto SerialDoExternalNamingError;
}
deviceExtension->ComX = ComX;
deviceExtension->CreatedSerialCommEntry = TRUE;
SerialDoExternalNamingError:
if( !NT_SUCCESS( status )){
if( deviceExtension->DosName.Buffer != NULL ){
ExFreePool( deviceExtension->DosName.Buffer );
deviceExtension->DosName.Buffer = NULL;
deviceExtension->CreatedSerialCommEntry = FALSE;
}
if( deviceExtension->CreatedSymbolicLink ){
IoDeleteSymbolicLink( &deviceExtension->SymbolicLinkName );
deviceExtension->CreatedSymbolicLink = FALSE;
}
if( deviceExtension->SymbolicLinkName.Buffer != NULL ){
ExFreePool( deviceExtension->SymbolicLinkName.Buffer );
deviceExtension->SymbolicLinkName.Buffer = NULL;
}
}
return status;
}
BOOLEAN InitializeSerialDevName( PUNICODE_STRING lpDeviceName )
/*++
Routine Description:
Initialize serial device name. This name is available in Kernel Mode.
Arguments:
PUNICODE_STRING lpDeviceName - the pointer to device name string.
Return Value:
return TRUE if it is successful, otherwise return FALSE.
--*/
{
WCHAR instanceNumberBuffer[ 4 ];
UNICODE_STRING instanceStr;
static ULONG currentInstance = 10;
// 首先处理deviceObjName,这是一个核心态使用的设备名,形如"\\Device\\SiSerial0"
// Zero out allocated memory pointers so we know if they must be freed
RtlZeroMemory( lpDeviceName, sizeof(UNICODE_STRING) );
lpDeviceName->MaximumLength = DEVICE_OBJECT_NAME_LENGTH * sizeof(WCHAR);
lpDeviceName->Buffer = ExAllocatePool(PagedPool, lpDeviceName->MaximumLength + sizeof(WCHAR));
if ( lpDeviceName->Buffer == NULL) {
DebugPrint(("Couldn't allocate memory for device name\n"));
return FALSE;
}
RtlZeroMemory( lpDeviceName->Buffer, lpDeviceName->MaximumLength + sizeof(WCHAR));
// now, the size of deviceObjName.Buffer is (128 + 1)(WCHAR)
RtlAppendUnicodeToString( lpDeviceName, DEVICE_NAME); // L"\\Device\\VSer"
// 处理 lpDeviceName 的后缀序号
RtlInitUnicodeString(&instanceStr, NULL);
instanceStr.MaximumLength = sizeof(instanceNumberBuffer);
instanceStr.Buffer = instanceNumberBuffer; // 20 WCHAR
currentInstance++;
RtlIntegerToUnicodeString( currentInstance, 10, &instanceStr);
RtlAppendUnicodeStringToString( lpDeviceName, &instanceStr);
DebugPrint(("DeviceName:\n"));
DebugPrint(("----------- %ws\n", lpDeviceName->Buffer ));
return TRUE;
}
NTSTATUS
SerialUndoExternalNaming(
IN PDEVICE_EXTENSION deviceExtension
)
/*++
Routine Description:
Delete or unregister external name, SymbolicLinkName and DOS name.
Arguments:
deviceExtension - pointer to a device object extension.
Return Value:
NT status code.
--*/
{
NTSTATUS status = STATUS_SUCCESS;
if( deviceExtension->CreatedSymbolicLink ){
IoDeleteSymbolicLink( &deviceExtension->SymbolicLinkName );
ExFreePool( deviceExtension->SymbolicLinkName.Buffer );
deviceExtension->SymbolicLinkName.Buffer = NULL;
deviceExtension->CreatedSymbolicLink = FALSE;
}else{
ASSERT( deviceExtension->SymbolicLinkName.Buffer == NULL );
}
ASSERT( deviceExtension->DeviceName.Buffer != NULL );
if( deviceExtension->CreatedSerialCommEntry ){
status = RtlDeleteRegistryValue( RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP, deviceExtension->DeviceName.Buffer );
if( !NT_SUCCESS( status ) ){
DebugPrint(("RtlDeleteRegistryValue device map failed\n"));
}
ExFreePool( deviceExtension->DosName.Buffer );
deviceExtension->DosName.Buffer = NULL;
deviceExtension->CreatedSerialCommEntry = FALSE;
}else{
ASSERT( deviceExtension->DosName.Buffer == NULL );
}
return status;
}
NTSTATUS
SerialRemoveDevObj(IN PDEVICE_OBJECT PDevObj)
/*++
Routine Description:
Removes a serial device object from the system.
Arguments:
PDevObj - A pointer to the Device Object we want removed.
Return Value:
Always TRUE
--*/
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)PDevObj->DeviceExtension;
PAGED_CODE();
DebugPrint(("SERIAL: Enter SerialRemoveDevObj\n"));
//
// Free memory allocated in the extension
//
if (pDevExt->DeviceName.Buffer != NULL) {
ExFreePool(pDevExt->DeviceName.Buffer);
}
if (pDevExt->SymbolicLinkName.Buffer != NULL) {
ExFreePool(pDevExt->SymbolicLinkName.Buffer);
}
if (pDevExt->DosName.Buffer != NULL) {
ExFreePool(pDevExt->DosName.Buffer);
}
DebugPrint(("SERIAL: Leave SerialRemoveDevObj\n"));
return STATUS_SUCCESS;
}
NTSTATUS CompleteRequest(IN PIRP Irp, IN NTSTATUS status, IN ULONG info)
{
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = info;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
VOID
Unload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
Free all the allocated resources in DriverEntry, etc.
Arguments:
DriverObject - pointer to a driver object.
Return Value:
VOID.
--*/
{
PAGED_CODE ();
//
// The device object(s) should be NULL now
// (since we unload, all the devices objects associated with this
// driver must be deleted.
//
DebugPrint( ("Is unloading......\n") );
ASSERT(DriverObject->DeviceObject == NULL);
//
// We should not be unloaded until all the devices we control
// have been removed from our queue.
//
DebugPrint (("Unload: unload\n"));
return;
}
LONG
SampleIoIncrement(
IN OUT PDEVICE_EXTENSION DeviceExtension
)
/* ++
Routine Description:
Increment deviceExtension->OutStandingIO within the scope of IOCountLock.
Arguments:
deviceExtension - pointer to a device object extension.
Return Value:
deviceExtension->OutStandingIO
--*/
{
LONG result = 0;
KIRQL oldIrql;
KeAcquireSpinLock(&DeviceExtension->IOCountLock, &oldIrql);
result = InterlockedIncrement(&DeviceExtension->OutStandingIO);
//
// when OutStandingIO bumps from 1 to 2, clear the StopEvent
//
if(result == 2) {
KeClearEvent(&DeviceExtension->StopEvent);
}
KeReleaseSpinLock(&DeviceExtension->IOCountLock, oldIrql);
DebugPrint(("SampleIncrement: %d\n", result));
return result;
}
LONG
SampleIoDecrement(
IN OUT PDEVICE_EXTENSION DeviceExtension
)
/*++
Routine Description:
Decrement deviceExtension->OutStandingIO within the scope of IOCountLock.
Arguments:
deviceExtension - pointer to a device object extension.
Return Value:
deviceExtension->OutStandingIO
--*/
{
LONG result = 0;
KIRQL oldIrql;
KeAcquireSpinLock(&DeviceExtension->IOCountLock, &oldIrql);
result = InterlockedDecrement(&DeviceExtension->OutStandingIO);
if(result == 1) {
KeSetEvent(&DeviceExtension->StopEvent, IO_NO_INCREMENT, FALSE);
}
if(result == 0) {
ASSERT(Removed == DeviceExtension->DevicePnPState);
KeSetEvent(&DeviceExtension->RemoveEvent, IO_NO_INCREMENT, FALSE);
}
KeReleaseSpinLock(&DeviceExtension->IOCountLock, oldIrql);
DebugPrint(("SampleIoDecrement: %d\n", result));
return result;
}