/*++ Copyright (c) 2003 Aleksandrov O.E. All rights Reserved Module Name: mi1201.h Abstract: Include file for MI1201-AGM Port I/O Driver ******************************************************** ORIGINATED from Copyright (c) 1990-2000 Microsoft Corporation, All Rights Reserved Module Name: genport.c Abstract: Generic Port I/O driver for Windows 2000 Author: Author: Robert R. Howell January 8, 1993 Environment: Kernel mode Revision History: Robert B. Nelson (Microsoft) January 12, 1993 Cleaned up comments Enabled and tested resource reporting Added code to retrieve I/O address and port count from the Registry. Robert B. Nelson (Microsoft) March 1, 1993 Added support for byte, word, and long I/O. Added support for MIPS. Fixed resource reporting. Robert B. Nelson (Microsoft) May 1, 1993 Fixed port number validation. Robert B. Nelson (Microsoft) Oct 25, 1993 Fixed MIPS support. Eliyas Yakub Fixed AddressSpace Bug Nov 30, 1997 Eliyas Yakub Converted to Windows 2000 Dec 29, 1998 Fixed bugs Feb 17, 2000 --*/ #include "MI1201Get_SetControlInfo.h" #include "mi1201.h" // Разрешение ВЫГРУЗКИ СТРАНИЦ на диск (paging) #ifdef ALLOC_PRAGMA // насколько я понял при запуске BUILD это ОПРЕДЕЛЕНО # pragma alloc_text (INIT, DriverEntry) // Names the code section where the specified function definitions are to reside. The pragma must occur between a function declarator and the function definition for the named functions. # pragma alloc_text (PAGE, GpdAddDevice) # pragma alloc_text (PAGE, GpdDispatchPnp) # pragma alloc_text (PAGE, GpdDispatchSystemControl) # pragma alloc_text (PAGE, GpdUnload) # pragma alloc_text (PAGE, GpdDispatch) # pragma alloc_text (PAGE, GpdIoctlReadPort) # pragma alloc_text (PAGE, GpdIoctlWritePort) # pragma alloc_text (PAGE, GpdStartDevice) //------------- # pragma alloc_text (PAGE, myUnmapIoSpaces) # pragma alloc_text (PAGE, TranslatePort) # pragma alloc_text (PAGE, GetPortSize) # pragma alloc_text (PAGE, GpdIoctlReadPortBuffer) # pragma alloc_text (PAGE, GpdIoctlWritePortBuffer) # pragma alloc_text (PAGE, MI1201IoctlAbort) # pragma alloc_text (PAGE, AbortSet) # pragma alloc_text (PAGE, AbortState) # pragma alloc_text (PAGE, WaitOnPort) # pragma alloc_text (PAGE, GpdIoctlWaitPort) # pragma alloc_text (PAGE, WaitOnPortEx) //# pragma alloc_text (PAGE, WriteAndWaitOnPort) # pragma alloc_text (PAGE, GpdIoctlWriteAndWaitPort) # pragma alloc_text (PAGE, TranslatePortEx) # pragma alloc_text (PAGE, ProcessMultifunctionIrpBuffer) # pragma alloc_text (PAGE, CheckMultifunctionIrpBuffer) # pragma alloc_text (PAGE, GpdIoctlCheckMultifunctionInputBuffer) # pragma alloc_text (PAGE, GpdIoctlProcessMultifunctionInputBuffer) #elif ------ ??? Запрет ВЫГРУЗКИ СТРАНИЦ на диск? #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 --*/ { UNREFERENCED_PARAMETER (RegistryPath); DebugPrint (("Entered Driver Entry\n")); // // Create dispatch points for the IRPs. // DriverObject->MajorFunction[IRP_MJ_CREATE] = GpdDispatch; DriverObject->MajorFunction[IRP_MJ_CLOSE] = GpdDispatch; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = GpdDispatch; DriverObject->DriverUnload = GpdUnload; DriverObject->MajorFunction[IRP_MJ_PNP] = GpdDispatchPnp; DriverObject->MajorFunction[IRP_MJ_POWER] = GpdDispatchPower; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = GpdDispatchSystemControl; DriverObject->DriverExtension->AddDevice = GpdAddDevice; return STATUS_SUCCESS; } NTSTATUS GpdAddDevice( 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 functional 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 (my??? DriverObject) - 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; PLOCAL_DEVICE_INFO deviceInfo; UNICODE_STRING ntDeviceName; UNICODE_STRING win32DeviceName, win32OldDeviceName; PAGED_CODE(); RtlInitUnicodeString(&ntDeviceName, GPD_DEVICE_NAME); // // Create a device object. // status = IoCreateDevice (DriverObject, sizeof (LOCAL_DEVICE_INFO), &ntDeviceName, GPD_TYPE, 0, FALSE, &deviceObject); if (!NT_SUCCESS (status)) { // // Either not enough memory to create a deviceobject or another // deviceobject with the same name exits. This could happen // if you install another instance of this device. // return status; } // создать символическую ссылку на имя драйвера RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); status = IoCreateSymbolicLink( &win32DeviceName, &ntDeviceName ); // создать символическую ссылку на старое имя драйвера (Для совместимости со старыми программами) RtlInitUnicodeString(&win32OldDeviceName, OLD_DOS_DEVICE_NAME); status = IoCreateSymbolicLink( &win32OldDeviceName, &ntDeviceName ); if (!NT_SUCCESS(status)) // If we we couldn't create the link then { // abort installation. IoDeleteDevice(deviceObject); return status; } deviceInfo = (PLOCAL_DEVICE_INFO) deviceObject->DeviceExtension; deviceInfo->NextLowerDriver = IoAttachDeviceToDeviceStack ( deviceObject, PhysicalDeviceObject); if(NULL == deviceInfo->NextLowerDriver) { IoDeleteSymbolicLink(&win32DeviceName); IoDeleteSymbolicLink(&win32OldDeviceName); IoDeleteDevice(deviceObject); return STATUS_NO_SUCH_DEVICE; } IoInitializeRemoveLock (&deviceInfo->RemoveLock , PORTIO_TAG, 2, // MaxLockedMinutes 5); // HighWatermark, this parameter is // used only on checked build. // // Set the flag if the device is not holding a pagefile // crashdump file or hibernate file. // deviceObject->Flags |= DO_POWER_PAGABLE; deviceInfo->DeviceObject = deviceObject; deviceInfo->Removed = FALSE; deviceInfo->Started = FALSE; deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; //--------------------------------------- // Инициализирует структуы tInitialPortInfo - физические диапазоны адресов портов и // tTransPortInfo - транслированные диапазоны адресов портов начальными значениями InitInitialPortInfo((ptInitialPortInfo)&deviceInfo->InitialPortRangesInfo, cMaxRangeNumber); InitTranslatedPortInfo((ptTransPortInfo)&deviceInfo->TranslatedPortRangesInfo, cMaxRangeNumber); // инициализировать блокировку параллельного чтения/записи ExInitializeFastMutex(&deviceInfo->LastRead.LockMutex); ExInitializeFastMutex(&deviceInfo->LastWrite.LockMutex); // Инициализирует последний проверенный на принадлежность диапазону порт начальными значениямии. // LastPortSize=0 означает что порт недействителен. deviceInfo->LastRead.Initial.PortType = 0xFF; // последняя операция чтения deviceInfo->LastWrite.Initial.PortType = 0xFF; // последняя операция чтения // Инициализацич события немедленного завершения всех операций KeInitializeEvent(&deviceInfo->AbortEvent, NotificationEvent, TRUE); deviceInfo->AbortStateBeforeRemoving=FALSE; //--------------------------------------- DebugPrint(("AddDevice: %p to %p->%p \n", deviceObject, deviceInfo->NextLowerDriver, PhysicalDeviceObject)); return STATUS_SUCCESS; } //---------------------------------- NTSTATUS GpdCompletionRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) /*++ Routine Description: The completion routine for plug & play irps that needs to be processed first by the lower drivers. Arguments: DeviceObject - pointer to a device object. Irp - pointer to an I/O Request Packet. Context - pointer to an event object. Return Value: NT status code --*/ { PKEVENT event; event = (PKEVENT) Context; UNREFERENCED_PARAMETER(DeviceObject); if (Irp->PendingReturned) { IoMarkIrpPending(Irp); } // // We could switch on the major and minor functions of the IRP to perform // different functions, but we know that Context is an event that needs // to be set. // KeSetEvent(event, 0, FALSE); // // Allows the caller to reuse the IRP // return STATUS_MORE_PROCESSING_REQUIRED; } NTSTATUS myUnmapIoSpaces( IN OUT tTransPortInfo *PortInfo ) /*++ Описание процедуры: Освобождение ранее захваченных системных ресурсов (портов). Arguments: PortInfo - указатель на список ТРАНСЛИРОВАННЫХ портов в формате tTransPortInfo. Return Value: NT status code --*/ { ULONG i; if (PortInfo->UsedRangeCount > PortInfo->RangeCount){ return STATUS_UNSUCCESSFUL; } else { for (i=0; iUsedRangeCount; i++){ if (PortInfo->Range[i].PortWasMapped) { MmUnmapIoSpace(PortInfo->Range[i].PortFirst, PortInfo->Range[i].PortCount); PortInfo->Range[i].PortWasMapped = FALSE; } } } return STATUS_SUCCESS; } NTSTATUS GpdDispatchPnp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: The plug and play dispatch routines. Most of these the driver will completely ignore. In all cases it must pass the IRP to the next lower driver. Arguments: DeviceObject - pointer to a device object. Irp - pointer to an I/O Request Packet. Return Value: NT status code --*/ { PIO_STACK_LOCATION irpStack; NTSTATUS status = STATUS_SUCCESS; KEVENT event; UNICODE_STRING win32DeviceName; PLOCAL_DEVICE_INFO deviceInfo; PAGED_CODE(); deviceInfo = (PLOCAL_DEVICE_INFO) DeviceObject->DeviceExtension; irpStack = IoGetCurrentIrpStackLocation(Irp); // Остановка обработки длинных запросов, если затребовано удаление драйвера if (deviceInfo->Started) { switch (irpStack->MinorFunction) { case IRP_MN_SURPRISE_REMOVAL: case IRP_MN_REMOVE_DEVICE: deviceInfo->AbortStateBeforeRemoving = AbortSet(deviceInfo,TRUE); break; default: break; } }; status = IoAcquireRemoveLock (&deviceInfo->RemoveLock, Irp); if (!NT_SUCCESS (status)) { Irp->IoStatus.Status = status; IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } DebugPrint(("%s (0x%x)\n",PnPMinorFunctionString(irpStack->MinorFunction), irpStack->MinorFunction)); switch (irpStack->MinorFunction) { case IRP_MN_START_DEVICE: // // The device is starting. // // We cannot touch the device (send it any non pnp irps) until a // start device has been passed down to the lower drivers. // IoCopyCurrentIrpStackLocationToNext(Irp); KeInitializeEvent(&event, NotificationEvent, FALSE ); IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) GpdCompletionRoutine, &event, TRUE, TRUE, TRUE); status = IoCallDriver(deviceInfo->NextLowerDriver, Irp); if (STATUS_PENDING == status) { KeWaitForSingleObject( &event, Executive, // Waiting for reason of a driver KernelMode, // Must be kernelmode if event memory is in stack FALSE, // No allert NULL); // No timeout } if (NT_SUCCESS(status) && NT_SUCCESS(Irp->IoStatus.Status)) { status = GpdStartDevice(DeviceObject, Irp); if(NT_SUCCESS(status)) { // // As we are successfully now back from our start device // we can do work. // deviceInfo->Started = TRUE; deviceInfo->Removed = FALSE; } } // // We must now complete the IRP, since we stopped it in the // completion routine with STATUS_MORE_PROCESSING_REQUIRED. // Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; case IRP_MN_QUERY_STOP_DEVICE: // // Fail the query stop to prevent the system from taking away hardware // resources. If you do support this you must have a queue to hold // incoming requests between stop and subsequent start with new set of // resources. // Irp->IoStatus.Status = status = STATUS_UNSUCCESSFUL; IoCompleteRequest(Irp, IO_NO_INCREMENT); break; case IRP_MN_QUERY_REMOVE_DEVICE: // // The device can be removed without disrupting the machine. // Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceInfo->NextLowerDriver, Irp); break; case IRP_MN_SURPRISE_REMOVAL: // // The device has been unexpectedly removed from the machine // and is no longer available for I/O. Stop all access to the device. // Release any resources associated with the device, but leave the // device object attached to the device stack until the PnP Manager // sends a subsequent IRP_MN_REMOVE_DEVICE request. // You should fail any outstanding I/O to the device. You will // not get a remove until all the handles open to the device // have been closed. // deviceInfo->Removed = TRUE; deviceInfo->Started = FALSE; // Убираем отображение ресурсов myUnmapIoSpaces((ptTransPortInfo)&deviceInfo->TranslatedPortRangesInfo); RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); IoDeleteSymbolicLink(&win32DeviceName); RtlInitUnicodeString(&win32DeviceName, OLD_DOS_DEVICE_NAME); IoDeleteSymbolicLink(&win32DeviceName); IoSkipCurrentIrpStackLocation(Irp); Irp->IoStatus.Status = STATUS_SUCCESS; status = IoCallDriver(deviceInfo->NextLowerDriver, Irp); break; case IRP_MN_REMOVE_DEVICE: // // Relinquish all resources here. // Detach and delete the device object so that // your driver can be unloaded. You get remove // either after query_remove or surprise_remove. // if(!deviceInfo->Removed) { deviceInfo->Removed = TRUE; deviceInfo->Started = FALSE; // Убираем отображение ресурсов myUnmapIoSpaces(&deviceInfo->TranslatedPortRangesInfo.Info); RtlInitUnicodeString(&win32DeviceName, DOS_DEVICE_NAME); IoDeleteSymbolicLink(&win32DeviceName); RtlInitUnicodeString(&win32DeviceName, OLD_DOS_DEVICE_NAME); IoDeleteSymbolicLink(&win32DeviceName); } // // Wait for all outstanding requests to complete // DebugPrint(("Waiting for outstanding requests\n")); IoReleaseRemoveLockAndWait(&deviceInfo->RemoveLock, Irp); Irp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(Irp); status = IoCallDriver(deviceInfo->NextLowerDriver, Irp); IoDetachDevice(deviceInfo->NextLowerDriver); IoDeleteDevice(DeviceObject); return status; case IRP_MN_CANCEL_REMOVE_DEVICE: AbortSet(deviceInfo, deviceInfo->AbortStateBeforeRemoving); deviceInfo->AbortStateBeforeRemoving = FALSE; case IRP_MN_STOP_DEVICE: // Since you failed query stop, you will not get this request. case IRP_MN_CANCEL_STOP_DEVICE: //No action required in this case. Irp->IoStatus.Status = STATUS_SUCCESS; default: // // Please see PnP documentation for use of these IRPs. // IoSkipCurrentIrpStackLocation (Irp); status = IoCallDriver(deviceInfo->NextLowerDriver, Irp); break; } IoReleaseRemoveLock(&deviceInfo->RemoveLock, Irp); return status; } NTSTATUS GpdStartDevice ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: Get the resources, map the resources if required and initialize the device. Arguments: DeviceObject - pointer to a device object. Irp - pointer to an I/O Request Packet. Return Value: NT status code --*/ { NTSTATUS status = STATUS_SUCCESS; PCM_PARTIAL_RESOURCE_DESCRIPTOR resource; PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceTrans; PCM_PARTIAL_RESOURCE_LIST partialResourceList; PCM_PARTIAL_RESOURCE_LIST partialResourceListTranslated; PIO_STACK_LOCATION stack; ULONG i; PLOCAL_DEVICE_INFO deviceInfo; deviceInfo = (PLOCAL_DEVICE_INFO) DeviceObject->DeviceExtension; stack = IoGetCurrentIrpStackLocation (Irp); PAGED_CODE(); // // We need to check that we haven't received a surprise removal // if (deviceInfo->Removed) { // // Some kind of surprise removal arrived. We will fail the IRP // The dispatch routine that called us will take care of // completing the IRP. // return STATUS_DELETE_PENDING; } // Инициализирует структуры tPortInfo начальными значениями InitInitialPortInfo(&deviceInfo->InitialPortRangesInfo.Info, cMaxRangeNumber); InitTranslatedPortInfo(&deviceInfo->TranslatedPortRangesInfo.Info, cMaxRangeNumber); // Инициализирует последний проверенный на принадлежность диапазону порт начальными значениямии. // 0 означает что порт недействителен. deviceInfo->LastRead.Initial.PortType = 0xFF; // последняя операция чтения deviceInfo->LastWrite.Initial.PortType = 0xFF; // последняя операция чтения // // Do whatever initialization needed when starting the device: // gather information about it, update the registry, etc. // if ((NULL == stack->Parameters.StartDevice.AllocatedResources) && (NULL == stack->Parameters.StartDevice.AllocatedResourcesTranslated)) { return STATUS_INSUFFICIENT_RESOURCES; } // // Parameters.StartDevice.AllocatedResources points to a // CM_RESOURCE_LIST describing the hardware resources that // the PnP Manager assigned to the device. This list contains // the resources in raw form. Use the raw resources to program // the device. // partialResourceList = &stack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList; /* Parameters.StartDevice.AllocatedResources содержит ссылку на CM_RESOURCE_LIST typedef struct _CM_RESOURCE_LIST { ULONG Count; CM_FULL_RESOURCE_DESCRIPTOR List[1]; } CM_RESOURCE_LIST, *PCM_RESOURCE_LIST; The CM_RESOURCE_LIST structure specifies all of the system hardware resources assigned to a device. Members Count - The number of elements contained in the List array. For WDM drivers, this value is always 1. List - An array of CM_FULL_RESOURCE_DESCRIPTOR structures. Похоже здесь используется: "For WDM drivers, this value is always 1." */ resource = &partialResourceList->PartialDescriptors[0]; // // Parameters.StartDevice.AllocatedResourcesTranslated points // to a CM_RESOURCE_LIST describing the hardware resources that // the PnP Manager assigned to the device. This list contains // the resources in translated form. Use the translated resources // to connect the interrupt vector, map I/O space, and map memory. // partialResourceListTranslated = &stack->Parameters.StartDevice.AllocatedResourcesTranslated->List[0].PartialResourceList; resourceTrans = &partialResourceListTranslated->PartialDescriptors[0]; // Проверяем количество ресурсов if (partialResourceList->Count > cMaxRangeNumber) { // мы не можем обработать более cMaxRangeNumber ресурсов return STATUS_INSUFFICIENT_RESOURCES; } for (i = 0; i < partialResourceList->Count; i++, resource++, resourceTrans++) { switch (resource->Type) { case CmResourceTypePort: switch (resourceTrans->Type) { case CmResourceTypePort: // Добавление диапазона в структуру deviceInfo->InitialPortRangesInfo с упорядочением по возрастанию AddInitialPortInfoAscending(&deviceInfo->InitialPortRangesInfo.Info, resource->u.Port.Start.LowPart, // адрес первого порта диапазона resource->u.Port.Length // число портов в диапазоне. ); // Добавление диапазона в структуру deviceInfo->TranslatedPortRangesInfo AddTranslatedPortInfo(&deviceInfo->TranslatedPortRangesInfo.Info, (PVOID)resourceTrans->u.Port.Start.LowPart, // адрес первого порта диапазона resourceTrans->u.Port.Length, // число портов в диапазоне. CmResourceTypePort, // HalTranslateBusAddress MemoryType FALSE // If TRUE, we have to unmap on unload ); DebugPrint(("Resource Translated Port: (%x) Length: (%d)\n", resourceTrans->u.Port.Start.LowPart, resourceTrans->u.Port.Length)); break; case CmResourceTypeMemory: // // We need to map the memory // // Добавление диапазона в структуру deviceInfo->InitialPortRangesInfo с упорядочением по возрастанию AddInitialPortInfoAscending(&deviceInfo->InitialPortRangesInfo.Info, resource->u.Memory.Start.LowPart, // адрес первого порта диапазона resource->u.Memory.Length // число портов в диапазоне. ); // Добавление диапазона в структуру deviceInfo->TranslatedPortRangesInfo AddTranslatedPortInfo(&deviceInfo->TranslatedPortRangesInfo.Info, MmMapIoSpace (resourceTrans->u.Memory.Start, // отображаем порты в память resourceTrans->u.Memory.Length, MmNonCached), resourceTrans->u.Memory.Length, // число портов в диапазоне. CmResourceTypeMemory, // HalTranslateBusAddress MemoryType TRUE // If TRUE, we have to unmap on unload ); DebugPrint(("Resource Translated Memory: (%x) Length: (%d)\n", resourceTrans->u.Memory.Start.LowPart, resourceTrans->u.Memory.Length)); break; default: DebugPrint(("Unhandled resource_type (0x%x)\n", resourceTrans->Type)); status = STATUS_UNSUCCESSFUL; TRAP (); } break; case CmResourceTypeMemory: // Добавление диапазона в структуру deviceInfo->InitialPortRangesInfo с упорядочением по возрастанию AddInitialPortInfoAscending(&deviceInfo->InitialPortRangesInfo.Info, resource->u.Memory.Start.LowPart, // адрес первого порта диапазона resource->u.Memory.Length // число портов в диапазоне. ); // Добавление диапазона в структуру deviceInfo->PortRangesInfo с упорядочением по возрастанию AddTranslatedPortInfo(&deviceInfo->TranslatedPortRangesInfo.Info, MmMapIoSpace (resourceTrans->u.Memory.Start, // отображаем порты в память resourceTrans->u.Memory.Length, MmNonCached), resourceTrans->u.Memory.Length, // число портов в диапазоне. CmResourceTypeMemory, // HalTranslateBusAddress MemoryType TRUE // If TRUE, we have to unmap on unload ); DebugPrint(("Resource Translated Memory: (%x) Length: (%d)\n", resourceTrans->u.Memory.Start.LowPart, resourceTrans->u.Memory.Length)); break; case CmResourceTypeInterrupt: default: DebugPrint(("Unhandled resource type (0x%x)\n", resource->Type)); status = STATUS_UNSUCCESSFUL; break; } // end of switch } // end of for // Драйвер не имеет запроса на немедленное прекращение операций KeClearEvent(&deviceInfo->AbortEvent); return status; } NTSTATUS GpdDispatchPower( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch routine for power irps. Does nothing except forwarding the IRP to the next device in the stack. Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the request packet. Return Value: NT Status code --*/ { PLOCAL_DEVICE_INFO deviceInfo; deviceInfo = (PLOCAL_DEVICE_INFO) DeviceObject->DeviceExtension; // // If the device has been removed, the driver should not pass // the IRP down to the next lower driver. // if (deviceInfo->Removed) { PoStartNextPowerIrp(Irp); Irp->IoStatus.Status = STATUS_DELETE_PENDING; IoCompleteRequest(Irp, IO_NO_INCREMENT ); return STATUS_DELETE_PENDING; } PoStartNextPowerIrp(Irp); IoSkipCurrentIrpStackLocation(Irp); return PoCallDriver(deviceInfo->NextLowerDriver, Irp); } NTSTATUS GpdDispatchSystemControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch routine for WMI irps. Does nothing except forwarding the IRP to the next device in the stack. Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the request packet. Return Value: NT Status code --*/ { PLOCAL_DEVICE_INFO deviceInfo; PAGED_CODE(); deviceInfo = (PLOCAL_DEVICE_INFO) DeviceObject->DeviceExtension; IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(deviceInfo->NextLowerDriver, Irp); } VOID GpdUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: Free all the allocated resources, 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 have been deleted. // ASSERT(DriverObject->DeviceObject == NULL); DebugPrint (("unload\n")); return; } BOOLEAN AbortableIOctlc(IN ULONG IoControlCode) { return !( (IOCTL_MI1201_ABORT == IoControlCode) |(IOCTL_MI1201_GET_CONTROL_INFO == IoControlCode) |(IOCTL_MI1201_SET_CONTROL_INFO == IoControlCode) ); } NTSTATUS GpdDispatch( IN PDEVICE_OBJECT pDO, IN PIRP pIrp ) /*++ Routine Description: This routine is the dispatch handler for the driver. It is responsible for processing the IRPs. Arguments: pDO - Pointer to device object. pIrp - Pointer to the current IRP. Return Value: STATUS_SUCCESS if the IRP was processed successfully, otherwise an error indicating the reason for failure. --*/ { PLOCAL_DEVICE_INFO pLDI; PIO_STACK_LOCATION pIrpStack; NTSTATUS Status; PAGED_CODE(); pIrp->IoStatus.Information = 0; pLDI = (PLOCAL_DEVICE_INFO)pDO->DeviceExtension; // Get local info struct DebugPrint (("Entered GpdDispatch. ")); Status = IoAcquireRemoveLock (&pLDI->RemoveLock, pIrp); if (!NT_SUCCESS (Status)) { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = Status; IoCompleteRequest (pIrp, IO_NO_INCREMENT); return Status; } if (!pLDI->Started) { // // We fail all the IRPs that arrive before the device is started. // pIrp->IoStatus.Status = Status = STATUS_DEVICE_NOT_READY; IoCompleteRequest(pIrp, IO_NO_INCREMENT ); IoReleaseRemoveLock(&pLDI->RemoveLock, pIrp); return Status; } pIrpStack = IoGetCurrentIrpStackLocation(pIrp); // Dispatch based on major fcn code. DebugPrint (("MajorFunc=(0x%x)\n",pIrpStack->MajorFunction)); switch (pIrpStack->MajorFunction) { case IRP_MJ_CREATE: case IRP_MJ_CLOSE: // We don't need any special processing on open/close so we'll // just return success. Status = STATUS_SUCCESS; break; case IRP_MJ_DEVICE_CONTROL: if ( AbortState(pLDI) && AbortableIOctlc(pIrpStack->Parameters.DeviceIoControl.IoControlCode) ){ // установлен запрет операций ввода/вывода myTRAP(); Status = STATUS_REQUEST_ABORTED; } else // Dispatch on IOCTL switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_GPD_WAIT_ON_PORT: Status = GpdIoctlWaitPort( pLDI, pIrp, pIrpStack ); break; case IOCTL_GPD_WRITE_PORT_BUFFER: Status = GpdIoctlWritePortBuffer( pLDI, pIrp, pIrpStack ); break; case IOCTL_GPD_READ_PORT_BUFFER: Status = GpdIoctlReadPortBuffer( pLDI, pIrp, pIrpStack ); break; case IOCTL_GPD_WRITE_AND_WAIT_ON_PORT: Status = GpdIoctlWriteAndWaitPort( pLDI, pIrp, pIrpStack ); break; case IOCTL_GPD_READ_PORT_UCHAR: case IOCTL_GPD_READ_PORT_USHORT: case IOCTL_GPD_READ_PORT_ULONG: Status = GpdIoctlReadPort( pLDI, pIrp, pIrpStack, pIrpStack->Parameters.DeviceIoControl.IoControlCode ); break; case IOCTL_GPD_WRITE_PORT_UCHAR: case IOCTL_GPD_WRITE_PORT_USHORT: case IOCTL_GPD_WRITE_PORT_ULONG: DebugPrint (("IoControlCode 0x%x", pIrpStack->Parameters.DeviceIoControl.IoControlCode)); Status = GpdIoctlWritePort( pLDI, pIrp, pIrpStack, pIrpStack->Parameters.DeviceIoControl.IoControlCode ); break; case IOCTL_MI1201_GET_CONTROL_INFO: Status = MI1201IoctlGetControlInfo( pLDI, pIrp, pIrpStack ); break; case IOCTL_MI1201_SET_CONTROL_INFO: pIrp->IoStatus.Information = 0; Status = STATUS_SUCCESS; break; case IOCTL_MI1201_ABORT: Status = MI1201IoctlAbort( pLDI, pIrp, pIrpStack ); break; case IOCTL_GPD_MULTIFUNCTION_IO_OPERATION: Status = GpdIoctlProcessMultifunctionInputBuffer( pLDI, pIrp, pIrpStack ); break; case IOCTL_GPD_MULTIFUNCTION_IO_OPERATION_CHECK_INPUT_BUFFER: Status = GpdIoctlCheckMultifunctionInputBuffer( pLDI, pIrp, pIrpStack ); break; default: myTRAP(); Status = STATUS_NOT_IMPLEMENTED; // Status = STATUS_INVALID_PARAMETER; } break; default: DebugPrint(("GpdDispatch not implemented: (0x%x)\n",pIrpStack->MajorFunction)); Status = STATUS_NOT_IMPLEMENTED; //STATUS_INVALID_DEVICE_REQUEST; break; } // We're done with I/O request. Record the status of the I/O action. pIrp->IoStatus.Status = Status; // Don't boost priority when returning since this took little time. IoCompleteRequest(pIrp, IO_NO_INCREMENT ); IoReleaseRemoveLock(&pLDI->RemoveLock, pIrp); return Status; } //---------------------------------------------------------- BOOLEAN AbortState( IN PLOCAL_DEVICE_INFO pLDI ) { LARGE_INTEGER TimeOut; TimeOut.QuadPart = 0; return (KeWaitForSingleObject(&pLDI->AbortEvent,Executive,KernelMode,FALSE,&TimeOut) == STATUS_SUCCESS); } //---------------------------------------------------------- BOOLEAN AbortSet( IN PLOCAL_DEVICE_INFO pLDI, IN BOOLEAN Abort ) { BOOLEAN PrevState; PrevState = AbortState(pLDI); if (Abort){ KeSetEvent(&pLDI->AbortEvent, 0, FALSE); } else { KeClearEvent(&pLDI->AbortEvent); }; return PrevState; } //---------------------------------------------------------- NTSTATUS MI1201IoctlAbort( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack ) { // NOTE: Use METHOD_BUFFERED ioctls. ULONG InBufferSize; // Amount of data avail. from caller. ULONG OutBufferSize; // Max data that caller can accept. BOOLEAN PrevState; PAGED_CODE(); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Size of buffer for data to be sent to application OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; if ( InBufferSize != sizeof(MI1201_ABORT_INPUT_BUFFER)) { return STATUS_INVALID_PARAMETER; }; if ( (OutBufferSize != sizeof(MI1201_ABORT_OUTPUT_BUFFER)) && (OutBufferSize != 0)) { return STATUS_INVALID_PARAMETER; }; // Buffers are big enough. PrevState = AbortSet(pLDI, ((PMI1201_ABORT_INPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer)->Stop); if ( (OutBufferSize != 0) ) { ((PMI1201_ABORT_OUTPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer)->PreviousState = PrevState; pIrp->IoStatus.Information = OutBufferSize; } else { pIrp->IoStatus.Information = 0; }; return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS TranslatePort( IN ULONG nPort, IN ULONG nPortType, IN PLOCAL_DEVICE_INFO pLDI, IN OUT ptLastReadWriteInfo pInfo, OUT ptTranslatedPortInfo pTranslatedPort ) /*++ Описание процедуры: Процедура проверяет, принадлежит ли Port допустимому диапазону портов. Аргументы: nPort - порт для проверки, nPortSize - размер порта, pLDI - область данных устройства, pInfo - данные о последней операции pTranslatedPort - для возврата транслированного порта Return Value: STATUS_SUCCESS -- OK STATUS_ACCESS_VIOLATION -- порт не верен, pTranslatedPort и pPortMemoryType неопределены. --*/ { LONG RangeNumber; // Номер диапазона нетранслированных портов ULONG nPortShift; // Сдвиг порта в диапазоне ptInitialPortRange ActiveInitialPortRange; // Указатель диапазона нетранслированных портов ptTransPortRange ActiveTranslatedPortRange; // Указатель диапазона транслированных портов UCHAR PortSize; ExAcquireFastMutex(&pInfo->LockMutex); if( (pInfo->Initial.PortType == nPortType) && (pInfo->Initial.Port == nPort) ) { // Восстанавливаем данные последнего чтения *pTranslatedPort = pInfo->Translated; // последний порт с которым проводились операция чтения ExReleaseFastMutex(&pInfo->LockMutex); } else { ExReleaseFastMutex(&pInfo->LockMutex); // Проверка порта на принадлежность допустимым диапазонам RangeNumber = CompareInitialPortInfoAndPort(&pLDI->InitialPortRangesInfo.Info, nPort); if (RangeNumber < 0){ return STATUS_ACCESS_VIOLATION; // Порт не попадает в диапазон } // ИСХОДНЫЙ Рабочий диапазон портов ActiveInitialPortRange = &pLDI->InitialPortRangesInfo.Info.Range[RangeNumber]; // Вычисляем смещение порта относительно начала диапазона nPortShift = (nPort - ActiveInitialPortRange->PortFirst); PortSize = GetPortSize(nPortType); // Проверка порта не принадлежность допустимым диапазонам if( ( (nPort + PortSize-1) > ActiveInitialPortRange->PortLast ) || ( (nPortShift & (PortSize - 1)) != 0) // ??? эта проверка не очень понятна // предположительно: адрес порта для WORD должен быть четным, адрес порта для DWORD должен быть кратным 4? ) { return STATUS_ACCESS_VIOLATION; // Порт не попадает в диапазон } // Порт принадлежит допустимым диапазонам - можно выводить данные // ТРАНСЛИРОВАНЫЙ Рабочий диапазон портов ActiveTranslatedPortRange = &pLDI->TranslatedPortRangesInfo.Info.Range[ActiveInitialPortRange->TranslatedRangeNumber]; // Вычисляем порт в транслрованном диапазоне // pTranslatedPort->Port = (ULONG_PTR)ActiveTranslatedPortRange->PortFirst + nPortShift; pTranslatedPort->Number = (ULONG_PTR)ActiveTranslatedPortRange->PortFirst + nPortShift; // Запоминаем тип памяти порта // pTranslatedPort->PortMemoryType = ActiveTranslatedPortRange->PortMemoryType; pTranslatedPort->MemoryType = ActiveTranslatedPortRange->PortMemoryType; // Запоминаем тип порта pTranslatedPort->Type = nPortType; ExAcquireFastMutex(&pInfo->LockMutex); // Запоминаем данные последнего чтения pInfo->Initial.PortType = nPortType; // последняя операция чтения pInfo->Initial.Port = nPort; // последний порт с которым проводились операция чтения pInfo->Translated = *pTranslatedPort; // последний порт с которым проводились операция чтения ExReleaseFastMutex(&pInfo->LockMutex); }; return STATUS_SUCCESS; } NTSTATUS TranslatePortEx( IN PPORT_DATA pPort, IN PLOCAL_DEVICE_INFO pLDI, IN OUT ptLastReadWriteInfo pInfo, OUT PPORT_DATA pTranslatedPort ) /*++ Описание процедуры: Процедура проверяет, принадлежит ли Port допустимому диапазону портов. Аргументы: Port - порт для проверки, pLDI - область данных устройства, pInfo - данные о последней операции pTranslatedPort - для возврата транслированного порта Return Value: STATUS_SUCCESS -- OK STATUS_ACCESS_VIOLATION -- порт не верен, pTranslatedPort и pPortMemoryType неопределены. --*/ { LONG RangeNumber; // Номер диапазона нетранслированных портов ULONG nPortShift; // Сдвиг порта в диапазоне ptInitialPortRange ActiveInitialPortRange; // Указатель диапазона нетранслированных портов ptTransPortRange ActiveTranslatedPortRange; // Указатель диапазона транслированных портов UCHAR nPortSize; ULONG nPort; ExAcquireFastMutex(&pInfo->LockMutex); if( (pInfo->Initial.PortType == pPort->Type) && (pInfo->Initial.Port == pPort->Number) ) { // Восстанавливаем данные последнего чтения *pTranslatedPort = pInfo->Translated; // последний порт с которым проводились операция чтения ExReleaseFastMutex(&pInfo->LockMutex); } else { ExReleaseFastMutex(&pInfo->LockMutex); // Проверка порта на принадлежность допустимым диапазонам RangeNumber = CompareInitialPortInfoAndPort(&pLDI->InitialPortRangesInfo.Info, pPort->Number); if (RangeNumber < 0){ return STATUS_ACCESS_VIOLATION; // Порт не попадает в диапазон } // ИСХОДНЫЙ Рабочий диапазон портов ActiveInitialPortRange = &pLDI->InitialPortRangesInfo.Info.Range[RangeNumber]; // Вычисляем смещение порта относительно начала диапазона nPortShift = (pPort->Number - ActiveInitialPortRange->PortFirst); // Вычисляем размер порта относительно начала диапазона nPortSize = GetPortSize(pPort->Type); // Проверка порта не принадлежность допустимым диапазонам if( ( (pPort->Number + nPortSize-1) > ActiveInitialPortRange->PortLast ) || ( (nPortShift & (nPortSize - 1)) != 0) // ??? эта проверка не очень понятна // предположительно: адрес порта для WORD должен быть четным, адрес порта для DWORD должен быть кратным 4? ) { return STATUS_ACCESS_VIOLATION; // Порт не попадает в диапазон } // Порт принадлежит допустимым диапазонам - можно выводить данные // ТРАНСЛИРОВАНЫЙ Рабочий диапазон портов ActiveTranslatedPortRange = &pLDI->TranslatedPortRangesInfo.Info.Range[ActiveInitialPortRange->TranslatedRangeNumber]; nPort = pPort->Number; // Вычисляем порт в транслрованном диапазоне pTranslatedPort->Number = (ULONG_PTR)ActiveTranslatedPortRange->PortFirst + nPortShift; // Запоминаем тип порта pTranslatedPort->MemoryType = ActiveTranslatedPortRange->PortMemoryType; ExAcquireFastMutex(&pInfo->LockMutex); // Запоминаем данные последнего чтения pInfo->Initial.PortType = pPort->Type; // последняя операция чтения pInfo->Initial.Port = nPort; // последний порт с которым проводились операция чтения // последний порт с которым проводились операция чтения pInfo->Translated = *pTranslatedPort; ExReleaseFastMutex(&pInfo->LockMutex); }; return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS GpdIoctlReadPort( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack, IN ULONG IoctlCode ) /*++ Routine Description: This routine processes the IOCTLs which read from the ports. Arguments: pLDI - our local device data pIrp - IO request packet IrpStack - The current stack location IoctlCode - The ioctl code from the IRP Return Value: STATUS_SUCCESS -- OK STATUS_INVALID_PARAMETER -- The buffer sent to the driver was too small to contain the port, or the buffer which would be sent back to the driver was not a multiple of the data size. STATUS_ACCESS_VIOLATION -- An illegal port number was given. --*/ { // NOTE: Use METHOD_BUFFERED ioctls. PGENPORT_READ_INPUT_OUTPUT_BUFFER pIOBuffer; // Указатель на буфер для чтения (исходная информация) ULONG InBufferSize; // Amount of data avail. from caller. ULONG OutBufferSize; // Max data that caller can accept. UCHAR DataBufferSize; UCHAR PortType; tTranslatedPortInfo TranslatedPort; NTSTATUS status; pfReadPort pFuncReadPort; ULONG Data; PAGED_CODE(); // myTRAP(); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Size of buffer for data to be sent to application OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; // Check to ensure input buffer is big enough to hold a port number and // the output buffer is at least as big as the port data width. // switch (IoctlCode) { case IOCTL_GPD_READ_PORT_UCHAR: PortType = myPORT_UCHAR; break; case IOCTL_GPD_READ_PORT_USHORT: PortType = myPORT_USHORT; break; case IOCTL_GPD_READ_PORT_ULONG: PortType = myPORT_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; }; DataBufferSize = GetPortSize(PortType); if ( InBufferSize != sizeof(ULONG) || OutBufferSize < DataBufferSize ) { myTRAP(); return STATUS_INVALID_PARAMETER; }; // Buffers are big enough. // NT copies inbuf here before entry and copies this to outbuf after // return, for METHOD_BUFFERED IOCTL's. pIOBuffer = (PGENPORT_READ_INPUT_OUTPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer; status = TranslatePort( pIOBuffer->Input.PortNumber, // Get the I/O port number from the buffer PortType, pLDI, &pLDI->LastRead, &TranslatedPort ); if(!(status == STATUS_SUCCESS) ){ myTRAP(); return status; } // !!! БУФЕР ДАЛЕЕ НЕ ИСПОЛЬЗУЕТСЯ КАК ВХОДНЫЕ ДАННЫЕ // Определяем подходящую функцию для чтения порта status=GetAppropriateReadPortFunction(&TranslatedPort, &pFuncReadPort); if((STATUS_SUCCESS != status) ){ myTRAP(); return status; }; Data = pFuncReadPort( (PULONG)TranslatedPort.Number); switch (IoctlCode) { case IOCTL_GPD_READ_PORT_UCHAR: pIOBuffer->Output.Data.UChar = (UCHAR)Data; break; case IOCTL_GPD_READ_PORT_USHORT: pIOBuffer->Output.Data.UShort = (USHORT)Data; break; case IOCTL_GPD_READ_PORT_ULONG: pIOBuffer->Output.Data.ULong = Data; break; }; // Indicate # of bytes read pIrp->IoStatus.Information = DataBufferSize; return STATUS_SUCCESS; } //---------------------------------------------------------- UCHAR GetPortSize(ULONG PortType) { switch (PortType) { case myPORT_UCHAR: return sizeof(UCHAR); break; case myPORT_USHORT: return sizeof(USHORT); break; case myPORT_ULONG: return sizeof(ULONG); break; default: return 0; }; } //---------------------------------------------------------- NTSTATUS GpdIoctlReadPortBuffer( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack ) /*++ Описание процедуры: Эта процедура обрабатывает IOCTLs, который читает из порта несколько значений. Аргументы: pLDI - our local device data pIrp - IO request packet IrpStack - The current stack location IoctlCode - The ioctl code from the IRP Return Value: STATUS_SUCCESS -- OK STATUS_INVALID_PARAMETER -- The buffer sent to the driver was too small to contain the port, or the buffer which would be sent back to the driver was not a multiple of the data size. STATUS_ACCESS_VIOLATION -- An illegal port number was given. --*/ { PGENPORT_READ_MULTI_INPUT_OUTPUT_BUFFER pIOBuffer; // Указатель на буфер для чтения (исходная информация) ULONG InBufferSize; // Amount of data avail. from caller. ULONG OutBufferSize; // Max data that caller can accept. ULONG DataBufferSize; tTranslatedPortInfo TranslatedPort; NTSTATUS status; ULONG PortType; // Число считываний ULONG Count; // Число считываний pfReadPortBuffer pFuncReadPortBuffer; PAGED_CODE(); myTRAP(); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Size of buffer for data to be sent to application OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; //Проверяем размер данных во входном буфере if ( InBufferSize != sizeof(GENPORT_READ_MULTI_INPUT_BUFFER) ) { myTRAP(); return STATUS_INVALID_PARAMETER; }; pIOBuffer = (PGENPORT_READ_MULTI_INPUT_OUTPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer; Count = pIOBuffer->Input.Count; PortType = pIOBuffer->Input.PortType; //Проверяем, что запрошено чтение хотя бы одного порта . if ( (0 == Count) ){ myTRAP(); return STATUS_INVALID_PARAMETER; } //Вычисляем размер возвращаемых данных. DataBufferSize = GetPortSize(PortType)*Count+sizeof(pIOBuffer->Output.Header); //Проверяем, что размер выходного буфера достаточен. if ( (DataBufferSize > OutBufferSize) ){ myTRAP(); return STATUS_INVALID_PARAMETER; } // Проверяем порт на допустимость status = TranslatePort( pIOBuffer->Input.Port, // Get the I/O port number from the buffer PortType, pLDI, &pLDI->LastRead, &TranslatedPort ); if(!(status == STATUS_SUCCESS) ){ myTRAP(); return status; // негодный порт } /// !!! ВХОДНОЙ БУФЕР ДАЛЕЕ НЕ ИСПОЛЬЗУЕТСЯ КАК ВХОДНЫЕ ДАННЫЕ DebugPrint(("Read buf[%x] type (%x)->port(0x%x)/trans:(0x%x)/\n", Count,PortType,pIOBuffer->Input.Port,TranslatedPort.Number)); // Определяем подходящую функцию для чтения порта status=GetAppropriateReadPortBufferFunction(&TranslatedPort, &pFuncReadPortBuffer); if((STATUS_SUCCESS != status) ){ myTRAP(); return status; }; pFuncReadPortBuffer( (PULONG)TranslatedPort.Number, &pIOBuffer->Output.Data.ULong[0], Count ); // Indicate # of bytes read pIOBuffer->Output.Header.Count = Count; pIrp->IoStatus.Information = DataBufferSize; return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS GpdIoctlWritePort( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack, IN ULONG IoctlCode ) /*++ Routine Description: This routine processes the IOCTLs which write to the ports. Arguments: pLDI - our local device data pIrp - IO request packet IrpStack - The current stack location IoctlCode - The ioctl code from the IRP Return Value: STATUS_SUCCESS -- OK STATUS_INVALID_PARAMETER -- The buffer sent to the driver was too small to contain the port, or the buffer which would be sent back to the driver was not a multiple of the data size. STATUS_ACCESS_VIOLATION -- An illegal port number was given. --*/ { // NOTE: Use METHOD_BUFFERED ioctls. PGENPORT_WRITE_INPUT_BUFFER pIOBuffer; // Указатель на буфер для чтения/записи ULONG InBufferSize ; // Amount of data avail. from caller. UCHAR DataBufferSize; tTranslatedPortInfo TranslatedPort; NTSTATUS status; UCHAR PortType; pfWritePort pFuncWritePort; PAGED_CODE(); // DebugPrint (("IoControlCode 0x%x (0x%x)", IoctlCode, IOCTL_GPD_READ_PORT_UCHAR)); // myTRAP(); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Check to ensure input buffer is big enough to hold a port number as well // as the data to write. // // The relative port # is a ULONG, and the data is the type appropriate to // the IOCTL. // switch (IoctlCode) { case IOCTL_GPD_WRITE_PORT_UCHAR: PortType = myPORT_UCHAR; break; case IOCTL_GPD_WRITE_PORT_USHORT: PortType = myPORT_USHORT; break; case IOCTL_GPD_WRITE_PORT_ULONG: PortType = myPORT_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; }; DataBufferSize = GetPortSize(PortType); if ( InBufferSize < (sizeof(ULONG) + DataBufferSize) ) { myTRAP(); return STATUS_INVALID_PARAMETER; } // NT copies inbuf here before entry and copies this to outbuf after return, // for METHOD_BUFFERED IOCTL's. pIOBuffer = (PGENPORT_WRITE_INPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer; status = TranslatePort( pIOBuffer->Port, // Get the I/O port number from the buffer PortType, pLDI, &pLDI->LastWrite, &TranslatedPort ); if((STATUS_SUCCESS != status) ){ myTRAP(); return status; }; // Определяем подходящую функцию для чтения порта status=GetAppropriateWritePortFunction(&TranslatedPort, &pFuncWritePort); if((STATUS_SUCCESS != status) ){ myTRAP(); return status; }; pFuncWritePort( (PULONG)TranslatedPort.Number, pIOBuffer->Data.ULong); pIrp->IoStatus.Information = 0; return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS GpdIoctlWritePortBuffer( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack ) /*++ Описание процедуры: Эта процедура обрабатывает IOCTLs, который читает из порта несколько значений. Аргументы: pLDI - our local device data pIrp - IO request packet IrpStack - The current stack location IoctlCode - The ioctl code from the IRP Return Value: STATUS_SUCCESS -- OK STATUS_INVALID_PARAMETER -- The buffer sent to the driver was too small to contain the port, or the buffer which would be sent back to the driver was not a multiple of the data size. STATUS_ACCESS_VIOLATION -- An illegal port number was given. --*/ { PGENPORT_WRITE_MULTI_INPUT_OUTPUT_BUFFER pIOBuffer; // Указатель на буфер для чтения (исходная информация) ULONG InBufferSize; // Amount of data avail. from caller. ULONG OutBufferSize; // Max data that caller can accept. tTranslatedPortInfo TranslatedPort; NTSTATUS status; ULONG PortType; // Тип порта ULONG Count; // Число считываний ULONG DataSize; // Размер данных pfWritePortBuffer pFuncWritePortBuffer; PAGED_CODE(); // TRAP(); // myTRAP(); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Size of buffer for data to be sent to application OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; //Проверяем размер данных во входном буфере if ( InBufferSize < sizeof(GENPORT_WRITE_MULTI_INPUT_BUFFER_HEADER) ) { myTRAP(); return STATUS_INVALID_PARAMETER; }; pIOBuffer = (PGENPORT_WRITE_MULTI_INPUT_OUTPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer; Count = pIOBuffer->Input.Header.Count; PortType = pIOBuffer->Input.Header.PortType; //Проверяем, что запрошена запись хотя бы одного порта. if ( (0 == Count) ){ myTRAP(); return STATUS_INVALID_PARAMETER; } // Размер данных (полный) DataSize = Count*GetPortSize(PortType)+sizeof(GENPORT_WRITE_MULTI_INPUT_BUFFER_HEADER); //Проверяем, что размер буфера соответствует запрошенному числу записей в порт if ( (InBufferSize != DataSize) ){ myTRAP(); return STATUS_INVALID_PARAMETER; } // Проверяем порт на допустимость status = TranslatePort( pIOBuffer->Input.Header.Port, // Get the I/O port number from the buffer PortType, pLDI, &pLDI->LastRead, &TranslatedPort ); if(!(status == STATUS_SUCCESS) ){ myTRAP(); return status; // негодный порт } DebugPrint(("Write buf[%x] type (%x)->port(0x%x)/trans:(0x%x)/\n", Count,PortType,pIOBuffer->Input.Header.Port,TranslatedPort.Number)); status = GetAppropriateWritePortBufferFunction(&TranslatedPort, &pFuncWritePortBuffer); if((STATUS_SUCCESS != status) ){ myTRAP(); return status; }; pFuncWritePortBuffer( (PULONG)TranslatedPort.Number, &pIOBuffer->Input.Data.ULong[0], pIOBuffer->Input.Header.Count ); pIrp->IoStatus.Information = 0; return STATUS_SUCCESS; } // READ_PORT---------------------------------------- ULONG myREAD_PORT_UCHAR(IN PULONG Port) { return READ_PORT_UCHAR((PUCHAR) Port); } ULONG myREAD_PORT_USHORT(IN PULONG Port) { return READ_PORT_USHORT((PUSHORT) Port); } ULONG myREAD_REGISTER_UCHAR(IN PULONG Port) { return READ_REGISTER_UCHAR((PUCHAR) Port); } ULONG myREAD_REGISTER_USHORT(IN PULONG Port) { return READ_REGISTER_USHORT((PUSHORT) Port); } // WRITE_PORT---------------------------------------- VOID myWRITE_PORT_UCHAR(IN PULONG Port, IN ULONG Data) { WRITE_PORT_UCHAR((PUCHAR) Port, (UCHAR)Data); } VOID myWRITE_PORT_USHORT(IN PULONG Port, IN ULONG Data) { WRITE_PORT_USHORT((PUSHORT) Port, (USHORT)Data); } VOID myWRITE_REGISTER_UCHAR(IN PULONG Port, IN ULONG Data) { WRITE_REGISTER_UCHAR((PUCHAR) Port, (UCHAR)Data); } VOID myWRITE_REGISTER_USHORT(IN PULONG Port, IN ULONG Data) { WRITE_REGISTER_USHORT((PUSHORT) Port, (USHORT)Data); } // READ_PORT_BUFFER---------------------------------------- VOID myREAD_PORT_BUFFER_UCHAR(IN PULONG Port, IN PULONG Buffer, IN ULONG Count) { READ_PORT_BUFFER_UCHAR((PUCHAR) Port, (PUCHAR)Buffer, Count); } VOID myREAD_PORT_BUFFER_USHORT(IN PULONG Port, IN PULONG Buffer, IN ULONG Count) { READ_PORT_BUFFER_USHORT((PUSHORT) Port, (PUSHORT)Buffer, Count); } VOID myREAD_REGISTER_BUFFER_UCHAR(IN PULONG Port, IN PULONG Buffer, IN ULONG Count) { READ_REGISTER_BUFFER_UCHAR((PUCHAR) Port, (PUCHAR)Buffer, Count); } VOID myREAD_REGISTER_BUFFER_USHORT(IN PULONG Port, IN PULONG Buffer, IN ULONG Count) { READ_REGISTER_BUFFER_USHORT((PUSHORT) Port, (PUSHORT)Buffer, Count); } // WRITE_PORT_BUFFER---------------------------------------- VOID myWRITE_PORT_BUFFER_UCHAR(IN PULONG Port, IN PULONG Buffer, IN ULONG Count) { WRITE_PORT_BUFFER_UCHAR((PUCHAR) Port, (PUCHAR)Buffer, Count); } VOID myWRITE_PORT_BUFFER_USHORT(IN PULONG Port, IN PULONG Buffer, IN ULONG Count) { WRITE_PORT_BUFFER_USHORT((PUSHORT) Port, (PUSHORT)Buffer, Count); } VOID myWRITE_REGISTER_BUFFER_UCHAR(IN PULONG Port, IN PULONG Buffer, IN ULONG Count) { WRITE_REGISTER_BUFFER_UCHAR((PUCHAR) Port, (PUCHAR)Buffer, Count); } VOID myWRITE_REGISTER_BUFFER_USHORT(IN PULONG Port, IN PULONG Buffer, IN ULONG Count) { WRITE_REGISTER_BUFFER_USHORT((PUSHORT) Port, (PUSHORT)Buffer, Count); } // ---------------------------------------- NTSTATUS WaitOnPort( IN pfReadPort pFuncReadPort, IN PGENPORT_WAIT_INPUT_BUFFER pWaitData, IN OUT PGENPORT_WAIT_OUTPUT_BUFFER pResultData, IN PKEVENT pAbortEvent ) /*++ Описание процедуры: Эта процедура обрабатывает ОЖИДАНИЕ события на порту. (Порт(pWaitData->Port) and pWaitData->Mask) == pWaitData->Patt в течение pWaitData->TimeOut миллисекунд, опрашивая порт через pWaitData->TimeResolution (но не чаще чем раз в 1 мс). Если pWaitData->TimeResolution == 0, то порт повторно опрашивается через KeQueryTimeIncrement()*100 наносекунд. !!! Если pWaitData->TimeResolution > pWaitData->TimeOut, то порт повторно опрашивается через pWaitData->TimeResolution мс. Аргументы: pFuncReadPort - функция чтения данных с порта pWaitData - данные для ожидания pResultData - результат ожидания pAbortEvent - сигнал немедленного завершения ожидания, например драйвер требуется остановить или удалить по запросу системы Return Value: STATUS_SUCCESS -- OK !!! ВНИМАНИЕ функция НЕ ПРОВЕРЯЕТ допустимость значений в pWriteAndWaitData --*/ { BOOLEAN Event; BOOLEAN TimeOut; LARGE_INTEGER StartTime; LARGE_INTEGER Time; LARGE_INTEGER EndTime; LARGE_INTEGER TimeInterval; ULONG TimeIncrement; ULONG Value; ULONG WaitResult; NTSTATUS KeWaitResult; KWAIT_REASON WaitReason; KPROCESSOR_MODE WaitMode; BOOLEAN Alertable; // Опрашиваем порт на предмет события Value = pFuncReadPort( (PVOID)pWaitData->Port ); Event = (Value & pWaitData->Mask) == pWaitData->Patt; // Проверяем: имеет ли место событие? if (!Event){ // Событие отсутствует - начинаем ожидание // Запрашиваем текущее время KeQueryTickCount(&StartTime); // Запрашиваем интервал приращения счетчика TimeIncrement = KeQueryTimeIncrement(); // Запрашиваем интервал приращения счетчика if ( (0 == pWaitData->TimeResolution) && (0 == (pWaitData->ControlFlags & bit_wcfAllowZeroResolution)) ){ TimeInterval.QuadPart = -1*TimeIncrement; } else { TimeInterval.QuadPart = pWaitData->TimeResolution*(-10000); }; // Вычисляем время окончания ожидания в тиках (Ticks) EndTime.QuadPart = StartTime.QuadPart + (pWaitData->TimeOut*10000+(TimeIncrement-1))/TimeIncrement; // Разбираем управляющие флаги if ( 0 != (pWaitData->ControlFlags & bit_wcfUserRequest) ){ WaitReason = UserRequest;} else {WaitReason = Executive;}; if ( 0 != (pWaitData->ControlFlags & bit_wcfUserMode) ){ WaitMode = UserMode;} else {WaitMode = KernelMode;}; Alertable = (0 != TimeInterval.QuadPart) && (0 != (pWaitData->ControlFlags & bit_wcfAlertable)); // Цикл ожидания TimeOut = FALSE; while ( (!Event) && (!TimeOut) ){ // Запрашиваем текущее время KeQueryTickCount(&Time); // Опрашиваем порт на предмет события Value = pFuncReadPort( (PVOID)pWaitData->Port ); Event = (Value & pWaitData->Mask) == pWaitData->Patt; // Проверяем: имеет ли место событие? if (!Event){ // нет события // Проверяем: имеет ли место таймаут? TimeOut = (Time.QuadPart > EndTime.QuadPart); if(TimeOut){ // таймаут WaitResult = pwrTIMEOUT; } else { // нет таймаута // Прекращаем выполнение на TimeInterval, с возможностью прерывания по событию pAbortEvent KeWaitResult = KeWaitForSingleObject( pAbortEvent, WaitReason, //Executive WaitMode, //KernelMode Alertable, //FALSE &TimeInterval ); // Проверяем: имеет ли место pAbortEvent? TimeOut = (KeWaitResult != STATUS_TIMEOUT); if (TimeOut) { WaitResult = pwrABANDONED; }; } } }; // Вычисляем время ожидания в мс Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*TimeIncrement)/10000; } else { // Устанавливаеm значения результатов по-умолчанию Time.QuadPart = 0; WaitResult = pwrSUCCESS; }; // Заполняем данные результата ожидания pResultData->Event = Event; pResultData->WaitResult = WaitResult; pResultData->Data = Value; if (Time.HighPart > 0){ pResultData->TimeOut = 0xFFFFFFFF; } else { pResultData->TimeOut = Time.LowPart; }; return STATUS_SUCCESS; }; //---------------------------------------------------------- NTSTATUS GpdIoctlWaitPort( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack ) /*++ Описание процедуры: Эта процедура обрабатывает IOCTLs, который ожидает на порту события. Аргументы: pLDI - our local device data pIrp - IO request packet IrpStack - The current stack location IoctlCode - The ioctl code from the IRP Return Value: STATUS_SUCCESS -- OK STATUS_INVALID_PARAMETER -- The buffer sent to the driver was too small to contain the port, or the buffer which would be sent back to the driver was not a multiple of the data size. STATUS_ACCESS_VIOLATION -- An illegal port number was given. --*/ { PGENPORT_WAIT_INPUT_OUTPUT_BUFFER pIOBuffer; // Указатель на буфер (исходная информация) ULONG InBufferSize; // Amount of data avail. from caller. ULONG OutBufferSize; // Max data that caller can accept. ULONG DataBufferSize; tTranslatedPortInfo TranslatedPort; NTSTATUS status; ULONG PortType; GENPORT_WAIT_OUTPUT_BUFFER OutputData; PAGED_CODE(); // myTRAP(); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Size of buffer for data to be sent to application OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; //Проверяем размер данных во входном буфере if ( InBufferSize != sizeof(GENPORT_WAIT_INPUT_BUFFER) ) { return STATUS_INVALID_PARAMETER; }; pIOBuffer = (PGENPORT_WAIT_INPUT_OUTPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer; //Вычисляем размер возвращаемых данных. DataBufferSize = sizeof(GENPORT_WAIT_OUTPUT_BUFFER); //Проверяем, что размер выходного буфера достаточен. if ( (DataBufferSize > OutBufferSize) ){ myTRAP(); return STATUS_INVALID_PARAMETER; } PortType = pIOBuffer->Input.PortType; // Проверяем порт на допустимость status = TranslatePort( pIOBuffer->Input.Port, // Get the I/O port number from the buffer PortType, pLDI, &pLDI->LastRead, &TranslatedPort ); if(!(status == STATUS_SUCCESS) ){ myTRAP(); return status; // негодный порт } pIOBuffer->Input.Port = TranslatedPort.Number; if (pIOBuffer->Input.TimeResolution >= pIOBuffer->Input.TimeOut){ pIOBuffer->Input.TimeResolution = pIOBuffer->Input.TimeOut / 10; } switch(TranslatedPort.MemoryType) { case CmResourceTypePort: // Address is in I/O space switch (PortType) { case myPORT_UCHAR: WaitOnPort(&myREAD_PORT_UCHAR, &pIOBuffer->Input, &OutputData, &pLDI->AbortEvent ); break; case myPORT_USHORT: WaitOnPort(&myREAD_PORT_USHORT, &pIOBuffer->Input, &OutputData, &pLDI->AbortEvent ); break; case myPORT_ULONG: WaitOnPort(&READ_PORT_ULONG, &pIOBuffer->Input, &OutputData, &pLDI->AbortEvent ); break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; case CmResourceTypeMemory: // Address is in Memory space switch (PortType) { case myPORT_UCHAR: WaitOnPort(&myREAD_REGISTER_UCHAR, &pIOBuffer->Input, &OutputData, &pLDI->AbortEvent ); break; case myPORT_USHORT: WaitOnPort(&myREAD_REGISTER_USHORT, &pIOBuffer->Input, &OutputData, &pLDI->AbortEvent ); break; case myPORT_ULONG: WaitOnPort(&READ_REGISTER_ULONG, &pIOBuffer->Input, &OutputData, &pLDI->AbortEvent ); break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; default: myTRAP(); return STATUS_UNSUCCESSFUL; } pIOBuffer->Output = OutputData; pIrp->IoStatus.Information = DataBufferSize; return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS GetAppropriateReadPortFunction( IN PPORT_DATA pPort, OUT pfReadPort *pFuncReadPort ) { switch(pPort->MemoryType) { case CmResourceTypePort: // Address is in I/O space switch (pPort->Type) { case myPORT_UCHAR: *pFuncReadPort=&myREAD_PORT_UCHAR; break; case myPORT_USHORT: *pFuncReadPort=&myREAD_PORT_USHORT; break; case myPORT_ULONG: *pFuncReadPort=&READ_PORT_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; case CmResourceTypeMemory: // Address is in Memory space switch (pPort->Type) { case myPORT_UCHAR: *pFuncReadPort=&myREAD_REGISTER_UCHAR; break; case myPORT_USHORT: *pFuncReadPort=&myREAD_REGISTER_USHORT; break; case myPORT_ULONG: *pFuncReadPort=&READ_REGISTER_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } NTSTATUS GetAppropriateWritePortFunction( IN PPORT_DATA pPort, OUT pfWritePort *pFuncWritePort ) { switch(pPort->MemoryType) { case CmResourceTypePort: // Address is in I/O space switch (pPort->Type) { case myPORT_UCHAR: *pFuncWritePort=&myWRITE_PORT_UCHAR; break; case myPORT_USHORT: *pFuncWritePort=&myWRITE_PORT_USHORT; break; case myPORT_ULONG: *pFuncWritePort=&WRITE_PORT_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; case CmResourceTypeMemory: // Address is in Memory space switch (pPort->Type) { case myPORT_UCHAR: *pFuncWritePort=&myWRITE_REGISTER_UCHAR; break; case myPORT_USHORT: *pFuncWritePort=&myWRITE_REGISTER_USHORT; break; case myPORT_ULONG: *pFuncWritePort=&WRITE_REGISTER_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } NTSTATUS GetAppropriateWritePortBufferFunction( IN PPORT_DATA pPort, OUT pfWritePortBuffer *pFuncWritePort ) { switch(pPort->MemoryType) { case CmResourceTypePort: // Address is in I/O space switch (pPort->Type) { case myPORT_UCHAR: *pFuncWritePort=&myWRITE_PORT_BUFFER_UCHAR; break; case myPORT_USHORT: *pFuncWritePort=&myWRITE_PORT_BUFFER_USHORT; break; case myPORT_ULONG: *pFuncWritePort=&WRITE_PORT_BUFFER_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; case CmResourceTypeMemory: // Address is in Memory space switch (pPort->Type) { case myPORT_UCHAR: *pFuncWritePort=&myWRITE_REGISTER_BUFFER_UCHAR; break; case myPORT_USHORT: *pFuncWritePort=&myWRITE_REGISTER_BUFFER_USHORT; break; case myPORT_ULONG: *pFuncWritePort=&WRITE_REGISTER_BUFFER_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS GetAppropriateReadPortBufferFunction( IN PPORT_DATA pPort, OUT pfReadPortBuffer *pFuncReadPort ) { switch(pPort->MemoryType) { case CmResourceTypePort: // Address is in I/O space switch (pPort->Type) { case myPORT_UCHAR: *pFuncReadPort=&myREAD_PORT_BUFFER_UCHAR; break; case myPORT_USHORT: *pFuncReadPort=&myREAD_PORT_BUFFER_USHORT; break; case myPORT_ULONG: *pFuncReadPort=&READ_PORT_BUFFER_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; case CmResourceTypeMemory: // Address is in Memory space switch (pPort->Type) { case myPORT_UCHAR: *pFuncReadPort=&myREAD_REGISTER_BUFFER_UCHAR; break; case myPORT_USHORT: *pFuncReadPort=&myREAD_REGISTER_BUFFER_USHORT; break; case myPORT_ULONG: *pFuncReadPort=&READ_REGISTER_BUFFER_ULONG; break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } break; default: myTRAP(); return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS WaitOnPortEx( IN PGENPORT_GENPORT_WRITE_AND_WAIT_WAIT_DATA pWaitData, IN OUT PGENPORT_WAIT_OUTPUT_BUFFER pResultData, IN PKEVENT pAbortEvent ) /*++ Описание процедуры: Эта процедура обрабатывает ОЖИДАНИЕ события на порту. (Порт(pWaitData->Port) and pWaitData->Mask) == pWaitData->Patt в течение pWaitData->TimeOut миллисекунд, опрашивая порт через pWaitData->TimeResolution (но не чаще чем раз в 1 мс). Если pWaitData->TimeResolution == 0, то порт повторно опрашивается через KeQueryTimeIncrement()*100 наносекунд. При уст. флаге wcfAllowZeroResolution и pWaitData->TimeResolution=0 порт опрашивается непрерывно. !!! Если pWaitData->TimeResolution > pWaitData->TimeOut, то порт повторно опрашивается через pWaitData->TimeResolution мс. Аргументы: pWaitData - данные для ожидания pResultData - результат ожидания pAbortEvent - сигнал немедленного завершения ожидания, например драйвер требуется остановить или удалить по запросу системы Return Value: STATUS_SUCCESS -- OK иначе -- ошибка в параметрах !!! ВНИМАНИЕ функция НЕ ПРОВЕРЯЕТ допустимость значений в pWriteAndWaitData --*/ { pfReadPort pFuncReadPort; NTSTATUS status; BOOLEAN Event; BOOLEAN TimeOut; LARGE_INTEGER StartTime; LARGE_INTEGER Time; LARGE_INTEGER EndTime; LARGE_INTEGER TimeInterval; ULONG TimeIncrement; ULONG Value; ULONG WaitResult; NTSTATUS KeWaitResult; KWAIT_REASON WaitReason; KPROCESSOR_MODE WaitMode; BOOLEAN Alertable; BOOLEAN UseQueryPerformanceCounter; LARGE_INTEGER PerformanceFrequency; // Определяем подходящую функцию для чтения порта status=GetAppropriateReadPortFunction(&pWaitData->Port, &pFuncReadPort); if (STATUS_SUCCESS != status){ return status; }; // Опрашиваем порт на предмет события Value = pFuncReadPort( (PVOID)pWaitData->Port.Number ); Event = (Value & pWaitData->Mask) == pWaitData->Patt; // Проверяем: имеет ли место событие? if (!Event){ // Событие отсутствует - начинаем ожидание // Использовать наиболее точное разрешение по времени? UseQueryPerformanceCounter = ( 0 != (pWaitData->ControlFlags & bit_wcfUseQueryPerformanceCounter) ); // Запрашиваем текущее время if (UseQueryPerformanceCounter) { StartTime= KeQueryPerformanceCounter(&PerformanceFrequency); // Вычисляем время окончания ожидания в интервалах PerformanceCounter EndTime.QuadPart = StartTime.QuadPart + (pWaitData->TimeOut*PerformanceFrequency.QuadPart)/1000; } else { KeQueryTickCount(&StartTime); // Запрашиваем интервал приращения счетчика TimeIncrement = KeQueryTimeIncrement(); // Вычисляем время окончания ожидания в тиках (Ticks) EndTime.QuadPart = StartTime.QuadPart + (pWaitData->TimeOut*10000+(TimeIncrement-1))/TimeIncrement; }; // Запрашиваем интервал приращения счетчика if ( !UseQueryPerformanceCounter && (0 == pWaitData->TimeResolution) && (0 == (pWaitData->ControlFlags & bit_wcfAllowZeroResolution)) ){ TimeInterval.QuadPart = -1*TimeIncrement; } else { TimeInterval.QuadPart = pWaitData->TimeResolution*(-10000); }; // Разбираем управляющие флаги if ( 0 != (pWaitData->ControlFlags & bit_wcfUserRequest) ){ WaitReason = UserRequest;} else {WaitReason = Executive;}; if ( 0 != (pWaitData->ControlFlags & bit_wcfUserMode) ){ WaitMode = UserMode;} else {WaitMode = KernelMode;}; Alertable = (0 !=TimeInterval.QuadPart) && (0 != (pWaitData->ControlFlags & bit_wcfAlertable)); // Цикл ожидания TimeOut = FALSE; while ( (!Event) && (!TimeOut) ){ // Запрашиваем текущее время if (UseQueryPerformanceCounter) { Time = KeQueryPerformanceCounter(NULL); } else { KeQueryTickCount(&Time); }; // Опрашиваем порт на предмет события Value = pFuncReadPort( (PVOID)pWaitData->Port.Number ); Event = (Value & pWaitData->Mask) == pWaitData->Patt; // Проверяем: имеет ли место событие? if (!Event){ // нет события // Проверяем: имеет ли место таймаут? TimeOut = (Time.QuadPart > EndTime.QuadPart); if(TimeOut){ // таймаут WaitResult = pwrTIMEOUT; } else { // нет таймаута // Прекращаем выполнение на TimeInterval, с возможностью прерывания по событию pAbortEvent KeWaitResult = KeWaitForSingleObject( pAbortEvent, WaitReason, //Executive WaitMode, //KernelMode Alertable, //FALSE &TimeInterval ); // Проверяем: имеет ли место pAbortEvent? TimeOut = (KeWaitResult != STATUS_TIMEOUT); if (TimeOut) { WaitResult = pwrABANDONED; }; } } }; // Вычисляем время ожидания if ( 0 != (pWaitData->ControlFlags & bit_wcfReturnWaitIntervalIn100nanosecUnit) ){ // Вычисляем время ожидания в 100 наносекунд if (UseQueryPerformanceCounter) { Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*10000000)/PerformanceFrequency.QuadPart; } else { Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*TimeIncrement); }; } else { // Вычисляем время ожидания в мс if (UseQueryPerformanceCounter) { Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*1000)/PerformanceFrequency.QuadPart; } else { Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*TimeIncrement)/10000; }; } } else { // Устанавливаеm значения результатов по-умолчанию Time.QuadPart = 0; WaitResult = pwrSUCCESS; }; // Заполняем данные результата ожидания pResultData->Event = Event; pResultData->WaitResult = WaitResult; pResultData->Data = Value; if (Time.HighPart > 0){ pResultData->TimeOut = 0xFFFFFFFF; } else { pResultData->TimeOut = Time.LowPart; }; return STATUS_SUCCESS; }; // ---------------------------------------- NTSTATUS WaitOnPortAtDISPATCH_LEVEL( IN PGENPORT_GENPORT_WRITE_AND_WAIT_WAIT_DATA pWaitData, IN OUT PGENPORT_WAIT_OUTPUT_BUFFER pResultData, IN PKEVENT pAbortEvent ) /*++ Описание процедуры: Эта процедура обрабатывает ОЖИДАНИЕ события на порту на IRQL = DISPATCH_LEVEL. (Порт(pWaitData->Port) and pWaitData->Mask) == pWaitData->Patt в течение pWaitData->TimeOut миллисекунд, опрашивая порт НЕПРЕРЫВНО. !!! Если pWaitData->TimeResolution > pWaitData->TimeOut, то порт повторно опрашивается через pWaitData->TimeResolution мс. Аргументы: pWaitData - данные для ожидания pResultData - результат ожидания pAbortEvent - сигнал немедленного завершения ожидания, например драйвер требуется остановить или удалить по запросу системы Return Value: STATUS_SUCCESS -- OK иначе -- ошибка в параметрах !!! ВНИМАНИЕ функция НЕ ПРОВЕРЯЕТ допустимость значений в pWriteAndWaitData --*/ { pfReadPort pFuncReadPort; NTSTATUS status; BOOLEAN Event; BOOLEAN TimeOut; LARGE_INTEGER StartTime; LARGE_INTEGER Time; LARGE_INTEGER EndTime; LARGE_INTEGER TimeInterval; ULONG TimeIncrement; ULONG Value; ULONG WaitResult; NTSTATUS KeWaitResult; KWAIT_REASON WaitReason; KPROCESSOR_MODE WaitMode; BOOLEAN Alertable; BOOLEAN UseQueryPerformanceCounter; LARGE_INTEGER PerformanceFrequency; // Определяем подходящую функцию для чтения порта status=GetAppropriateReadPortFunction(&pWaitData->Port, &pFuncReadPort); if (STATUS_SUCCESS != status){ return status; }; // Опрашиваем порт на предмет события Value = pFuncReadPort( (PVOID)pWaitData->Port.Number ); Event = (Value & pWaitData->Mask) == pWaitData->Patt; // Проверяем: имеет ли место событие? if (!Event){ // Событие отсутствует - начинаем ожидание // интервал приращения счетчика = 0 для DISPATCH_LEVEL TimeInterval.QuadPart=0; // Использовать наиболее точное разрешение по времени? UseQueryPerformanceCounter = ( 0 != (pWaitData->ControlFlags & bit_wcfUseQueryPerformanceCounter) ); // Запрашиваем текущее время if (UseQueryPerformanceCounter) { StartTime= KeQueryPerformanceCounter(&PerformanceFrequency); // Вычисляем время окончания ожидания в интервалах PerformanceCounter EndTime.QuadPart = StartTime.QuadPart + (pWaitData->TimeOut*PerformanceFrequency.QuadPart)/1000; } else { KeQueryTickCount(&StartTime); // Запрашиваем интервал приращения счетчика TimeIncrement = KeQueryTimeIncrement(); // Вычисляем время окончания ожидания в тиках (Ticks) EndTime.QuadPart = StartTime.QuadPart + (pWaitData->TimeOut*10000+(TimeIncrement-1))/TimeIncrement; }; // Разбираем управляющие флаги if ( 0 != (pWaitData->ControlFlags & bit_wcfUserRequest) ){ WaitReason = UserRequest;} else {WaitReason = Executive;}; if ( 0 != (pWaitData->ControlFlags & bit_wcfUserMode) ){ WaitMode = UserMode;} else {WaitMode = KernelMode;}; // Цикл ожидания TimeOut = FALSE; while ( (!Event) && (!TimeOut) ){ // Запрашиваем текущее время if (UseQueryPerformanceCounter) { Time = KeQueryPerformanceCounter(NULL); } else { KeQueryTickCount(&Time); }; // Опрашиваем порт на предмет события Value = pFuncReadPort( (PVOID)pWaitData->Port.Number ); Event = (Value & pWaitData->Mask) == pWaitData->Patt; // Проверяем: имеет ли место событие? if (!Event){ // нет события // Проверяем: имеет ли место таймаут? TimeOut = (Time.QuadPart > EndTime.QuadPart); if(TimeOut){ // таймаут WaitResult = pwrTIMEOUT; } else { // нет таймаута // Прекращаем выполнение на TimeInterval, с возможностью прерывания по событию pAbortEvent KeWaitResult = KeWaitForSingleObject( pAbortEvent, WaitReason, //Executive WaitMode, //KernelMode FALSE, // все равно TimeInterval=0 &TimeInterval // =0 ); // Проверяем: имеет ли место pAbortEvent? TimeOut = (KeWaitResult != STATUS_TIMEOUT); if (TimeOut) { WaitResult = pwrABANDONED; }; } } }; // Вычисляем время ожидания if ( 0 != (pWaitData->ControlFlags & bit_wcfReturnWaitIntervalIn100nanosecUnit) ){ // Вычисляем время ожидания в 100 наносекунд if (UseQueryPerformanceCounter) { Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*10000000)/PerformanceFrequency.QuadPart; } else { Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*TimeIncrement); }; } else { // Вычисляем время ожидания в мс if (UseQueryPerformanceCounter) { Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*1000)/PerformanceFrequency.QuadPart; } else { Time.QuadPart = ((Time.QuadPart-StartTime.QuadPart)*TimeIncrement)/10000; }; } } else { // Устанавливаеm значения результатов по-умолчанию Time.QuadPart = 0; WaitResult = pwrSUCCESS; }; // Заполняем данные результата ожидания pResultData->Event = Event; pResultData->WaitResult = WaitResult; pResultData->Data = Value; if (Time.HighPart > 0){ pResultData->TimeOut = 0xFFFFFFFF; } else { pResultData->TimeOut = Time.LowPart; }; return STATUS_SUCCESS; }; // ---------------------------------------- NTSTATUS WriteAndWaitOnPort( IN PGENPORT_WRITE_AND_WAIT_INPUT_BUFFER pWriteAndWaitData, OUT PGENPORT_WAIT_OUTPUT_BUFFER pResultData, IN PKEVENT pAbortEvent ) /*++ Описание процедуры: Эта процедура обрабатывает ЗАПИСЬ в порт1 и ОЖИДАНИЕ события на порту2. (Порт2(pWaitData->Port) and pWaitData->Mask) == pWaitData->Patt в течение pWaitData->TimeOut миллисекунд, опрашивая порт через pWaitData->TimeResolution (но не чаще чем раз в 1 мс). Если pWaitData->TimeResolution == 0, то порт повторно опрашивается через KeQueryTimeIncrement()*100 наносекунд. !!! Если pWaitData->TimeResolution > pWaitData->TimeOut, то порт повторно опрашивается через pWaitData->TimeResolution мс. Аргументы: pWriteAndWaitData - данные для записи и ожидания pResultData - результат ожидания pAbortEvent - сигнал немедленного завершения ожидания, например, драйвер требуется остановить или удалить по запросу системы Return Value: STATUS_SUCCESS -- OK !!! ВНИМАНИЕ функция НЕ ПРОВЕРЯЕТ допустимость значений в pWriteAndWaitData --*/ { pfWritePort pFuncWritePort; NTSTATUS status; BOOLEAN IRQL_change; KIRQL LastIRQL; // Определяем подходящую функцию для записи порта status=GetAppropriateWritePortFunction(&pWriteAndWaitData->WriteData.Port, &pFuncWritePort); if (STATUS_SUCCESS != status){ return status; }; // Определяем: надо ли изменять IRQL if (0 != (pWriteAndWaitData->WaitData.ControlFlags & bit_wcfWaitOnDISPATCH_Level)){ IRQL_change = (KeGetCurrentIrql() < DISPATCH_LEVEL); if (IRQL_change) { // Поднимаем IRQL до DISPATCH_LEVEL KeRaiseIrql(DISPATCH_LEVEL, &LastIRQL); } pFuncWritePort((PULONG)pWriteAndWaitData->WriteData.Port.Number, pWriteAndWaitData->WriteData.Data.ULong); status = WaitOnPortAtDISPATCH_LEVEL(&pWriteAndWaitData->WaitData, pResultData, pAbortEvent); if (IRQL_change) { // Возвращаем IRQL на прежний уровень KeLowerIrql(LastIRQL); } return status; } else if(0 != (pWriteAndWaitData->WaitData.ControlFlags & bit_wcfWaitOnAPC_Level)) { IRQL_change = (KeGetCurrentIrql() < APC_LEVEL); if (IRQL_change) { // Поднимаем IRQL до APC_LEVEL KeRaiseIrql(APC_LEVEL, &LastIRQL); } pFuncWritePort((PULONG)pWriteAndWaitData->WriteData.Port.Number, pWriteAndWaitData->WriteData.Data.ULong); status = WaitOnPortEx(&pWriteAndWaitData->WaitData, pResultData, pAbortEvent); if (IRQL_change) { // Возвращаем IRQL на прежний уровень KeLowerIrql(LastIRQL); } return status; } else { pFuncWritePort((PULONG)pWriteAndWaitData->WriteData.Port.Number, pWriteAndWaitData->WriteData.Data.ULong); return WaitOnPortEx(&pWriteAndWaitData->WaitData, pResultData, pAbortEvent); } }; //---------------------------------------------------------- NTSTATUS GpdIoctlWriteAndWaitPort( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack ) /*++ Описание процедуры: Эта процедура обрабатывает IOCTLs, который записывает данные в порт 1 и ожидает на порту 2 события. Аргументы: pLDI - our local device data pIrp - IO request packet IrpStack - The current stack location IoctlCode - The ioctl code from the IRP Return Value: STATUS_SUCCESS -- OK STATUS_INVALID_PARAMETER -- The buffer sent to the driver was too small to contain the port, or the buffer which would be sent back to the driver was not a multiple of the data size. STATUS_ACCESS_VIOLATION -- An illegal port number was given. --*/ { PGENPORT_WRITE_AND_WAIT_INPUT_OUTPUT_BUFFER pIOBuffer; // Указатель на буфер (исходная информация) ULONG InBufferSize; // Amount of data avail. from caller. ULONG OutBufferSize; // Max data that caller can accept. tTranslatedPortInfo TranslatedPort; NTSTATUS status; ULONG PortType; UCHAR PortSize; GENPORT_WAIT_OUTPUT_BUFFER OutputData; PAGED_CODE(); //myTRAP(); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Size of buffer for data to be sent to application OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; //Проверяем размер данных во входном буфере if ( InBufferSize != sizeof(pIOBuffer->Input) ) { myTRAP(); return STATUS_INVALID_PARAMETER; }; //Проверяем размер данных в выходном буфере if ( OutBufferSize != sizeof(pIOBuffer->Output) ) { myTRAP(); return STATUS_INVALID_PARAMETER; }; pIOBuffer = (PGENPORT_WRITE_AND_WAIT_INPUT_OUTPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer; // Проверяем порт для ЗАПИСИ на допустимость status = TranslatePortEx( &pIOBuffer->Input.WriteData.Port, // Get the I/O port number from the buffer pLDI, &pLDI->LastWrite, &pIOBuffer->Input.WriteData.Port ); if(!(status == STATUS_SUCCESS) ){ myTRAP(); return status; // негодный порт } // Проверяем порт для ОЖИДАНИЯ на допустимость status = TranslatePortEx( &pIOBuffer->Input.WaitData.Port, // Get the I/O port number from the buffer pLDI, &pLDI->LastRead, &pIOBuffer->Input.WaitData.Port ); if(!(status == STATUS_SUCCESS) ){ myTRAP(); return status; // негодный порт } // Проверяе соотношение разрешения и времени ожидания if (pIOBuffer->Input.WaitData.TimeResolution >= pIOBuffer->Input.WaitData.TimeOut){ pIOBuffer->Input.WaitData.TimeResolution = pIOBuffer->Input.WaitData.TimeOut / 10; } status = WriteAndWaitOnPort( &pIOBuffer->Input, &OutputData, &pLDI->AbortEvent); if(STATUS_SUCCESS != status){ myTRAP(); return status; } pIOBuffer->Output = OutputData; pIrp->IoStatus.Information = sizeof(OutputData); return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS GpdIoctlCheckMultifunctionInputBuffer( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack ) /*++ Описание процедуры: Эта процедура проверяет допустимость входного буфера для многофункциональной операции. Аргументы: pLDI - our local device data pIrp - IO request packet IrpStack - The current stack location Возвращает: STATUS_SUCCESS -- OK. В буфере возврата возвращается результат проверки. STATUS_INVALID_PARAMETER -- Буфера, переданные драйверу, или др. имеют неверный размер, или иные ошибки, см. возвращаемое значение. --*/ { PGENPORT_MULTIFUNCTION_IO_CHECK_INPUT_OUTPUT_BUFFER pIOBuffer; // Указатель на буфер (исходная информация) ULONG InBufferSize; // Amount of data avail. from caller. ULONG OutBufferSize; // Max data that caller can accept. NTSTATUS status; ULONG OutputBufferSize; GENPORT_MULTIFUNCTION_IO_OPERATION_CHECK_OUTPUT_BUFFER OutputData; PAGED_CODE(); myTRAP(); DebugPrint(("GpdIoctlCheckMultifunctionInputBuffer\n")); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Size of buffer for data to be sent to application OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; DebugPrint(("InBufferSize = %d; OutBufferSize = %d\n",InBufferSize, OutBufferSize)); //Проверяем размер данных в выходном буфере if ( OutBufferSize != sizeof(pIOBuffer->Output) ) { myTRAP(); return STATUS_INVALID_PARAMETER; }; pIOBuffer = (PGENPORT_MULTIFUNCTION_IO_CHECK_INPUT_OUTPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer; //Проверяем размер данных во входном буфере status = CheckMultifunctionIrpBuffer( &pIOBuffer->Input, InBufferSize, &OutputData, &OutputBufferSize, pLDI ); if ( STATUS_SUCCESS == status ) { // если буфер проверен успешно, то в OutputData.ErrorCode = mferrOK; // код ошибки заносится OK OutputData.OutputDataSize = OutputBufferSize; // размер необходимого выходного буфера }; // Возвращаем данные pIOBuffer->Output = OutputData; pIrp->IoStatus.Information = sizeof(OutputData); return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS CheckMultifunctionIrpBuffer( IN PGENPORT_MULTIFUNCTION_IO_INPUT_BUFFER pBuffer, IN ULONG uBufferSize, OUT PGENPORT_MULTIFUNCTION_IO_ERROR_DESCRIPTION pErrorDesc, OUT PULONG pOutputBufferSize, IN PLOCAL_DEVICE_INFO pLDI ) /*++ Описание процедуры: Эта процедура проверяет и предобрабатывает буфер многофункционального IOCTL: 1) Проверяет размеры и данные на допустимость. 2) Транслирует порты. Аргументы: pBuffer - Входной буфер, который надо обработать. pErrorDesc - Описание ошибки, если возвращен не STATUS_SUCCESS. pOutputBufferSize - Необходимый размер выходного буфера. pLDI - локальные данные драйвера. Возвращает: STATUS_SUCCESS -- OK. В буфере pBuffer все порты заменены на транслированные. pOutputBufferSize = необходимый размер буфера для возврата данных. STATUS_INVALID_PARAMETER -- буфер слишком мал (велик) или содержит недопустимые данные, см. описание ошибки в pErrorDesc. --*/ { ULONG RecordCount, RecordNumber; // номер текущей записи в буфере PGENPORT_MULTIFUNCTION_IO_INPUT_HEADER pCurRecord; NTSTATUS status; _int64 BufferSize; _int64 OutputBufferSize; ULONG Cnt, Size, ULongCnt; LONG RecordSize; PAGED_CODE(); //myTRAP(); DebugPrint(("CheckMultifunctionIrpBuffer\n")); // проверяем минимально необходимый размер буфера DebugPrint(("uBufferSize=%u\n", uBufferSize)); if( uBufferSize < sizeof(GENPORT_MULTIFUNCTION_IO_INPUT_BUFFER) ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall; pErrorDesc->RecordNumber = 0; return STATUS_INVALID_PARAMETER; }; // ожидаемое число записей в буфере RecordCount = pBuffer->Count; DebugPrint(("RecordCount = %u\n",RecordCount)); // проверяем число записей в буфере if( 0 == RecordCount ){ myTRAP(); pErrorDesc->ErrorCode = mferrRecordsCountEqZero; pErrorDesc->RecordNumber = 0; return STATUS_INVALID_PARAMETER; }; // размер буфера BufferSize = uBufferSize; // указатель на текущую запись в буфере pCurRecord = &(pBuffer->Data); // размер остатка буфера BufferSize = BufferSize - sizeof(pBuffer->Count); // размер выходного буфера OutputBufferSize = sizeof(GENPORT_MULTIFUNCTION_IO_OUTPUT_HEADER); DebugPrint(("OutputBufferSize=%u\n", OutputBufferSize)); for (RecordNumber=1; (RecordNumber <= RecordCount); RecordNumber++) { DebugPrint(("BufferSize=%u; ", BufferSize)); DebugPrint(("OutputBufferSize=%u\n", OutputBufferSize)); // проверяем минимально необходимый размер буфера if( BufferSize < sizeof(*pCurRecord) ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall_2; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; // разбираем код операции switch(pCurRecord->OpCode) { case mfocRead: // чтение одного значения // myTRAP(); // проверяем необходимый размер буфера if( BufferSize < sizeof(GENPORT_MULTIFUNCTION_IO_READ_INPUT) ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall_3; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; // транслируем порт status = TranslatePortEx( &((PGENPORT_MULTIFUNCTION_IO_READ_INPUT)pCurRecord)->Port, pLDI, &pLDI->LastRead, &((PGENPORT_MULTIFUNCTION_IO_READ_INPUT)pCurRecord)->Port ); if( status != STATUS_SUCCESS ){ myTRAP(); pErrorDesc->ErrorCode = mferrInvalidPort; pErrorDesc->RecordNumber = RecordNumber; return status; // негодный порт } // все хорошо, переходим к следующей записи BufferSize = BufferSize - sizeof(GENPORT_MULTIFUNCTION_IO_READ_INPUT); pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_READ_INPUT_NEXT)pCurRecord)->NextBuffer; // размер выходного буфера OutputBufferSize = OutputBufferSize + sizeof(GENPORT_MULTIFUNCTION_IO_READ_OUTPUT); break; case mfocWrite: // запись одного значения // myTRAP(); // проверяем необходимый размер буфера if( BufferSize < sizeof(GENPORT_MULTIFUNCTION_IO_WRITE_INPUT) ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall_3; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; // транслируем порт status = TranslatePortEx( &((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port, pLDI, &pLDI->LastWrite, &((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port ); if( status != STATUS_SUCCESS ){ myTRAP(); pErrorDesc->ErrorCode = mferrInvalidPort; pErrorDesc->RecordNumber = RecordNumber; return status; // негодный порт } // все хорошо, переходим к следующей записи DebugPrint(("Port=0x%x;",((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port.Number)); DebugPrint1((" Type=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port.Type)); DebugPrint1((" MemType=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port.MemoryType)); DebugPrint1((" Data=%u;\n",((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Data.ULong)); BufferSize = BufferSize - sizeof(GENPORT_MULTIFUNCTION_IO_WRITE_INPUT); pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT_NEXT)pCurRecord)->NextBuffer; // размер выходного буфера = 0 break; case mfocReadBuffer: // чтение нескольких значений //myTRAP(); // проверяем необходимый размер буфера if( BufferSize < sizeof(GENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT) ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall_3; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; // транслируем порт status = TranslatePortEx( &((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Port, pLDI, &pLDI->LastRead, &((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Port ); if( status != STATUS_SUCCESS ){ myTRAP(); pErrorDesc->ErrorCode = mferrInvalidPort; pErrorDesc->RecordNumber = RecordNumber; return status; // негодный порт } BufferSize = BufferSize - sizeof(GENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT); // размер выходного буфера Cnt = ((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Count; if( 0 == Cnt ){ myTRAP(); pErrorDesc->ErrorCode = mferrReadBuffer_CountZero; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; Size = GetPortSize(((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Port.Type); OutputBufferSize = OutputBufferSize +((Cnt*Size +3)/4)*4; // округление до целого числа ULONG // все хорошо, переходим к следующей записи pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT_NEXT)pCurRecord)->NextBuffer; break; case mfocWriteBuffer: // запись нескольких значений //myTRAP(); // проверяем необходимый размер буфера if( BufferSize < sizeof(GENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT) ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall_3; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; // вычисление полного размера записи вместе с массивом данных буфера Cnt = ((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Count; if( 0 == Cnt ){ myTRAP(); pErrorDesc->ErrorCode = mferrWriteBuffer_CountZero; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; Size = GetPortSize(((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Type); if( 0 == Size ){ myTRAP(); pErrorDesc->ErrorCode = mferrInvalidPortType; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; ULongCnt = ((Cnt*Size +3)/4); // округление размера массива данных до целого числа ULONG RecordSize = ULongCnt*4 - 4 // один ULONG уже есть в GENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT + sizeof(GENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT); DebugPrint(("RecordSize=%u;",RecordSize)); DebugPrint1(("ULongCnt=%u;\n",ULongCnt)); DebugPrint(("ArraySize=%u;\n",ULongCnt*4)); if( BufferSize < RecordSize ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall_4; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; // транслируем порт status = TranslatePortEx( &((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port, pLDI, &pLDI->LastWrite, &((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port ); if( status != STATUS_SUCCESS ){ myTRAP(); pErrorDesc->ErrorCode = mferrInvalidPort; pErrorDesc->RecordNumber = RecordNumber; return status; // негодный порт } BufferSize = BufferSize - RecordSize; // размер выходного буфера = 0 DebugPrint(("Port=0x%x;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Number)); DebugPrint1((" Type=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Type)); DebugPrint1((" MemType=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.MemoryType)); DebugPrint1((" Count=%u;\n",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Count)); DebugPrint1((" DataArray= %u, %u, %u, %u\n", ((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.UChar[0],((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.UChar[1], ((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.UChar[2], ((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.UChar[3])); // все хорошо, переходим к следующей записи pCurRecord = (PGENPORT_MULTIFUNCTION_IO_INPUT_HEADER) &((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.ULong[ULongCnt]; break; case mfocWait: // ожидание //myTRAP(); // проверяем необходимый размер буфера if( BufferSize < sizeof(GENPORT_MULTIFUNCTION_IO_WAIT_INPUT) ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall_3; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; // транслируем порт status = TranslatePortEx( &((PGENPORT_MULTIFUNCTION_IO_WAIT_INPUT)pCurRecord)->WaitData.Port, pLDI, &pLDI->LastRead, &((PGENPORT_MULTIFUNCTION_IO_WAIT_INPUT)pCurRecord)->WaitData.Port ); if( status != STATUS_SUCCESS ){ myTRAP(); pErrorDesc->ErrorCode = mferrInvalidPort; pErrorDesc->RecordNumber = RecordNumber; return status; // негодный порт } // все хорошо, переходим к следующей записи BufferSize = BufferSize - sizeof(GENPORT_MULTIFUNCTION_IO_WAIT_INPUT); pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_WAIT_INPUT_NEXT)pCurRecord)->NextBuffer; // размер выходного буфера OutputBufferSize = OutputBufferSize + sizeof(GENPORT_MULTIFUNCTION_IO_WAIT_OUTPUT); break; case mfocWriteAndWait: // запись одного значения+ожидание //myTRAP(); // проверяем необходимый размер буфера if( BufferSize < sizeof(GENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT) ){ myTRAP(); pErrorDesc->ErrorCode = mferrBufferTooSmall_3; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; // транслируем порт status = TranslatePortEx( &((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT)pCurRecord)->Data.WriteData.Port, pLDI, &pLDI->LastWrite, &((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT)pCurRecord)->Data.WriteData.Port ); if( status != STATUS_SUCCESS ){ myTRAP(); pErrorDesc->ErrorCode = mferrInvalidPort; pErrorDesc->RecordNumber = RecordNumber; return status; // негодный порт } // транслируем порт status = TranslatePortEx( &((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT)pCurRecord)->Data.WaitData.Port, pLDI, &pLDI->LastRead, &((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT)pCurRecord)->Data.WaitData.Port ); if( status != STATUS_SUCCESS ){ myTRAP(); pErrorDesc->ErrorCode = mferrInvalidPort_2; pErrorDesc->RecordNumber = RecordNumber; return status; // негодный порт } // все хорошо, переходим к следующей записи BufferSize = BufferSize - sizeof(GENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT); pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT_NEXT)pCurRecord)->NextBuffer; // размер выходного буфера OutputBufferSize = OutputBufferSize + sizeof(GENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_OUTPUT); break; case mfocInvalidOperation: // неверная операция - резервировано default: myTRAP(); pErrorDesc->ErrorCode = mferrInvalidOpCode; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; } if( OutputBufferSize > 0xFFFFFF00 ){ myTRAP(); pErrorDesc->ErrorCode = mferrOutputBufferTooLarge; pErrorDesc->RecordNumber = RecordNumber; return STATUS_INVALID_PARAMETER; }; *pOutputBufferSize = ((PLARGE_INTEGER)&OutputBufferSize)->LowPart; return STATUS_SUCCESS; } //---------------------------- NTSTATUS ProcessMultifunctionIrpBuffer( IN PGENPORT_MULTIFUNCTION_IO_INPUT_BUFFER pBuffer, OUT PGENPORT_MULTIFUNCTION_IO_OUTPUT_BUFFER pOutputBuffer, IN PLOCAL_DEVICE_INFO pLDI ) /*++ Описание процедуры: Эта процедура исполняет буфер многофункционального IOCTL. ПРАКТИЧЕСКИ НИКАКИХ проверок не осуществляется. Для проверки и предобработки буфера следует вызвать CheckMultifunctionIrpBuffer. Аргументы: pBuffer - Входной буфер, который надо обработать. pOutputBuffer - Выходной буфер, который содержит результаты работы. pLDI - локальные данные драйвера. Возвращает: STATUS_SUCCESS -- OK STATUS_INVALID_PARAMETER -- буфер слишком мал (велик) или содержит недопустимые данные. --*/ { ULONG RecordCount, OutRecordCount, RecordNumber; // номер текущей записи в буфере PGENPORT_MULTIFUNCTION_IO_INPUT_HEADER pCurRecord; PULONG pCurOutRecord; NTSTATUS status; ULONG Cnt, Size; LONG RecordSize; pfReadPort pFuncReadPort; pfWritePort pFuncWritePort; pfReadPortBuffer pFuncReadPortBuffer; pfWritePortBuffer pFuncWritePortBuffer; GENPORT_WAIT_OUTPUT_BUFFER WaitResultData; PAGED_CODE(); myTRAP(); DebugPrint(("ProcessMultifunctionIrpBuffer\n")); // число записей в буфере RecordCount = pBuffer->Count; // указатель на текущую запись в буфере pCurRecord = &(pBuffer->Data); // указатель на текущую запись в ВЫХОДНОМ буфере pCurOutRecord = &pOutputBuffer->Data; // счетчик записей в ВЫХОДНОМ буфере pOutputBuffer->Header.OpCount=RecordCount; pOutputBuffer->Header.RecordCount=0; OutRecordCount = 0; for (RecordNumber=1; (RecordNumber <= RecordCount); RecordNumber++) { // проверяем нет ли запроса на прерывание if (AbortState(pLDI)){ // есть запрос на прерывание - завершаем, возвратив обработанное число записей myTRAP(); pOutputBuffer->Header.OpCount=RecordNumber-1; pOutputBuffer->Header.RecordCount = OutRecordCount; return STATUS_SUCCESS; } // разбираем код операции switch(pCurRecord->OpCode) { case mfocRead: // чтение одного значения myTRAP(); // Определяем подходящую функцию для чтения порта status = GetAppropriateReadPortFunction( &((PGENPORT_MULTIFUNCTION_IO_READ_INPUT)pCurRecord)->Port, &pFuncReadPort); if (STATUS_SUCCESS != status) { myTRAP(); return STATUS_INVALID_PARAMETER; }; // читаем ((PGENPORT_MULTIFUNCTION_IO_READ_OUTPUT)pCurOutRecord)->Data.ULong = pFuncReadPort((PVOID)((PGENPORT_MULTIFUNCTION_IO_READ_INPUT)pCurRecord)->Port.Number); DebugPrint(("Port=0x%x;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Number)); DebugPrint1((" Type=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Type)); DebugPrint1((" MemType=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.MemoryType)); DebugPrint1((" Count=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Count)); DebugPrint1((" Data= %u\n", ((PGENPORT_MULTIFUNCTION_IO_READ_OUTPUT)pCurOutRecord)->Data.ULong)); // все хорошо, переходим к следующей записи pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_READ_INPUT_NEXT)pCurRecord)->NextBuffer; pCurOutRecord = &((PGENPORT_MULTIFUNCTION_IO_READ_OUTPUT_NEXT)pCurOutRecord)->NextBuffer; OutRecordCount = OutRecordCount+1; break; case mfocWrite: // запись одного значения // Определяем подходящую функцию для записи порта status = GetAppropriateWritePortFunction( &((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port, &pFuncWritePort); if (STATUS_SUCCESS != status) { myTRAP(); return STATUS_INVALID_PARAMETER; }; // пишем pFuncWritePort( (PVOID)((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port.Number, ((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Data.ULong ); DebugPrint(("Port=0x%x;",((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port.Number)); DebugPrint1((" Type=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port.Type)); DebugPrint1((" MemType=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Port.MemoryType)); DebugPrint1((" Data= %u\n", ((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT)pCurRecord)->Data.ULong)); // все хорошо, переходим к следующей записи pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_WRITE_INPUT_NEXT)pCurRecord)->NextBuffer; break; case mfocReadBuffer: // чтение нескольких значений myTRAP(); // Определяем подходящую функцию для чтения порта status = GetAppropriateReadPortBufferFunction( &((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Port, &pFuncReadPortBuffer ); if (STATUS_SUCCESS != status) { myTRAP(); return STATUS_INVALID_PARAMETER; }; // читаем pFuncReadPortBuffer( (PVOID)((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Port.Number, &((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_OUTPUT)pCurOutRecord)->Data.ULong[0], ((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Count ); DebugPrint(("Port=0x%x;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Number)); DebugPrint1((" Type=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Type)); DebugPrint1((" MemType=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.MemoryType)); DebugPrint1((" Count=%u;\n",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Count)); DebugPrint1((" DataArray= %u, %u, %u, %u\n", ((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_OUTPUT)pCurRecord)->Data.UChar[0],((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_OUTPUT)pCurRecord)->Data.UChar[1], ((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_OUTPUT)pCurRecord)->Data.UChar[2], ((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_OUTPUT)pCurRecord)->Data.UChar[3])); // размер выходного буфера Cnt = ((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Count; Size = GetPortSize(((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT)pCurRecord)->Port.Type); // округление до целого числа ULONG pCurOutRecord = &((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_OUTPUT)pCurOutRecord)->Data.ULong[(Cnt*Size +3)/4]; OutRecordCount = OutRecordCount+1; // все хорошо, переходим к следующей записи pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_READ_BUFFER_INPUT_NEXT)pCurRecord)->NextBuffer; break; case mfocWriteBuffer: // запись нескольких значений myTRAP(); // Определяем подходящую функцию для чтения порта status = GetAppropriateWritePortBufferFunction( &((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port, &pFuncWritePortBuffer ); if (STATUS_SUCCESS != status) { myTRAP(); return STATUS_INVALID_PARAMETER; }; // пишем Cnt = ((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Count; pFuncWritePortBuffer( (PVOID)((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Number, &((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.ULong[0], Cnt ); // вычисление полного размера записи вместе с массивом данных буфера Size = GetPortSize(((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Type); DebugPrint(("Port=0x%x;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Number)); DebugPrint1((" Type=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.Type)); DebugPrint1((" MemType=%u;",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Port.MemoryType)); DebugPrint1((" Count=%u;\n",((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Count)); DebugPrint1((" DataArray= %u, %u, %u, %u\n", ((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.UChar[0],((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.UChar[1], ((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.UChar[2], ((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.UChar[3])); // все хорошо, переходим к следующей записи pCurRecord = (PGENPORT_MULTIFUNCTION_IO_INPUT_HEADER) &((PGENPORT_MULTIFUNCTION_IO_WRITE_BUFFER_INPUT)pCurRecord)->Data.ULong[((Cnt*Size +3)/4)]; break; case mfocWait: // ожидание myTRAP(); status = WaitOnPortEx( &((PGENPORT_MULTIFUNCTION_IO_WAIT_INPUT)pCurRecord)->WaitData, &WaitResultData, &pLDI->AbortEvent ); if (STATUS_SUCCESS != status) { myTRAP(); return STATUS_INVALID_PARAMETER; }; // запоминаем результат *((PGENPORT_MULTIFUNCTION_IO_WAIT_OUTPUT)pCurOutRecord) = WaitResultData; // переходим к следующей записи pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_WAIT_INPUT_NEXT)pCurRecord)->NextBuffer; pCurOutRecord = &((PGENPORT_MULTIFUNCTION_IO_WAIT_OUTPUT_NEXT)pCurOutRecord)->NextBuffer; OutRecordCount = OutRecordCount+1; break; case mfocWriteAndWait: // запись одного значения+ожидание myTRAP(); status = WriteAndWaitOnPort( &((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT)pCurRecord)->Data, &WaitResultData, &pLDI->AbortEvent ); if (STATUS_SUCCESS != status) { myTRAP(); return STATUS_INVALID_PARAMETER; }; // запоминаем результат *((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_OUTPUT)pCurOutRecord) = WaitResultData; // переходим к следующей записи pCurRecord = &((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_INPUT_NEXT)pCurRecord)->NextBuffer; pCurOutRecord = &((PGENPORT_MULTIFUNCTION_IO_WRITE_AND_WAIT_OUTPUT_NEXT)pCurOutRecord)->NextBuffer; OutRecordCount = OutRecordCount+1; break; case mfocInvalidOperation: // неверная операция - резервировано default: myTRAP(); return STATUS_INVALID_PARAMETER; }; } pOutputBuffer->Header.RecordCount = OutRecordCount; return STATUS_SUCCESS; } //---------------------------------------------------------- NTSTATUS GpdIoctlProcessMultifunctionInputBuffer( IN PLOCAL_DEVICE_INFO pLDI, IN PIRP pIrp, IN PIO_STACK_LOCATION IrpStack ) /*++ Описание процедуры: Эта процедура исполняет команды входного буфера для многофункциональной операции ввода/вывода. Аргументы: pLDI - our local device data pIrp - IO request packet IrpStack - The current stack location Возвращает: STATUS_SUCCESS -- OK STATUS_INVALID_PARAMETER -- Буфер, переданный драйверу, имеет неверный размер, неверное содержание или иные ошибки, см. возвращаемое значение. --*/ { PGENPORT_MULTIFUNCTION_IO_CHECK_INPUT_OUTPUT_BUFFER pIOBuffer; // Указатель на буфер (исходная информация) ULONG InBufferSize; // Amount of data avail. from caller. ULONG OutBufferSize; // Max data that caller can accept. NTSTATUS status; ULONG OutputBufferSize; GENPORT_MULTIFUNCTION_IO_OPERATION_CHECK_OUTPUT_BUFFER ErrorOutputData; PGENPORT_MULTIFUNCTION_IO_OUTPUT_BUFFER pOutputData; PAGED_CODE(); myTRAP(); // Size of buffer containing data from application InBufferSize = IrpStack->Parameters.DeviceIoControl.InputBufferLength; // Size of buffer for data to be sent to application OutBufferSize = IrpStack->Parameters.DeviceIoControl.OutputBufferLength; pIOBuffer = (PGENPORT_MULTIFUNCTION_IO_CHECK_INPUT_OUTPUT_BUFFER)pIrp->AssociatedIrp.SystemBuffer; //Проверяем размер и правильность данных во входном буфере status = CheckMultifunctionIrpBuffer( &pIOBuffer->Input, InBufferSize, &ErrorOutputData, &OutputBufferSize, pLDI ); if ( STATUS_SUCCESS != status ) { myTRAP(); return STATUS_INVALID_PARAMETER_1; }; // Проверяем размер данных в выходном буфере if ( OutBufferSize < OutputBufferSize ) { myTRAP(); return STATUS_INVALID_PARAMETER_2; }; // Размещаем буфер для результатов операции pOutputData = ExAllocatePool( PagedPool, OutputBufferSize ); // Проверяем результат размещения буфера if ( NULL == pOutputData ) { myTRAP(); return STATUS_NO_MEMORY; }; // Выполняем операцию status = ProcessMultifunctionIrpBuffer( &pIOBuffer->Input, pOutputData, pLDI ); if ( STATUS_SUCCESS != status ) { myTRAP(); ExFreePool(pOutputData); return STATUS_INVALID_PARAMETER_1; }; // Возвращаем данные RtlCopyMemory( &pIOBuffer->Output, pOutputData, OutputBufferSize ); pIrp->IoStatus.Information = OutputBufferSize; // Освобождаем буфер для результатов операции ExFreePool(pOutputData); return STATUS_SUCCESS; } //-------------------------------- VOID Sleep(IN ULONG MicrosecondsToSleep) { KTIMER SleepTimer; LARGE_INTEGER TimerValue; ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL); KeInitializeTimerEx(&SleepTimer, SynchronizationTimer); TimerValue.QuadPart = Int32x32To64(MicrosecondsToSleep, -10); KeSetTimer(&SleepTimer, TimerValue, NULL); KeWaitForSingleObject(&SleepTimer, Executive, KernelMode, FALSE, NULL); } void Test ( UCHAR MinorFunction ) { KIRQL X; X = KeGetCurrentIrql(); } #if DBG PCHAR PnPMinorFunctionString ( UCHAR MinorFunction ) { switch (MinorFunction) { case IRP_MN_START_DEVICE: return "IRP_MN_START_DEVICE"; case IRP_MN_QUERY_REMOVE_DEVICE: return "IRP_MN_QUERY_REMOVE_DEVICE"; case IRP_MN_REMOVE_DEVICE: return "IRP_MN_REMOVE_DEVICE"; case IRP_MN_CANCEL_REMOVE_DEVICE: return "IRP_MN_CANCEL_REMOVE_DEVICE"; case IRP_MN_STOP_DEVICE: return "IRP_MN_STOP_DEVICE"; case IRP_MN_QUERY_STOP_DEVICE: return "IRP_MN_QUERY_STOP_DEVICE"; case IRP_MN_CANCEL_STOP_DEVICE: return "IRP_MN_CANCEL_STOP_DEVICE"; case IRP_MN_QUERY_DEVICE_RELATIONS: return "IRP_MN_QUERY_DEVICE_RELATIONS"; case IRP_MN_QUERY_INTERFACE: return "IRP_MN_QUERY_INTERFACE"; case IRP_MN_QUERY_CAPABILITIES: return "IRP_MN_QUERY_CAPABILITIES"; case IRP_MN_QUERY_RESOURCES: return "IRP_MN_QUERY_RESOURCES"; case IRP_MN_QUERY_RESOURCE_REQUIREMENTS: return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS"; case IRP_MN_QUERY_DEVICE_TEXT: return "IRP_MN_QUERY_DEVICE_TEXT"; case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS"; case IRP_MN_READ_CONFIG: return "IRP_MN_READ_CONFIG"; case IRP_MN_WRITE_CONFIG: return "IRP_MN_WRITE_CONFIG"; case IRP_MN_EJECT: return "IRP_MN_EJECT"; case IRP_MN_SET_LOCK: return "IRP_MN_SET_LOCK"; case IRP_MN_QUERY_ID: return "IRP_MN_QUERY_ID"; case IRP_MN_QUERY_PNP_DEVICE_STATE: return "IRP_MN_QUERY_PNP_DEVICE_STATE"; case IRP_MN_QUERY_BUS_INFORMATION: return "IRP_MN_QUERY_BUS_INFORMATION"; case IRP_MN_DEVICE_USAGE_NOTIFICATION: return "IRP_MN_DEVICE_USAGE_NOTIFICATION"; case IRP_MN_SURPRISE_REMOVAL: return "IRP_MN_SURPRISE_REMOVAL"; case IRP_MN_QUERY_LEGACY_BUS_INFORMATION: return "IRP_MN_QUERY_LEGACY_BUS_INFORMATION"; default: return "IRP_MN_?????"; } } #endif