/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: Parallel Port Function Driver * FILE: drivers/parallel/parport/fdo.c * PURPOSE: FDO functions */ #include "parport.h" /* * The following constants describe the various signals of the printer port * hardware. Note that the hardware inverts some signals and that some * signals are active low. An example is LP_STROBE, which must be programmed * with 1 for being active and 0 for being inactive, because the strobe signal * gets inverted, but it is also active low. */ /* * bit defines for 8255 status port * base + 1 * accessed with LP_S(minor), which gets the byte... */ #define LP_PBUSY 0x80 /* inverted input, active high */ #define LP_PACK 0x40 /* unchanged input, active low */ #define LP_POUTPA 0x20 /* unchanged input, active high */ #define LP_PSELECD 0x10 /* unchanged input, active high */ #define LP_PERRORP 0x08 /* unchanged input, active low */ /* * defines for 8255 control port * base + 2 * accessed with LP_C(minor) */ #define LP_PINTEN 0x10 #define LP_PSELECP 0x08 /* inverted output, active low */ #define LP_PINITP 0x04 /* unchanged output, active low */ #define LP_PAUTOLF 0x02 /* inverted output, active low */ #define LP_PSTROBE 0x01 /* inverted output, active low */ /* FUNCTIONS ****************************************************************/ NTSTATUS NTAPI AddDeviceInternal(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo, IN PULONG pLptPortNumber OPTIONAL, OUT PDEVICE_OBJECT* pFdo OPTIONAL) { PFDO_DEVICE_EXTENSION DeviceExtension = NULL; PDEVICE_OBJECT Fdo = NULL; WCHAR DeviceNameBuffer[32]; UNICODE_STRING DeviceName; NTSTATUS Status; DPRINT("AddDeviceInternal()\n"); ASSERT(DriverObject); ASSERT(Pdo); /* Create new device object */ swprintf(DeviceNameBuffer, L"\\Device\\ParallelPort%lu", IoGetConfigurationInformation()->ParallelCount); RtlInitUnicodeString(&DeviceName, DeviceNameBuffer); Status = IoCreateDevice(DriverObject, sizeof(FDO_DEVICE_EXTENSION), &DeviceName, FILE_DEVICE_PARALLEL_PORT, FILE_DEVICE_SECURE_OPEN, FALSE, &Fdo); if (!NT_SUCCESS(Status)) { DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status); Fdo = NULL; goto done; } DeviceExtension = (PFDO_DEVICE_EXTENSION)Fdo->DeviceExtension; RtlZeroMemory(DeviceExtension, sizeof(FDO_DEVICE_EXTENSION)); DeviceExtension->Common.IsFDO = TRUE; DeviceExtension->Common.PnpState = dsStopped; DeviceExtension->ParallelPortNumber = IoGetConfigurationInformation()->ParallelCount++; if (pLptPortNumber == NULL) DeviceExtension->LptPort = DeviceExtension->ParallelPortNumber + 1; else DeviceExtension->LptPort = *pLptPortNumber; DeviceExtension->Pdo = Pdo; Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice); if (!NT_SUCCESS(Status)) { DPRINT1("IoAttachDeviceToDeviceStackSafe() failed (Status 0x%08lx)\n", Status); goto done; } if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE) Fdo->Flags |= DO_POWER_PAGABLE; if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO) Fdo->Flags |= DO_BUFFERED_IO; if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO) Fdo->Flags |= DO_DIRECT_IO; /* Choose default strategy */ if ((Fdo->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)) == 0) Fdo->Flags |= DO_BUFFERED_IO; Fdo->Flags &= ~DO_DEVICE_INITIALIZING; if (pFdo) { *pFdo = Fdo; } return STATUS_SUCCESS; done: if (Fdo) { IoDeleteDevice(Fdo); } return Status; } NTSTATUS NTAPI FdoStartDevice(IN PDEVICE_OBJECT DeviceObject, IN PCM_RESOURCE_LIST ResourceList, IN PCM_RESOURCE_LIST ResourceListTranslated) { PFDO_DEVICE_EXTENSION DeviceExtension; WCHAR DeviceNameBuffer[32]; WCHAR LinkNameBuffer[32]; WCHAR LptPortBuffer[32]; UNICODE_STRING DeviceName; UNICODE_STRING LinkName; UNICODE_STRING LptPort; ULONG i; // ULONG Vector = 0; // KIRQL Dirql = 0; // KAFFINITY Affinity = 0; // KINTERRUPT_MODE InterruptMode = Latched; // BOOLEAN ShareInterrupt = TRUE; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING KeyName; HANDLE KeyHandle; NTSTATUS Status; DPRINT("FdoStartDevice ()\n"); DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ASSERT(DeviceExtension); ASSERT(DeviceExtension->Common.IsFDO == TRUE); if (!ResourceList) { DPRINT1("No allocated resources sent to driver\n"); return STATUS_INSUFFICIENT_RESOURCES; } if (ResourceList->Count != 1) { DPRINT1("Wrong number of allocated resources sent to driver\n"); return STATUS_INSUFFICIENT_RESOURCES; } if ((ResourceList->List[0].PartialResourceList.Version != 1) || (ResourceList->List[0].PartialResourceList.Revision != 1) || (ResourceListTranslated->List[0].PartialResourceList.Version != 1) || (ResourceListTranslated->List[0].PartialResourceList.Revision != 1)) { DPRINT1("Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n", ResourceList->List[0].PartialResourceList.Version, ResourceList->List[0].PartialResourceList.Revision, ResourceListTranslated->List[0].PartialResourceList.Version, ResourceListTranslated->List[0].PartialResourceList.Revision); return STATUS_REVISION_MISMATCH; } DeviceExtension->BaseAddress = 0; for (i = 0; i < ResourceList->List[0].PartialResourceList.Count; i++) { PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[i]; PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptorTranslated = &ResourceListTranslated->List[0].PartialResourceList.PartialDescriptors[i]; switch (PartialDescriptor->Type) { case CmResourceTypePort: DPRINT("Port: BaseAddress 0x%lx Length %lu\n", PartialDescriptor->u.Port.Start.u.LowPart, PartialDescriptor->u.Port.Length); if (DeviceExtension->BaseAddress == 0) { if (PartialDescriptor->u.Port.Length < 8) return STATUS_INSUFFICIENT_RESOURCES; DeviceExtension->BaseAddress = PartialDescriptor->u.Port.Start.u.LowPart; } break; case CmResourceTypeInterrupt: DPRINT("Interrupt: Level %lu Vector %lu\n", PartialDescriptorTranslated->u.Interrupt.Level, PartialDescriptorTranslated->u.Interrupt.Vector); // Dirql = (KIRQL)PartialDescriptorTranslated->u.Interrupt.Level; // Vector = PartialDescriptorTranslated->u.Interrupt.Vector; // Affinity = PartialDescriptorTranslated->u.Interrupt.Affinity; // if (PartialDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED) // InterruptMode = Latched; // else // InterruptMode = LevelSensitive; // ShareInterrupt = (PartialDescriptorTranslated->ShareDisposition == CmResourceShareShared); break; default: DPRINT1("Other ressource: \n"); break; } } DPRINT("New LPT port: Base 0x%lx\n", DeviceExtension->BaseAddress); if (!DeviceExtension->BaseAddress) return STATUS_INSUFFICIENT_RESOURCES; #if 0 if (!Dirql) return STATUS_INSUFFICIENT_RESOURCES; #endif /* Create link \DosDevices\LPTX -> \Device\ParallelPortX */ swprintf(DeviceNameBuffer, L"\\Device\\ParallelPort%lu", DeviceExtension->ParallelPortNumber); swprintf(LinkNameBuffer, L"\\DosDevices\\LPT%lu", DeviceExtension->LptPort); swprintf(LptPortBuffer, L"LPT%lu", DeviceExtension->LptPort); RtlInitUnicodeString(&DeviceName, DeviceNameBuffer); RtlInitUnicodeString(&LinkName, LinkNameBuffer); RtlInitUnicodeString(&LptPort, LptPortBuffer); Status = IoCreateSymbolicLink(&LinkName, &DeviceName); if (!NT_SUCCESS(Status)) { DPRINT1("IoCreateSymbolicLink() failed with status 0x%08x\n", Status); return Status; } /* Write an entry value under HKLM\HARDWARE\DeviceMap\PARALLEL PORTS. */ /* This step is not mandatory, so do not exit in case of error. */ RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\HARDWARE\\DeviceMap\\PARALLEL PORTS"); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwCreateKey(&KeyHandle, KEY_SET_VALUE, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, NULL); if (NT_SUCCESS(Status)) { /* Key = \Device\Parallelx, Value = LPTx */ ZwSetValueKey(KeyHandle, &DeviceName, 0, REG_SZ, LptPortBuffer, LptPort.Length + sizeof(WCHAR)); ZwClose(KeyHandle); } DeviceExtension->Common.PnpState = dsStarted; /* We don't really care if the call succeeded or not... */ return STATUS_SUCCESS; } /* PUBLIC FUNCTIONS *********************************************************/ NTSTATUS NTAPI AddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT Pdo) { DPRINT("AddDevice(%p %p)\n", DriverObject, Pdo); /* Serial.sys is a legacy driver. AddDevice is called once * with a NULL Pdo just after the driver initialization. * Detect this case and return success. */ if (Pdo == NULL) return STATUS_SUCCESS; /* We have here a PDO not null. It represents a real serial * port. So call the internal AddDevice function. */ return AddDeviceInternal(DriverObject, Pdo, NULL, NULL); } NTSTATUS NTAPI FdoCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PFDO_DEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION Stack; NTSTATUS Status = STATUS_SUCCESS; DPRINT("FdoCreate()\n"); Stack = IoGetCurrentIrpStackLocation(Irp); DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; if (Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE) { DPRINT1("Not a directory\n"); Status = STATUS_NOT_A_DIRECTORY; goto done; } DPRINT("Open LPT%lu: successful\n", DeviceExtension->LptPort); DeviceExtension->OpenCount++; done: Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS NTAPI FdoClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PFDO_DEVICE_EXTENSION pDeviceExtension; DPRINT("FdoClose()\n"); pDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; pDeviceExtension->OpenCount--; Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS NTAPI FdoCleanup(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { DPRINT("FdoCleanup()\n"); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS NTAPI FdoWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PFDO_DEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IoStack; PUCHAR Buffer; ULONG i; UCHAR PortStatus; ULONG ulCount; DPRINT("FdoWrite()\n"); DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; IoStack = IoGetCurrentIrpStackLocation(Irp); Buffer = GetUserBuffer(Irp); DPRINT("Length: %lu\n", IoStack->Parameters.Write.Length); DPRINT("Buffer: %p\n", Buffer); if (Buffer != NULL) { DPRINT("%s\n", Buffer); } for (i = 0; i < IoStack->Parameters.Write.Length; i++) { DPRINT("%lu: %c\n", i, Buffer[i]); ulCount = 0; do { KeStallExecutionProcessor(10); PortStatus = READ_PORT_UCHAR((PUCHAR)(DeviceExtension->BaseAddress + 1)); ulCount++; } while (ulCount < 500000 && !(PortStatus & LP_PBUSY)); if (ulCount == 500000) { DPRINT1("Timed out\n"); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_TIMEOUT; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_TIMEOUT; } /* Write character */ WRITE_PORT_UCHAR((PUCHAR)DeviceExtension->BaseAddress, Buffer[i]); KeStallExecutionProcessor(10); WRITE_PORT_UCHAR((PUCHAR)(DeviceExtension->BaseAddress + 2), (LP_PSELECP | LP_PINITP | LP_PSTROBE)); KeStallExecutionProcessor(10); WRITE_PORT_UCHAR((PUCHAR)(DeviceExtension->BaseAddress + 2), (LP_PSELECP | LP_PINITP)); } Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS NTAPI FdoPnp(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { ULONG MinorFunction; PIO_STACK_LOCATION Stack; ULONG_PTR Information = 0; NTSTATUS Status; DPRINT("FdoPnp()\n"); Stack = IoGetCurrentIrpStackLocation(Irp); MinorFunction = Stack->MinorFunction; switch (MinorFunction) { /* FIXME: do all these minor functions IRP_MN_QUERY_REMOVE_DEVICE 0x1 IRP_MN_REMOVE_DEVICE 0x2 { TRACE_(SERIAL, "IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n"); IoAcquireRemoveLock IoReleaseRemoveLockAndWait pass request to DeviceExtension-LowerDriver disable interface IoDeleteDevice(Fdo) and/or IoDetachDevice break; } IRP_MN_CANCEL_REMOVE_DEVICE 0x3 IRP_MN_STOP_DEVICE 0x4 IRP_MN_QUERY_STOP_DEVICE 0x5 IRP_MN_CANCEL_STOP_DEVICE 0x6 IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) 0x7 IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) 0x7 IRP_MN_QUERY_INTERFACE (optional) 0x8 IRP_MN_QUERY_CAPABILITIES (optional) 0x9 IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) 0xd IRP_MN_QUERY_PNP_DEVICE_STATE (optional) 0x14 IRP_MN_DEVICE_USAGE_NOTIFICATION (required or optional) 0x16 IRP_MN_SURPRISE_REMOVAL 0x17 */ case IRP_MN_START_DEVICE: /* 0x0 */ DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n"); ASSERT(((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Common.PnpState == dsStopped); /* Call lower driver */ Status = ForwardIrpAndWait(DeviceObject, Irp); if (NT_SUCCESS(Status)) { Status = FdoStartDevice(DeviceObject, Stack->Parameters.StartDevice.AllocatedResources, Stack->Parameters.StartDevice.AllocatedResourcesTranslated); } break; case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x7 */ switch (Stack->Parameters.QueryDeviceRelations.Type) { case BusRelations: DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n"); return ForwardIrpAndForget(DeviceObject, Irp); case RemovalRelations: DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n"); return ForwardIrpAndForget(DeviceObject, Irp); default: DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n", Stack->Parameters.QueryDeviceRelations.Type); return ForwardIrpAndForget(DeviceObject, Irp); } break; case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0xd */ DPRINT1("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n"); return ForwardIrpAndForget(DeviceObject, Irp); default: DPRINT1("Unknown minor function 0x%x\n", MinorFunction); return ForwardIrpAndForget(DeviceObject, Irp); } Irp->IoStatus.Information = Information; Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } NTSTATUS NTAPI FdoPower(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { DPRINT("FdoPower()\n"); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } /* EOF */