/* * UNIMODEM "Fakemodem" controllerless driver illustrative example * * (C) 2000 Microsoft Corporation * All Rights Reserved * */ #include "fakemodem.h" #if DBG ULONG DebugFlags=255; #endif UNICODE_STRING DriverEntryRegPath; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,DriverEntry) #pragma alloc_text(PAGE,FakeModemAddDevice) #endif NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { NTSTATUS status; RTL_QUERY_REGISTRY_TABLE paramTable[3]; ULONG zero = 0; ULONG debugLevel = 0; ULONG shouldBreak = 0; PWCHAR path; D_INIT(DbgPrint("FAKEMODEM: DriverEntry\n");) // Since the registry path parameter is a "counted" UNICODE string, it // might not be zero terminated. For a very short time allocate memory // to hold the registry path zero terminated so that we can use it to // delve into the registry. path = ALLOCATE_PAGED_POOL(RegistryPath->Length+sizeof(WCHAR)); if (path != NULL) { RtlZeroMemory(¶mTable[0],sizeof(paramTable)); RtlZeroMemory(path,RegistryPath->Length+sizeof(WCHAR)); RtlMoveMemory(path,RegistryPath->Buffer,RegistryPath->Length); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[0].Name = L"BreakOnEntry"; paramTable[0].EntryContext = &shouldBreak; paramTable[0].DefaultType = REG_DWORD; paramTable[0].DefaultData = &zero; paramTable[0].DefaultLength = sizeof(ULONG); paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; paramTable[1].Name = L"DebugFlags"; paramTable[1].EntryContext = &debugLevel; paramTable[1].DefaultType = REG_DWORD; paramTable[1].DefaultData = &zero; paramTable[1].DefaultLength = sizeof(ULONG); // If the Debugflags registry key is not set then // provide full debugging information if (!NT_SUCCESS(RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, path, ¶mTable[0], NULL, NULL))) { shouldBreak = 0; debugLevel = 255; } FREE_POOL(path); } #if DBG DebugFlags = debugLevel; #endif if (shouldBreak) { DbgBreakPoint(); } // Pnp driver entry point DriverObject->DriverExtension->AddDevice = FakeModemAddDevice; // Initialize the driver object with driver's entry points DriverObject->DriverUnload = FakeModemUnload; DriverObject->MajorFunction[IRP_MJ_CREATE] = FakeModemOpen; DriverObject->MajorFunction[IRP_MJ_CLOSE] = FakeModemClose; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = FakeModemCleanup; DriverObject->MajorFunction[IRP_MJ_WRITE] = FakeModemWrite; DriverObject->MajorFunction[IRP_MJ_READ] = FakeModemRead; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = FakeModemIoControl; DriverObject->MajorFunction[IRP_MJ_PNP] = FakeModemPnP; DriverObject->MajorFunction[IRP_MJ_POWER] = FakeModemPower; D_INIT(DbgPrint("FAKEMODEM: End of DriverEntry\n");) return STATUS_SUCCESS; } VOID FakeModemUnload( IN PDRIVER_OBJECT DriverObject ) { D_INIT(DbgPrint("FAKEMODEM: FakeModemUnload()\n");) return; } NTSTATUS FakeModemAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo ) { NTSTATUS status=STATUS_SUCCESS; PDEVICE_OBJECT Fdo; PDEVICE_EXTENSION DeviceExtension; UNICODE_STRING DeviceName; D_INIT(DbgPrint("FAKEMODEM: Fakemodem Add Device\n");) // Create our functional device object (FDO) status=IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, FILE_DEVICE_SERIAL_PORT, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &Fdo); if (status != STATUS_SUCCESS) { return status; } Fdo->Flags |= DO_BUFFERED_IO; DeviceExtension = Fdo->DeviceExtension; DeviceExtension->DeviceObject = Fdo; // Attach our FDO to the PDO supplied DeviceExtension->LowerDevice = IoAttachDeviceToDeviceStack(Fdo, Pdo); if (NULL == DeviceExtension->LowerDevice) { // Could not attach IoDeleteDevice(Fdo); return STATUS_UNSUCCESSFUL; } // Try to create a ComX for it. don't care if it fails // modem.sys creates a name for device that unimodem will use FakeModemHandleSymbolicLink(Pdo, TRUE, &DeviceExtension->InterfaceNameString, Fdo); // Initialise the spinlock KeInitializeSpinLock(&DeviceExtension->SpinLock); // Initialize the device extension DeviceExtension->ReferenceCount=1; DeviceExtension->Removing=FALSE; DeviceExtension->Started=FALSE; DeviceExtension->OpenCount=0; KeInitializeEvent(&DeviceExtension->RemoveEvent, NotificationEvent, FALSE); // Initialize the read and write queues InitializeListHead(&DeviceExtension->ReadQueue); DeviceExtension->CurrentReadIrp=NULL; InitializeListHead(&DeviceExtension->WriteQueue); DeviceExtension->CurrentWriteIrp=NULL; InitializeListHead(&DeviceExtension->MaskQueue); DeviceExtension->CurrentMaskIrp=NULL; // Clear this flag so the device object can be used Fdo->Flags &= ~(DO_DEVICE_INITIALIZING); return STATUS_SUCCESS; } NTSTATUS GetRegistryKeyValue ( IN HANDLE Handle, IN PWCHAR KeyNameString, IN ULONG KeyNameStringLength, IN PVOID Data, IN ULONG DataLength ) { UNICODE_STRING keyName; ULONG length; PKEY_VALUE_FULL_INFORMATION fullInfo; NTSTATUS ntStatus = STATUS_NO_MEMORY; RtlInitUnicodeString (&keyName, KeyNameString); length = sizeof(KEY_VALUE_FULL_INFORMATION) + KeyNameStringLength + DataLength; fullInfo = ExAllocatePool(PagedPool, length); if (fullInfo) { ntStatus = ZwQueryValueKey(Handle, &keyName, KeyValueFullInformation, fullInfo, length, &length); if (NT_SUCCESS(ntStatus)) { // If there is enough room in the data buffer, copy the output if (DataLength >= fullInfo->DataLength) { RtlCopyMemory(Data, ((PUCHAR) fullInfo) + fullInfo->DataOffset, fullInfo->DataLength); } } ExFreePool(fullInfo); } return ntStatus; } NTSTATUS FakeModemHandleSymbolicLink( PDEVICE_OBJECT Pdo, BOOLEAN Create, PUNICODE_STRING InterfaceName, PDEVICE_OBJECT Fdo ) { UNICODE_STRING SymbolicLink; ULONG StringLength; NTSTATUS Status; WCHAR ComPort[80]; HANDLE keyHandle; RTL_QUERY_REGISTRY_TABLE paramTable[1]; D_INIT(DbgPrint("FAKEMODEM: HandleSymbolicLink\n");) Status = IoOpenDeviceRegistryKey(Pdo, PLUGPLAY_REGKEY_DEVICE, STANDARD_RIGHTS_READ, &keyHandle); SymbolicLink.Length=0; SymbolicLink.MaximumLength=sizeof(WCHAR)*256; SymbolicLink.Buffer=ExAllocatePool(PagedPool, SymbolicLink.MaximumLength+sizeof(WCHAR)); if (SymbolicLink.Buffer == NULL) { ZwClose(keyHandle); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(SymbolicLink.Buffer, SymbolicLink.MaximumLength); RtlAppendUnicodeToString(&SymbolicLink, L"\\"); RtlAppendUnicodeToString(&SymbolicLink, OBJECT_DIRECTORY); RtlAppendUnicodeToString(&SymbolicLink, L"\\"); Status=GetRegistryKeyValue(keyHandle, L"PortName", sizeof(L"PortName"), ComPort, sizeof(ComPort)); D_INIT(DbgPrint("FAKEMODEM: PortName %ws\n",ComPort);) if (Status != STATUS_SUCCESS) { ExFreePool(SymbolicLink.Buffer); ZwClose(keyHandle); return Status; } RtlAppendUnicodeToString(&SymbolicLink, ComPort); ZwClose(keyHandle); if (Create) { UNICODE_STRING PdoName; PdoName.Length=0; PdoName.MaximumLength=sizeof(WCHAR)*256; PdoName.Buffer=ExAllocatePool(PagedPool, PdoName.MaximumLength+sizeof(WCHAR)); if (PdoName.Buffer == NULL) { ExFreePool(SymbolicLink.Buffer); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(PdoName.Buffer,PdoName.MaximumLength); Status=IoGetDeviceProperty(Pdo, DevicePropertyPhysicalDeviceObjectName, (ULONG)PdoName.MaximumLength, PdoName.Buffer, &StringLength); if (!NT_SUCCESS(Status)) { D_INIT(DbgPrint("FAKEMODEM: IoGetDeviceProperty() failed %08lx\n", Status);) ExFreePool(SymbolicLink.Buffer); return Status; } PdoName.Length+=(USHORT)StringLength-sizeof(UNICODE_NULL); D_INIT(DbgPrint("FAKEMODEM: PdoName: %ws\n",PdoName.Buffer);) Status=IoCreateSymbolicLink(&SymbolicLink, &PdoName); Status=IoRegisterDeviceInterface(Pdo, &GUID_CLASS_MODEM, NULL, InterfaceName); if (NT_SUCCESS(Status)) { IoSetDeviceInterfaceState(InterfaceName, TRUE); } else { D_INIT(DbgPrint("FAKEMODEM: IoRegisterDeviceInterface() failed %08lx\n",Status);) } Status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP, L"SERIALCOMM", PdoName.Buffer, REG_SZ, ComPort, (wcslen(ComPort) + 1) * sizeof(WCHAR)); if (!NT_SUCCESS(Status)) { D_INIT(DbgPrint("FAKEMODEM: RtlWriteRegistryValue() failed %08lx\n",Status);) ExFreePool(SymbolicLink.Buffer); ExFreePool(PdoName.Buffer); return Status; } ExFreePool(PdoName.Buffer); } else { Status=IoDeleteSymbolicLink(&SymbolicLink); D_INIT(DbgPrint("FAKEMODEM: Deleted symbolic link\n");) } ExFreePool(SymbolicLink.Buffer); D_INIT(DbgPrint("FAKEMODEM: End of handle symbolic link\n");) return Status; } NTSTATUS QueryDeviceCaps( PDEVICE_OBJECT Pdo, PDEVICE_CAPABILITIES Capabilities ) { PDEVICE_OBJECT deviceObject=Pdo; PIRP irp; PIO_STACK_LOCATION NextSp; KEVENT Event; NTSTATUS Status; // Get a pointer to the top most device object in the stack of // devices, beginning with the deviceObject. while (deviceObject->AttachedDevice) { deviceObject = deviceObject->AttachedDevice; } // Begin by allocating the IRP for this request. Do not charge // quota to the current process for this IRP. irp = IoAllocateIrp( #if DBG (UCHAR)(deviceObject->StackSize+1), #else deviceObject->StackSize, #endif FALSE); if (irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } #if DBG { // Setup a current stack location, so the debug code can see the // MJ value PIO_STACK_LOCATION irpSp=IoGetNextIrpStackLocation(irp); irpSp->MajorFunction=IRP_MJ_PNP; IoSetNextIrpStackLocation(irp); } #endif irp->IoStatus.Status = STATUS_NOT_SUPPORTED; irp->IoStatus.Information = 0; RtlZeroMemory(Capabilities,sizeof(DEVICE_CAPABILITIES)); Capabilities->Size=sizeof(DEVICE_CAPABILITIES); Capabilities->Version=1; Capabilities->Address=-1; Capabilities->UINumber=-1; // Get a pointer to the stack location of the first driver which will be // invoked. This is where the function codes and parameters are set. NextSp = IoGetNextIrpStackLocation(irp); NextSp->MajorFunction=IRP_MJ_PNP; NextSp->MinorFunction=IRP_MN_QUERY_CAPABILITIES; NextSp->Parameters.DeviceCapabilities.Capabilities=Capabilities; Status=WaitForLowerDriverToCompleteIrp(deviceObject, irp, FALSE ); IoFreeIrp(irp); return Status; } NTSTATUS ModemSetRegistryKeyValue( IN PDEVICE_OBJECT Pdo, IN ULONG DevInstKeyType, IN PWCHAR KeyNameString, IN ULONG DataType, IN PVOID Data, IN ULONG DataLength) { NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES; HANDLE Handle; UNICODE_STRING keyName; PAGED_CODE(); D_ERROR(DbgPrint("MODEM: Current IRQL %d\n",KeGetCurrentIrql());) ntStatus = IoOpenDeviceRegistryKey(Pdo, DevInstKeyType, KEY_ALL_ACCESS, &Handle); if (NT_SUCCESS(ntStatus)) { RtlInitUnicodeString(&keyName,KeyNameString); ntStatus = ZwSetValueKey(Handle, &keyName, 0, DataType, Data, DataLength); if (!NT_SUCCESS(ntStatus)) { D_ERROR(DbgPrint("MODEM: Could not set value, %08lx\n",ntStatus);) } } else { ZwClose(Handle); D_ERROR(DbgPrint("MODEM: Could not open dev registry key, %08lx\n", ntStatus);) } return ntStatus; } NTSTATUS ModemGetRegistryKeyValue ( IN PDEVICE_OBJECT Pdo, IN ULONG DevInstKeyType, IN PWCHAR KeyNameString, IN PVOID Data, IN ULONG DataLength ) { UNICODE_STRING keyName; ULONG length; PKEY_VALUE_PARTIAL_INFORMATION PartialInfo; NTSTATUS ntStatus = STATUS_INSUFFICIENT_RESOURCES; HANDLE Handle; PAGED_CODE(); ntStatus = IoOpenDeviceRegistryKey(Pdo, DevInstKeyType, STANDARD_RIGHTS_READ, &Handle); if (NT_SUCCESS(ntStatus)) { RtlInitUnicodeString (&keyName, KeyNameString); length = sizeof(KEY_VALUE_FULL_INFORMATION) + DataLength; PartialInfo = ALLOCATE_PAGED_POOL(length); if (PartialInfo) { ntStatus = ZwQueryValueKey (Handle, &keyName, KeyValuePartialInformation, PartialInfo, length, &length); if (NT_SUCCESS(ntStatus)) { // // If there is enough room in the data buffer, copy the output // if (DataLength >= PartialInfo->DataLength) { RtlCopyMemory (Data, PartialInfo->Data, PartialInfo->DataLength); } } else { D_ERROR(DbgPrint("MODEM: could not query value, %08lx\n", ntStatus);) } FREE_POOL(PartialInfo); } ZwClose(Handle); } else { D_ERROR(DbgPrint("MODEM: could open device reg key, %08lx\n",ntStatus);) } return ntStatus; }