[PARPORT]
[reactos.git] / reactos / drivers / parallel / parport / fdo.c
1 /*
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
6 */
7
8 #include "parport.h"
9
10 /*
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.
16 */
17
18 /*
19 * bit defines for 8255 status port
20 * base + 1
21 * accessed with LP_S(minor), which gets the byte...
22 */
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 */
28
29 /*
30 * defines for 8255 control port
31 * base + 2
32 * accessed with LP_C(minor)
33 */
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 */
39
40
41 /* FUNCTIONS ****************************************************************/
42
43 NTSTATUS
44 NTAPI
45 AddDeviceInternal(IN PDRIVER_OBJECT DriverObject,
46 IN PDEVICE_OBJECT Pdo,
47 IN PULONG pLptPortNumber OPTIONAL,
48 OUT PDEVICE_OBJECT* pFdo OPTIONAL)
49 {
50 PFDO_DEVICE_EXTENSION DeviceExtension = NULL;
51 PDEVICE_OBJECT Fdo = NULL;
52 WCHAR DeviceNameBuffer[32];
53 UNICODE_STRING DeviceName;
54 NTSTATUS Status;
55
56 DPRINT("AddDeviceInternal()\n");
57
58 ASSERT(DriverObject);
59 ASSERT(Pdo);
60
61 /* Create new device object */
62 swprintf(DeviceNameBuffer,
63 L"\\Device\\ParallelPort%lu",
64 IoGetConfigurationInformation()->ParallelCount);
65 RtlInitUnicodeString(&DeviceName,
66 DeviceNameBuffer);
67
68 Status = IoCreateDevice(DriverObject,
69 sizeof(FDO_DEVICE_EXTENSION),
70 &DeviceName,
71 FILE_DEVICE_PARALLEL_PORT,
72 FILE_DEVICE_SECURE_OPEN,
73 FALSE,
74 &Fdo);
75 if (!NT_SUCCESS(Status))
76 {
77 DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status);
78 Fdo = NULL;
79 goto done;
80 }
81
82 DeviceExtension = (PFDO_DEVICE_EXTENSION)Fdo->DeviceExtension;
83 RtlZeroMemory(DeviceExtension,
84 sizeof(FDO_DEVICE_EXTENSION));
85
86 DeviceExtension->Common.IsFDO = TRUE;
87 DeviceExtension->Common.PnpState = dsStopped;
88
89 DeviceExtension->ParallelPortNumber = IoGetConfigurationInformation()->ParallelCount++;
90 if (pLptPortNumber == NULL)
91 DeviceExtension->LptPort = DeviceExtension->ParallelPortNumber + 1;
92 else
93 DeviceExtension->LptPort = *pLptPortNumber;
94 DeviceExtension->Pdo = Pdo;
95
96 Status = IoAttachDeviceToDeviceStackSafe(Fdo,
97 Pdo,
98 &DeviceExtension->LowerDevice);
99 if (!NT_SUCCESS(Status))
100 {
101 DPRINT1("IoAttachDeviceToDeviceStackSafe() failed (Status 0x%08lx)\n", Status);
102 goto done;
103 }
104
105 if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
106 Fdo->Flags |= DO_POWER_PAGABLE;
107
108 if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
109 Fdo->Flags |= DO_BUFFERED_IO;
110
111 if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO)
112 Fdo->Flags |= DO_DIRECT_IO;
113
114 /* Choose default strategy */
115 if ((Fdo->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)) == 0)
116 Fdo->Flags |= DO_BUFFERED_IO;
117
118 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
119
120 if (pFdo)
121 {
122 *pFdo = Fdo;
123 }
124
125 return STATUS_SUCCESS;
126
127 done:
128 if (Fdo)
129 {
130 IoDeleteDevice(Fdo);
131 }
132
133 return Status;
134 }
135
136
137 NTSTATUS
138 NTAPI
139 FdoStartDevice(IN PDEVICE_OBJECT DeviceObject,
140 IN PCM_RESOURCE_LIST ResourceList,
141 IN PCM_RESOURCE_LIST ResourceListTranslated)
142 {
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;
150 ULONG i;
151 // ULONG Vector = 0;
152 // KIRQL Dirql = 0;
153 // KAFFINITY Affinity = 0;
154 // KINTERRUPT_MODE InterruptMode = Latched;
155 // BOOLEAN ShareInterrupt = TRUE;
156 OBJECT_ATTRIBUTES ObjectAttributes;
157 UNICODE_STRING KeyName;
158 HANDLE KeyHandle;
159 NTSTATUS Status;
160
161 DPRINT("FdoStartDevice ()\n");
162
163 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
164
165 ASSERT(DeviceExtension);
166 ASSERT(DeviceExtension->Common.IsFDO == TRUE);
167
168 if (!ResourceList)
169 {
170 DPRINT1("No allocated resources sent to driver\n");
171 return STATUS_INSUFFICIENT_RESOURCES;
172 }
173
174 if (ResourceList->Count != 1)
175 {
176 DPRINT1("Wrong number of allocated resources sent to driver\n");
177 return STATUS_INSUFFICIENT_RESOURCES;
178 }
179
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))
184 {
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;
191 }
192
193 DeviceExtension->BaseAddress = 0;
194
195 for (i = 0; i < ResourceList->List[0].PartialResourceList.Count; i++)
196 {
197 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[i];
198 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptorTranslated = &ResourceListTranslated->List[0].PartialResourceList.PartialDescriptors[i];
199
200 switch (PartialDescriptor->Type)
201 {
202 case CmResourceTypePort:
203 DPRINT("Port: BaseAddress 0x%lx Length %lu\n",
204 PartialDescriptor->u.Port.Start.u.LowPart,
205 PartialDescriptor->u.Port.Length);
206
207 if (DeviceExtension->BaseAddress == 0)
208 {
209 if (PartialDescriptor->u.Port.Length < 8)
210 return STATUS_INSUFFICIENT_RESOURCES;
211
212 DeviceExtension->BaseAddress = PartialDescriptor->u.Port.Start.u.LowPart;
213 }
214 break;
215
216 case CmResourceTypeInterrupt:
217 DPRINT("Interrupt: Level %lu Vector %lu\n",
218 PartialDescriptorTranslated->u.Interrupt.Level,
219 PartialDescriptorTranslated->u.Interrupt.Vector);
220
221 // Dirql = (KIRQL)PartialDescriptorTranslated->u.Interrupt.Level;
222 // Vector = PartialDescriptorTranslated->u.Interrupt.Vector;
223 // Affinity = PartialDescriptorTranslated->u.Interrupt.Affinity;
224
225 // if (PartialDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
226 // InterruptMode = Latched;
227 // else
228 // InterruptMode = LevelSensitive;
229
230 // ShareInterrupt = (PartialDescriptorTranslated->ShareDisposition == CmResourceShareShared);
231 break;
232
233 default:
234 DPRINT1("Other ressource: \n");
235 break;
236 }
237 }
238
239 DPRINT("New LPT port: Base 0x%lx\n",
240 DeviceExtension->BaseAddress);
241
242 if (!DeviceExtension->BaseAddress)
243 return STATUS_INSUFFICIENT_RESOURCES;
244
245 #if 0
246 if (!Dirql)
247 return STATUS_INSUFFICIENT_RESOURCES;
248 #endif
249
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,
258 &DeviceName);
259 if (!NT_SUCCESS(Status))
260 {
261 DPRINT1("IoCreateSymbolicLink() failed with status 0x%08x\n", Status);
262 return Status;
263 }
264
265
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,
271 &KeyName,
272 OBJ_CASE_INSENSITIVE,
273 NULL,
274 NULL);
275
276 Status = ZwCreateKey(&KeyHandle,
277 KEY_SET_VALUE,
278 &ObjectAttributes,
279 0,
280 NULL,
281 REG_OPTION_VOLATILE,
282 NULL);
283 if (NT_SUCCESS(Status))
284 {
285 /* Key = \Device\Parallelx, Value = LPTx */
286 ZwSetValueKey(KeyHandle,
287 &DeviceName,
288 0,
289 REG_SZ,
290 LptPortBuffer,
291 LptPort.Length + sizeof(WCHAR));
292 ZwClose(KeyHandle);
293 }
294
295 DeviceExtension->Common.PnpState = dsStarted;
296
297
298 /* We don't really care if the call succeeded or not... */
299
300 return STATUS_SUCCESS;
301 }
302
303
304 /* PUBLIC FUNCTIONS *********************************************************/
305
306 NTSTATUS
307 NTAPI
308 AddDevice(IN PDRIVER_OBJECT DriverObject,
309 IN PDEVICE_OBJECT Pdo)
310 {
311 DPRINT("AddDevice(%p %p)\n", DriverObject, Pdo);
312
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.
316 */
317 if (Pdo == NULL)
318 return STATUS_SUCCESS;
319
320 /* We have here a PDO not null. It represents a real serial
321 * port. So call the internal AddDevice function.
322 */
323 return AddDeviceInternal(DriverObject, Pdo, NULL, NULL);
324 }
325
326
327 NTSTATUS
328 NTAPI
329 FdoCreate(IN PDEVICE_OBJECT DeviceObject,
330 IN PIRP Irp)
331 {
332 PFDO_DEVICE_EXTENSION DeviceExtension;
333 PIO_STACK_LOCATION Stack;
334 NTSTATUS Status = STATUS_SUCCESS;
335
336 DPRINT("FdoCreate()\n");
337
338 Stack = IoGetCurrentIrpStackLocation(Irp);
339 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
340
341 if (Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
342 {
343 DPRINT1("Not a directory\n");
344 Status = STATUS_NOT_A_DIRECTORY;
345 goto done;
346 }
347
348 DPRINT("Open LPT%lu: successful\n", DeviceExtension->LptPort);
349 DeviceExtension->OpenCount++;
350
351 done:
352 Irp->IoStatus.Status = Status;
353 Irp->IoStatus.Information = 0;
354 IoCompleteRequest(Irp, IO_NO_INCREMENT);
355
356 return Status;
357 }
358
359
360 NTSTATUS
361 NTAPI
362 FdoClose(IN PDEVICE_OBJECT DeviceObject,
363 IN PIRP Irp)
364 {
365 PFDO_DEVICE_EXTENSION pDeviceExtension;
366
367 DPRINT("FdoClose()\n");
368
369 pDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
370 pDeviceExtension->OpenCount--;
371
372 Irp->IoStatus.Information = 0;
373 Irp->IoStatus.Status = STATUS_SUCCESS;
374 IoCompleteRequest(Irp, IO_NO_INCREMENT);
375
376 return STATUS_SUCCESS;
377 }
378
379
380 NTSTATUS
381 NTAPI
382 FdoCleanup(IN PDEVICE_OBJECT DeviceObject,
383 IN PIRP Irp)
384 {
385 DPRINT("FdoCleanup()\n");
386
387 Irp->IoStatus.Information = 0;
388 Irp->IoStatus.Status = STATUS_SUCCESS;
389 IoCompleteRequest(Irp, IO_NO_INCREMENT);
390
391 return STATUS_SUCCESS;
392 }
393
394
395 NTSTATUS
396 NTAPI
397 FdoWrite(IN PDEVICE_OBJECT DeviceObject,
398 IN PIRP Irp)
399 {
400 PFDO_DEVICE_EXTENSION DeviceExtension;
401 PIO_STACK_LOCATION IoStack;
402 PUCHAR Buffer;
403 ULONG i;
404 UCHAR PortStatus;
405 ULONG ulCount;
406
407 DPRINT("FdoWrite()\n");
408
409 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
410 IoStack = IoGetCurrentIrpStackLocation(Irp);
411
412 Buffer = GetUserBuffer(Irp);
413 DPRINT("Length: %lu\n", IoStack->Parameters.Write.Length);
414 DPRINT("Buffer: %p\n", Buffer);
415
416 if (Buffer != NULL)
417 {
418 DPRINT("%s\n", Buffer);
419 }
420
421 for (i = 0; i < IoStack->Parameters.Write.Length; i++)
422 {
423 DPRINT("%lu: %c\n", i, Buffer[i]);
424
425 ulCount = 0;
426
427 do
428 {
429 KeStallExecutionProcessor(10);
430 PortStatus = READ_PORT_UCHAR((PUCHAR)(DeviceExtension->BaseAddress + 1));
431 ulCount++;
432 }
433 while (ulCount < 500000 && !(PortStatus & LP_PBUSY));
434
435 if (ulCount == 500000)
436 {
437 DPRINT1("Timed out\n");
438
439 Irp->IoStatus.Information = 0;
440 Irp->IoStatus.Status = STATUS_TIMEOUT;
441 IoCompleteRequest(Irp, IO_NO_INCREMENT);
442
443 return STATUS_TIMEOUT;
444 }
445
446 /* Write character */
447 WRITE_PORT_UCHAR((PUCHAR)DeviceExtension->BaseAddress, Buffer[i]);
448
449 KeStallExecutionProcessor(10);
450
451 WRITE_PORT_UCHAR((PUCHAR)(DeviceExtension->BaseAddress + 2), (LP_PSELECP | LP_PINITP | LP_PSTROBE));
452
453 KeStallExecutionProcessor(10);
454
455 WRITE_PORT_UCHAR((PUCHAR)(DeviceExtension->BaseAddress + 2), (LP_PSELECP | LP_PINITP));
456 }
457
458 Irp->IoStatus.Information = 0;
459 Irp->IoStatus.Status = STATUS_SUCCESS;
460 IoCompleteRequest(Irp, IO_NO_INCREMENT);
461
462 return STATUS_SUCCESS;
463 }
464
465
466 NTSTATUS
467 NTAPI
468 FdoPnp(IN PDEVICE_OBJECT DeviceObject,
469 IN PIRP Irp)
470 {
471 ULONG MinorFunction;
472 PIO_STACK_LOCATION Stack;
473 ULONG_PTR Information = 0;
474 NTSTATUS Status;
475
476 DPRINT("FdoPnp()\n");
477
478 Stack = IoGetCurrentIrpStackLocation(Irp);
479 MinorFunction = Stack->MinorFunction;
480
481 switch (MinorFunction)
482 {
483 /* FIXME: do all these minor functions
484 IRP_MN_QUERY_REMOVE_DEVICE 0x1
485 IRP_MN_REMOVE_DEVICE 0x2
486 {
487 TRACE_(SERIAL, "IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
488 IoAcquireRemoveLock
489 IoReleaseRemoveLockAndWait
490 pass request to DeviceExtension-LowerDriver
491 disable interface
492 IoDeleteDevice(Fdo) and/or IoDetachDevice
493 break;
494 }
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
507 */
508 case IRP_MN_START_DEVICE: /* 0x0 */
509 DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
510
511 ASSERT(((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Common.PnpState == dsStopped);
512
513 /* Call lower driver */
514 Status = ForwardIrpAndWait(DeviceObject, Irp);
515 if (NT_SUCCESS(Status))
516 {
517 Status = FdoStartDevice(DeviceObject,
518 Stack->Parameters.StartDevice.AllocatedResources,
519 Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
520 }
521 break;
522
523 case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x7 */
524 switch (Stack->Parameters.QueryDeviceRelations.Type)
525 {
526 case BusRelations:
527 DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
528 return ForwardIrpAndForget(DeviceObject, Irp);
529
530 case RemovalRelations:
531 DPRINT1("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
532 return ForwardIrpAndForget(DeviceObject, Irp);
533
534 default:
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);
538 }
539 break;
540
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);
544
545 default:
546 DPRINT1("Unknown minor function 0x%x\n", MinorFunction);
547 return ForwardIrpAndForget(DeviceObject, Irp);
548 }
549
550 Irp->IoStatus.Information = Information;
551 Irp->IoStatus.Status = Status;
552 IoCompleteRequest(Irp, IO_NO_INCREMENT);
553
554 return Status;
555 }
556
557
558 NTSTATUS
559 NTAPI
560 FdoPower(IN PDEVICE_OBJECT DeviceObject,
561 IN PIRP Irp)
562 {
563 DPRINT("FdoPower()\n");
564
565 Irp->IoStatus.Information = 0;
566 Irp->IoStatus.Status = STATUS_SUCCESS;
567 IoCompleteRequest(Irp, IO_NO_INCREMENT);
568
569 return STATUS_SUCCESS;
570 }
571
572 /* EOF */