/*++ Copyright (c) 1991 - 2002 Microsoft Corporation Module Name: ## ## ### ##### #### ### ##### #### ##### ##### ### ### ## # ## ## ## ## # ## ## ## # ## ## ## ## ######## ### ## ## ## ### ## ## ## ## ## ## ## # ### ## ### ## ## ## ### ## ## ## ## ## ## ## # # ## ### ## ## ## ### ##### ## ##### ##### # ## # ## ## ## ## # ## ## ## ## # ## ## # ## ### ##### #### ### ## ## #### ## ## Abstract: This module contains the entire implementation of the Microsoft virtual display miniport driver. @@BEGIN_DDKSPLIT Author: Wesley Witt (wesw) 1-Oct-2001 @@END_DDKSPLIT Environment: Kernel mode only. Notes: --*/ #include "msdisp.h" #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,DriverEntry) #endif NTSTATUS MsDispHwInitialize( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID DeviceExtensionIn, IN PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResources, IN ULONG PartialResourceCount ) /*++ Routine Description: This routine is the driver's entry point, called by the I/O system to load the driver. The driver's entry points are initialized and a mutex to control paging is initialized. In DBG mode, this routine also examines the registry for special debug parameters. Arguments: DeviceObject - Miniport's device object Irp - Current IRP in progress DeviceExtensionIn - Miniport's device extension PartialResources - List of resources that are assigned to the miniport PartialResourceCount - Number of assigned resources Return Value: NT status code --*/ { PDEVICE_EXTENSION DeviceExtension; UNICODE_STRING EventName; DeviceExtension = (PDEVICE_EXTENSION) DeviceExtensionIn; DeviceExtension->DeviceObject = DeviceObject; KeInitializeMutex( &DeviceExtension->DeviceLock, 0 ); DeviceExtension->DisplayBufferLength = SA_DISPLAY_MAX_BITMAP_SIZE; DeviceExtension->DisplayBuffer = (PUCHAR) SaPortAllocatePool( DeviceExtension, DeviceExtension->DisplayBufferLength+128 ); if (DeviceExtension->DisplayBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory( DeviceExtension->DisplayBuffer, DeviceExtension->DisplayBufferLength+128 ); return STATUS_SUCCESS; } NTSTATUS MsDispCreate( IN PVOID DeviceExtensionIn, IN PIRP Irp, IN PVOID FsContextIn ) { PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) DeviceExtensionIn; PMSDISP_FSCONTEXT FsContext = (PMSDISP_FSCONTEXT) FsContextIn; FsContext->HasLockedPages = 0; return STATUS_SUCCESS; } NTSTATUS MsDispClose( IN PVOID DeviceExtensionIn, IN PIRP Irp, IN PVOID FsContextIn ) { PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) DeviceExtensionIn; PMSDISP_FSCONTEXT FsContext = (PMSDISP_FSCONTEXT) FsContextIn; if (FsContext->HasLockedPages) { KeAcquireMutex( &DeviceExtension->DeviceLock ); IoFreeMdl( FsContext->Mdl ); KeReleaseMutex( &DeviceExtension->DeviceLock, FALSE ); } return STATUS_SUCCESS; } VOID MsDispCreateEventsWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) /*++ Routine Description: This delayed work routine creates the necessary events used to communicate with the user mode application. Arguments: DeviceObject - Display device object Context - Context pointer Return Value: None. --*/ { PMSDISP_WORK_ITEM WorkItem = (PMSDISP_WORK_ITEM)Context; PDEVICE_EXTENSION DeviceExtension = WorkItem->DeviceExtension; NTSTATUS Status; WorkItem->Status = STATUS_SUCCESS; DeviceExtension->EventHandle = NULL; DeviceExtension->Event = NULL; KeAcquireMutex( &DeviceExtension->DeviceLock ); Status = SaPortCreateBasenamedEvent( DeviceExtension, MSDISP_EVENT_NAME, &DeviceExtension->Event, &DeviceExtension->EventHandle ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( SA_DEVICE_DISPLAY, "SaPortCreateBasenamedEvent failed", Status ); } WorkItem->Status = Status; KeReleaseMutex( &DeviceExtension->DeviceLock, FALSE ); IoFreeWorkItem( WorkItem->WorkItem ); KeSetEvent( &WorkItem->Event, IO_NO_INCREMENT, FALSE ); } NTSTATUS MsDispDeviceIoctl( IN PVOID DeviceExtensionIn, IN PIRP Irp, IN PVOID FsContextIn, IN ULONG FunctionCode, IN PVOID InputBuffer, IN ULONG InputBufferLength, IN PVOID OutputBuffer, IN ULONG OutputBufferLength ) /*++ Routine Description: This routine processes the device control requests for the local display miniport. Arguments: DeviceExtension - Miniport's device extension FunctionCode - Device control function code InputBuffer - Pointer to the user's input buffer InputBufferLength - Length in bytes of the input buffer OutputBuffer - Pointer to the user's output buffer OutputBufferLength - Length in bytes of the output buffer Return Value: NT status code. --*/ { NTSTATUS Status = STATUS_SUCCESS; PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) DeviceExtensionIn; PSA_DISPLAY_CAPS DeviceCaps; PMSDISP_BUFFER_DATA BufferData; PMSDISP_FSCONTEXT FsContext; MSDISP_WORK_ITEM WorkItem; switch (FunctionCode) { case FUNC_SA_GET_VERSION: *((PULONG)OutputBuffer) = SA_INTERFACE_VERSION; break; case FUNC_SA_GET_CAPABILITIES: DeviceCaps = (PSA_DISPLAY_CAPS)OutputBuffer; DeviceCaps->SizeOfStruct = sizeof(SA_DISPLAY_CAPS); DeviceCaps->DisplayType = SA_DISPLAY_TYPE_BIT_MAPPED_LCD; DeviceCaps->CharacterSet = SA_DISPLAY_CHAR_ASCII; DeviceCaps->DisplayHeight = DISPLAY_HEIGHT; DeviceCaps->DisplayWidth = DISPLAY_WIDTH; break; case FUNC_VDRIVER_INIT: if (DeviceExtension->Event == NULL) { WorkItem.DeviceExtension = DeviceExtension; WorkItem.Status = 0; WorkItem.WorkItem = IoAllocateWorkItem( DeviceExtension->DeviceObject ); if (WorkItem.WorkItem) { KeInitializeEvent( &WorkItem.Event, NotificationEvent, FALSE ); IoQueueWorkItem( WorkItem.WorkItem, MsDispCreateEventsWorker, DelayedWorkQueue, &WorkItem ); KeWaitForSingleObject( &WorkItem.Event, Executive, KernelMode, FALSE, NULL ); } else { WorkItem.Status = STATUS_INSUFFICIENT_RESOURCES; } } else { WorkItem.Status = STATUS_SUCCESS; } if (WorkItem.Status == STATUS_SUCCESS) { BufferData = (PMSDISP_BUFFER_DATA) OutputBuffer; FsContext = (PMSDISP_FSCONTEXT) FsContextIn; FsContext->Mdl = IoAllocateMdl( DeviceExtension->DisplayBuffer, DeviceExtension->DisplayBufferLength, FALSE, TRUE, NULL ); if (FsContext->Mdl) { MmBuildMdlForNonPagedPool( FsContext->Mdl ); BufferData->DisplayBuffer = MmMapLockedPagesSpecifyCache( FsContext->Mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority ); if (BufferData->DisplayBuffer == NULL) { IoFreeMdl( FsContext->Mdl ); Status = STATUS_INSUFFICIENT_RESOURCES; REPORT_ERROR( SA_DEVICE_DISPLAY, "MmMapLockedPagesSpecifyCache failed", Status ); } else { FsContext->HasLockedPages = 1; } } else { Status = STATUS_INSUFFICIENT_RESOURCES; REPORT_ERROR( SA_DEVICE_DISPLAY, "IoAllocateMdl failed", Status ); } } else { Status = WorkItem.Status; } break; default: Status = STATUS_NOT_SUPPORTED; REPORT_ERROR( SA_DEVICE_DISPLAY, "Unsupported device control", Status ); break; } return Status; } VOID MsDispWriteWorker( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context ) /*++ Routine Description: This delayed work routine completes a write operation. Arguments: DeviceObject - Display device object Context - Context pointer Return Value: None. --*/ { PMSDISP_WORK_ITEM WorkItem = (PMSDISP_WORK_ITEM)Context; PDEVICE_EXTENSION DeviceExtension = WorkItem->DeviceExtension; NTSTATUS Status; KeAcquireMutex( &DeviceExtension->DeviceLock ); RtlZeroMemory( DeviceExtension->DisplayBuffer, DeviceExtension->DisplayBufferLength ); RtlCopyMemory( DeviceExtension->DisplayBuffer, WorkItem->SaDisplay->Bits, (WorkItem->SaDisplay->Width/8)*WorkItem->SaDisplay->Height ); KeReleaseMutex( &DeviceExtension->DeviceLock, FALSE ); if (DeviceExtension->Event) { KeSetEvent( DeviceExtension->Event, 0, FALSE ); } IoFreeWorkItem( WorkItem->WorkItem ); SaPortFreePool( DeviceExtension, WorkItem ); SaPortCompleteRequest( DeviceExtension, NULL, 0, STATUS_SUCCESS, FALSE ); } NTSTATUS MsDispWrite( IN PVOID DeviceExtensionIn, IN PIRP Irp, IN PVOID FsContextIn, IN LONGLONG StartingOffset, IN PVOID DataBuffer, IN ULONG DataBufferLength ) /*++ Routine Description: This routine processes the write request for the local display miniport. Arguments: DeviceExtensionIn - Miniport's device extension StartingOffset - Starting offset for the I/O DataBuffer - Pointer to the data buffer DataBufferLength - Length of the data buffer in bytes Return Value: NT status code. --*/ { PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) DeviceExtensionIn; PSA_DISPLAY_SHOW_MESSAGE SaDisplay = (PSA_DISPLAY_SHOW_MESSAGE) DataBuffer; NTSTATUS Status = STATUS_SUCCESS; PMSDISP_WORK_ITEM WorkItem; if ((ULONG)((SaDisplay->Width/8)*SaDisplay->Height) > DeviceExtension->DisplayBufferLength) { return STATUS_INVALID_PARAMETER; } WorkItem = (PMSDISP_WORK_ITEM) SaPortAllocatePool( DeviceExtension, sizeof(MSDISP_WORK_ITEM) ); if (WorkItem == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } WorkItem->DeviceExtension = DeviceExtension; WorkItem->SaDisplay = (PSA_DISPLAY_SHOW_MESSAGE) DataBuffer; WorkItem->WorkItem = IoAllocateWorkItem( DeviceExtension->DeviceObject ); if (WorkItem->WorkItem) { IoQueueWorkItem( WorkItem->WorkItem, MsDispWriteWorker, DelayedWorkQueue, WorkItem ); Status = STATUS_PENDING; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } return Status; } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine is the driver's entry point, called by the I/O system to load the driver. The driver's entry points are initialized and a mutex to control paging is initialized. In DBG mode, this routine also examines the registry for special debug parameters. Arguments: DriverObject - a pointer to the object that represents this device driver. RegistryPath - a pointer to this driver's key in the Services tree. Return Value: STATUS_SUCCESS --*/ { NTSTATUS Status; SAPORT_INITIALIZATION_DATA SaPortInitData; RtlZeroMemory( &SaPortInitData, sizeof(SAPORT_INITIALIZATION_DATA) ); SaPortInitData.StructSize = sizeof(SAPORT_INITIALIZATION_DATA); SaPortInitData.DeviceType = SA_DEVICE_DISPLAY; SaPortInitData.HwInitialize = MsDispHwInitialize; SaPortInitData.Write = MsDispWrite; SaPortInitData.DeviceIoctl = MsDispDeviceIoctl; SaPortInitData.CloseRoutine = MsDispClose; SaPortInitData.CreateRoutine = MsDispCreate; SaPortInitData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION); SaPortInitData.FileContextSize = sizeof(MSDISP_FSCONTEXT); Status = SaPortInitialize( DriverObject, RegistryPath, &SaPortInitData ); if (!NT_SUCCESS(Status)) { REPORT_ERROR( SA_DEVICE_DISPLAY, "SaPortInitialize failed\n", Status ); return Status; } return STATUS_SUCCESS; }