2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: Parallel Port Function Driver
4 * FILE: drivers/parallel/parport/fdo.c
5 * PURPOSE: FDO functions
11 * The following constants describe the various signals of the printer port
12 * hardware. Note that the hardware inverts some signals and that some
13 * signals are active low. An example is LP_STROBE, which must be programmed
14 * with 1 for being active and 0 for being inactive, because the strobe signal
15 * gets inverted, but it is also active low.
19 * bit defines for 8255 status port
21 * accessed with LP_S(minor), which gets the byte...
23 #define LP_PBUSY 0x80 /* inverted input, active high */
24 #define LP_PACK 0x40 /* unchanged input, active low */
25 #define LP_POUTPA 0x20 /* unchanged input, active high */
26 #define LP_PSELECD 0x10 /* unchanged input, active high */
27 #define LP_PERRORP 0x08 /* unchanged input, active low */
30 * defines for 8255 control port
32 * accessed with LP_C(minor)
34 #define LP_PINTEN 0x10
35 #define LP_PSELECP 0x08 /* inverted output, active low */
36 #define LP_PINITP 0x04 /* unchanged output, active low */
37 #define LP_PAUTOLF 0x02 /* inverted output, active low */
38 #define LP_PSTROBE 0x01 /* inverted output, active low */
41 /* FUNCTIONS ****************************************************************/
45 AddDeviceInternal(IN PDRIVER_OBJECT DriverObject
,
46 IN PDEVICE_OBJECT Pdo
,
47 IN PULONG pLptPortNumber OPTIONAL
,
48 OUT PDEVICE_OBJECT
* pFdo OPTIONAL
)
50 PFDO_DEVICE_EXTENSION DeviceExtension
= NULL
;
51 PDEVICE_OBJECT Fdo
= NULL
;
52 WCHAR DeviceNameBuffer
[32];
53 UNICODE_STRING DeviceName
;
56 DPRINT("AddDeviceInternal()\n");
61 /* Create new device object */
62 swprintf(DeviceNameBuffer
,
63 L
"\\Device\\ParallelPort%lu",
64 IoGetConfigurationInformation()->ParallelCount
);
65 RtlInitUnicodeString(&DeviceName
,
68 Status
= IoCreateDevice(DriverObject
,
69 sizeof(FDO_DEVICE_EXTENSION
),
71 FILE_DEVICE_PARALLEL_PORT
,
72 FILE_DEVICE_SECURE_OPEN
,
75 if (!NT_SUCCESS(Status
))
77 DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status
);
82 DeviceExtension
= (PFDO_DEVICE_EXTENSION
)Fdo
->DeviceExtension
;
83 RtlZeroMemory(DeviceExtension
,
84 sizeof(FDO_DEVICE_EXTENSION
));
86 DeviceExtension
->Common
.IsFDO
= TRUE
;
87 DeviceExtension
->Common
.PnpState
= dsStopped
;
89 DeviceExtension
->ParallelPortNumber
= IoGetConfigurationInformation()->ParallelCount
++;
90 if (pLptPortNumber
== NULL
)
91 DeviceExtension
->LptPort
= DeviceExtension
->ParallelPortNumber
+ 1;
93 DeviceExtension
->LptPort
= *pLptPortNumber
;
94 DeviceExtension
->Pdo
= Pdo
;
96 Status
= IoAttachDeviceToDeviceStackSafe(Fdo
,
98 &DeviceExtension
->LowerDevice
);
99 if (!NT_SUCCESS(Status
))
101 DPRINT1("IoAttachDeviceToDeviceStackSafe() failed (Status 0x%08lx)\n", Status
);
105 if (DeviceExtension
->LowerDevice
->Flags
& DO_POWER_PAGABLE
)
106 Fdo
->Flags
|= DO_POWER_PAGABLE
;
108 if (DeviceExtension
->LowerDevice
->Flags
& DO_BUFFERED_IO
)
109 Fdo
->Flags
|= DO_BUFFERED_IO
;
111 if (DeviceExtension
->LowerDevice
->Flags
& DO_DIRECT_IO
)
112 Fdo
->Flags
|= DO_DIRECT_IO
;
114 /* Choose default strategy */
115 if ((Fdo
->Flags
& (DO_BUFFERED_IO
| DO_DIRECT_IO
)) == 0)
116 Fdo
->Flags
|= DO_BUFFERED_IO
;
118 Fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
125 return STATUS_SUCCESS
;
139 FdoStartDevice(IN PDEVICE_OBJECT DeviceObject
,
140 IN PCM_RESOURCE_LIST ResourceList
,
141 IN PCM_RESOURCE_LIST ResourceListTranslated
)
143 PFDO_DEVICE_EXTENSION DeviceExtension
;
144 WCHAR DeviceNameBuffer
[32];
145 WCHAR LinkNameBuffer
[32];
146 WCHAR LptPortBuffer
[32];
147 UNICODE_STRING DeviceName
;
148 UNICODE_STRING LinkName
;
149 UNICODE_STRING LptPort
;
153 // KAFFINITY Affinity = 0;
154 // KINTERRUPT_MODE InterruptMode = Latched;
155 // BOOLEAN ShareInterrupt = TRUE;
156 OBJECT_ATTRIBUTES ObjectAttributes
;
157 UNICODE_STRING KeyName
;
161 DPRINT("FdoStartDevice ()\n");
163 DeviceExtension
= (PFDO_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
165 ASSERT(DeviceExtension
);
166 ASSERT(DeviceExtension
->Common
.IsFDO
== TRUE
);
170 DPRINT1("No allocated resources sent to driver\n");
171 return STATUS_INSUFFICIENT_RESOURCES
;
174 if (ResourceList
->Count
!= 1)
176 DPRINT1("Wrong number of allocated resources sent to driver\n");
177 return STATUS_INSUFFICIENT_RESOURCES
;
180 if ((ResourceList
->List
[0].PartialResourceList
.Version
!= 1) ||
181 (ResourceList
->List
[0].PartialResourceList
.Revision
!= 1) ||
182 (ResourceListTranslated
->List
[0].PartialResourceList
.Version
!= 1) ||
183 (ResourceListTranslated
->List
[0].PartialResourceList
.Revision
!= 1))
185 DPRINT1("Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n",
186 ResourceList
->List
[0].PartialResourceList
.Version
,
187 ResourceList
->List
[0].PartialResourceList
.Revision
,
188 ResourceListTranslated
->List
[0].PartialResourceList
.Version
,
189 ResourceListTranslated
->List
[0].PartialResourceList
.Revision
);
190 return STATUS_REVISION_MISMATCH
;
193 DeviceExtension
->BaseAddress
= 0;
195 for (i
= 0; i
< ResourceList
->List
[0].PartialResourceList
.Count
; i
++)
197 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor
= &ResourceList
->List
[0].PartialResourceList
.PartialDescriptors
[i
];
198 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptorTranslated
= &ResourceListTranslated
->List
[0].PartialResourceList
.PartialDescriptors
[i
];
200 switch (PartialDescriptor
->Type
)
202 case CmResourceTypePort
:
203 DPRINT("Port: BaseAddress 0x%lx Length %lu\n",
204 PartialDescriptor
->u
.Port
.Start
.u
.LowPart
,
205 PartialDescriptor
->u
.Port
.Length
);
207 if (DeviceExtension
->BaseAddress
== 0)
209 if (PartialDescriptor
->u
.Port
.Length
< 8)
210 return STATUS_INSUFFICIENT_RESOURCES
;
212 DeviceExtension
->BaseAddress
= PartialDescriptor
->u
.Port
.Start
.u
.LowPart
;
216 case CmResourceTypeInterrupt
:
217 DPRINT("Interrupt: Level %lu Vector %lu\n",
218 PartialDescriptorTranslated
->u
.Interrupt
.Level
,
219 PartialDescriptorTranslated
->u
.Interrupt
.Vector
);
221 // Dirql = (KIRQL)PartialDescriptorTranslated->u.Interrupt.Level;
222 // Vector = PartialDescriptorTranslated->u.Interrupt.Vector;
223 // Affinity = PartialDescriptorTranslated->u.Interrupt.Affinity;
225 // if (PartialDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
226 // InterruptMode = Latched;
228 // InterruptMode = LevelSensitive;
230 // ShareInterrupt = (PartialDescriptorTranslated->ShareDisposition == CmResourceShareShared);
234 DPRINT1("Other ressource: \n");
239 DPRINT("New LPT port: Base 0x%lx\n",
240 DeviceExtension
->BaseAddress
);
242 if (!DeviceExtension
->BaseAddress
)
243 return STATUS_INSUFFICIENT_RESOURCES
;
247 return STATUS_INSUFFICIENT_RESOURCES
;
250 /* Create link \DosDevices\LPTX -> \Device\ParallelPortX */
251 swprintf(DeviceNameBuffer
, L
"\\Device\\ParallelPort%lu", DeviceExtension
->ParallelPortNumber
);
252 swprintf(LinkNameBuffer
, L
"\\DosDevices\\LPT%lu", DeviceExtension
->LptPort
);
253 swprintf(LptPortBuffer
, L
"LPT%lu", DeviceExtension
->LptPort
);
254 RtlInitUnicodeString(&DeviceName
, DeviceNameBuffer
);
255 RtlInitUnicodeString(&LinkName
, LinkNameBuffer
);
256 RtlInitUnicodeString(&LptPort
, LptPortBuffer
);
257 Status
= IoCreateSymbolicLink(&LinkName
,
259 if (!NT_SUCCESS(Status
))
261 DPRINT1("IoCreateSymbolicLink() failed with status 0x%08x\n", Status
);
266 /* Write an entry value under HKLM\HARDWARE\DeviceMap\PARALLEL PORTS. */
267 /* This step is not mandatory, so do not exit in case of error. */
268 RtlInitUnicodeString(&KeyName
,
269 L
"\\Registry\\Machine\\HARDWARE\\DeviceMap\\PARALLEL PORTS");
270 InitializeObjectAttributes(&ObjectAttributes
,
272 OBJ_CASE_INSENSITIVE
,
276 Status
= ZwCreateKey(&KeyHandle
,
283 if (NT_SUCCESS(Status
))
285 /* Key = \Device\Parallelx, Value = LPTx */
286 ZwSetValueKey(KeyHandle
,
291 LptPort
.Length
+ sizeof(WCHAR
));
295 DeviceExtension
->Common
.PnpState
= dsStarted
;
298 /* We don't really care if the call succeeded or not... */
300 return STATUS_SUCCESS
;
304 /* PUBLIC FUNCTIONS *********************************************************/
308 AddDevice(IN PDRIVER_OBJECT DriverObject
,
309 IN PDEVICE_OBJECT Pdo
)
311 DPRINT("AddDevice(%p %p)\n", DriverObject
, Pdo
);
313 /* Serial.sys is a legacy driver. AddDevice is called once
314 * with a NULL Pdo just after the driver initialization.
315 * Detect this case and return success.
318 return STATUS_SUCCESS
;
320 /* We have here a PDO not null. It represents a real serial
321 * port. So call the internal AddDevice function.
323 return AddDeviceInternal(DriverObject
, Pdo
, NULL
, NULL
);
329 FdoCreate(IN PDEVICE_OBJECT DeviceObject
,
332 PFDO_DEVICE_EXTENSION DeviceExtension
;
333 PIO_STACK_LOCATION Stack
;
334 NTSTATUS Status
= STATUS_SUCCESS
;
336 DPRINT("FdoCreate()\n");
338 Stack
= IoGetCurrentIrpStackLocation(Irp
);
339 DeviceExtension
= (PFDO_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
341 if (Stack
->Parameters
.Create
.Options
& FILE_DIRECTORY_FILE
)
343 DPRINT1("Not a directory\n");
344 Status
= STATUS_NOT_A_DIRECTORY
;
348 DPRINT("Open LPT%lu: successful\n", DeviceExtension
->LptPort
);
349 DeviceExtension
->OpenCount
++;
352 Irp
->IoStatus
.Status
= Status
;
353 Irp
->IoStatus
.Information
= 0;
354 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
362 FdoClose(IN PDEVICE_OBJECT DeviceObject
,
365 PFDO_DEVICE_EXTENSION pDeviceExtension
;
367 DPRINT("FdoClose()\n");
369 pDeviceExtension
= (PFDO_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
370 pDeviceExtension
->OpenCount
--;
372 Irp
->IoStatus
.Information
= 0;
373 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
374 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
376 return STATUS_SUCCESS
;
382 FdoCleanup(IN PDEVICE_OBJECT DeviceObject
,
385 DPRINT("FdoCleanup()\n");
387 Irp
->IoStatus
.Information
= 0;
388 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
389 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
391 return STATUS_SUCCESS
;
397 FdoWrite(IN PDEVICE_OBJECT DeviceObject
,
400 PFDO_DEVICE_EXTENSION DeviceExtension
;
401 PIO_STACK_LOCATION IoStack
;
407 DPRINT("FdoWrite()\n");
409 DeviceExtension
= (PFDO_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
410 IoStack
= IoGetCurrentIrpStackLocation(Irp
);
412 Buffer
= GetUserBuffer(Irp
);
413 DPRINT("Length: %lu\n", IoStack
->Parameters
.Write
.Length
);
414 DPRINT("Buffer: %p\n", Buffer
);
418 DPRINT("%s\n", Buffer
);
421 for (i
= 0; i
< IoStack
->Parameters
.Write
.Length
; i
++)
423 DPRINT("%lu: %c\n", i
, Buffer
[i
]);
429 KeStallExecutionProcessor(10);
430 PortStatus
= READ_PORT_UCHAR((PUCHAR
)(DeviceExtension
->BaseAddress
+ 1));
433 while (ulCount
< 500000 && !(PortStatus
& LP_PBUSY
));
435 if (ulCount
== 500000)
437 DPRINT1("Timed out\n");
439 Irp
->IoStatus
.Information
= 0;
440 Irp
->IoStatus
.Status
= STATUS_TIMEOUT
;
441 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
443 return STATUS_TIMEOUT
;
446 /* Write character */
447 WRITE_PORT_UCHAR((PUCHAR
)DeviceExtension
->BaseAddress
, Buffer
[i
]);
449 KeStallExecutionProcessor(10);
451 WRITE_PORT_UCHAR((PUCHAR
)(DeviceExtension
->BaseAddress
+ 2), (LP_PSELECP
| LP_PINITP
| LP_PSTROBE
));
453 KeStallExecutionProcessor(10);
455 WRITE_PORT_UCHAR((PUCHAR
)(DeviceExtension
->BaseAddress
+ 2), (LP_PSELECP
| LP_PINITP
));
458 Irp
->IoStatus
.Information
= 0;
459 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
460 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
462 return STATUS_SUCCESS
;
468 FdoPnp(IN PDEVICE_OBJECT DeviceObject
,
472 PIO_STACK_LOCATION Stack
;
473 ULONG_PTR Information
= 0;
476 DPRINT("FdoPnp()\n");
478 Stack
= IoGetCurrentIrpStackLocation(Irp
);
479 MinorFunction
= Stack
->MinorFunction
;
481 switch (MinorFunction
)
483 /* FIXME: do all these minor functions
484 IRP_MN_QUERY_REMOVE_DEVICE 0x1
485 IRP_MN_REMOVE_DEVICE 0x2
487 TRACE_(SERIAL, "IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
489 IoReleaseRemoveLockAndWait
490 pass request to DeviceExtension-LowerDriver
492 IoDeleteDevice(Fdo) and/or IoDetachDevice
495 IRP_MN_CANCEL_REMOVE_DEVICE 0x3
496 IRP_MN_STOP_DEVICE 0x4
497 IRP_MN_QUERY_STOP_DEVICE 0x5
498 IRP_MN_CANCEL_STOP_DEVICE 0x6
499 IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) 0x7
500 IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) 0x7
501 IRP_MN_QUERY_INTERFACE (optional) 0x8
502 IRP_MN_QUERY_CAPABILITIES (optional) 0x9
503 IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) 0xd
504 IRP_MN_QUERY_PNP_DEVICE_STATE (optional) 0x14
505 IRP_MN_DEVICE_USAGE_NOTIFICATION (required or optional) 0x16
506 IRP_MN_SURPRISE_REMOVAL 0x17
508 case IRP_MN_START_DEVICE
: /* 0x0 */
509 DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
511 ASSERT(((PFDO_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
)->Common
.PnpState
== dsStopped
);
513 /* Call lower driver */
514 Status
= ForwardIrpAndWait(DeviceObject
, Irp
);
515 if (NT_SUCCESS(Status
))
517 Status
= FdoStartDevice(DeviceObject
,
518 Stack
->Parameters
.StartDevice
.AllocatedResources
,
519 Stack
->Parameters
.StartDevice
.AllocatedResourcesTranslated
);
523 case IRP_MN_QUERY_DEVICE_RELATIONS
: /* (optional) 0x7 */
524 switch (Stack
->Parameters
.QueryDeviceRelations
.Type
)
527 DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
528 return ForwardIrpAndForget(DeviceObject
, Irp
);
530 case RemovalRelations
:
531 DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
532 return ForwardIrpAndForget(DeviceObject
, Irp
);
535 DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
536 Stack
->Parameters
.QueryDeviceRelations
.Type
);
537 return ForwardIrpAndForget(DeviceObject
, Irp
);
541 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS
: /* (optional) 0xd */
542 DPRINT1("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
543 return ForwardIrpAndForget(DeviceObject
, Irp
);
546 DPRINT1("Unknown minor function 0x%x\n", MinorFunction
);
547 return ForwardIrpAndForget(DeviceObject
, Irp
);
550 Irp
->IoStatus
.Information
= Information
;
551 Irp
->IoStatus
.Status
= Status
;
552 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
560 FdoPower(IN PDEVICE_OBJECT DeviceObject
,
563 DPRINT("FdoPower()\n");
565 Irp
->IoStatus
.Information
= 0;
566 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
567 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
569 return STATUS_SUCCESS
;