[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 /* FUNCTIONS ****************************************************************/
11
12 NTSTATUS
13 NTAPI
14 AddDeviceInternal(IN PDRIVER_OBJECT DriverObject,
15 IN PDEVICE_OBJECT Pdo,
16 IN PULONG pLptPortNumber OPTIONAL,
17 OUT PDEVICE_OBJECT* pFdo OPTIONAL)
18 {
19 PFDO_DEVICE_EXTENSION DeviceExtension = NULL;
20 PDEVICE_OBJECT Fdo = NULL;
21 WCHAR DeviceNameBuffer[32];
22 UNICODE_STRING DeviceName;
23 NTSTATUS Status;
24
25 DPRINT("AddDeviceInternal()\n");
26
27 ASSERT(DriverObject);
28 ASSERT(Pdo);
29
30 /* Create new device object */
31 swprintf(DeviceNameBuffer,
32 L"\\Device\\ParallelPort%lu",
33 IoGetConfigurationInformation()->ParallelCount);
34 RtlInitUnicodeString(&DeviceName,
35 DeviceNameBuffer);
36
37 Status = IoCreateDevice(DriverObject,
38 sizeof(FDO_DEVICE_EXTENSION),
39 &DeviceName,
40 FILE_DEVICE_PARALLEL_PORT,
41 FILE_DEVICE_SECURE_OPEN,
42 FALSE,
43 &Fdo);
44 if (!NT_SUCCESS(Status))
45 {
46 DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status);
47 Fdo = NULL;
48 goto done;
49 }
50
51 DeviceExtension = (PFDO_DEVICE_EXTENSION)Fdo->DeviceExtension;
52 RtlZeroMemory(DeviceExtension,
53 sizeof(FDO_DEVICE_EXTENSION));
54
55 DeviceExtension->Common.IsFDO = TRUE;
56 DeviceExtension->Common.PnpState = dsStopped;
57
58 DeviceExtension->PortNumber = IoGetConfigurationInformation()->ParallelCount++;
59 DeviceExtension->Pdo = Pdo;
60
61 Status = IoAttachDeviceToDeviceStackSafe(Fdo,
62 Pdo,
63 &DeviceExtension->LowerDevice);
64 if (!NT_SUCCESS(Status))
65 {
66 DPRINT1("IoAttachDeviceToDeviceStackSafe() failed (Status 0x%08lx)\n", Status);
67 goto done;
68 }
69
70 if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
71 Fdo->Flags |= DO_POWER_PAGABLE;
72
73 if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
74 Fdo->Flags |= DO_BUFFERED_IO;
75
76 if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO)
77 Fdo->Flags |= DO_DIRECT_IO;
78
79 /* Choose default strategy */
80 if ((Fdo->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)) == 0)
81 Fdo->Flags |= DO_BUFFERED_IO;
82
83 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
84
85 if (pFdo)
86 {
87 *pFdo = Fdo;
88 }
89
90 return STATUS_SUCCESS;
91
92 done:
93 if (Fdo)
94 {
95 IoDeleteDevice(Fdo);
96 }
97
98 return Status;
99 }
100
101
102 NTSTATUS
103 NTAPI
104 FdoStartDevice(IN PDEVICE_OBJECT DeviceObject,
105 IN PCM_RESOURCE_LIST ResourceList,
106 IN PCM_RESOURCE_LIST ResourceListTranslated)
107 {
108 PFDO_DEVICE_EXTENSION DeviceExtension;
109 ULONG i;
110 // ULONG Vector = 0;
111 // KIRQL Dirql = 0;
112 // KAFFINITY Affinity = 0;
113 // KINTERRUPT_MODE InterruptMode = Latched;
114 // BOOLEAN ShareInterrupt = TRUE;
115
116 DPRINT("FdoStartDevice ()\n");
117
118 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
119
120 ASSERT(DeviceExtension);
121 ASSERT(DeviceExtension->Common.IsFDO == TRUE);
122
123 if (!ResourceList)
124 {
125 DPRINT1("No allocated resources sent to driver\n");
126 return STATUS_INSUFFICIENT_RESOURCES;
127 }
128
129 if (ResourceList->Count != 1)
130 {
131 DPRINT1("Wrong number of allocated resources sent to driver\n");
132 return STATUS_INSUFFICIENT_RESOURCES;
133 }
134
135 if ((ResourceList->List[0].PartialResourceList.Version != 1) ||
136 (ResourceList->List[0].PartialResourceList.Revision != 1) ||
137 (ResourceListTranslated->List[0].PartialResourceList.Version != 1) ||
138 (ResourceListTranslated->List[0].PartialResourceList.Revision != 1))
139 {
140 DPRINT1("Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n",
141 ResourceList->List[0].PartialResourceList.Version,
142 ResourceList->List[0].PartialResourceList.Revision,
143 ResourceListTranslated->List[0].PartialResourceList.Version,
144 ResourceListTranslated->List[0].PartialResourceList.Revision);
145 return STATUS_REVISION_MISMATCH;
146 }
147
148 DeviceExtension->BaseAddress = 0;
149
150 for (i = 0; i < ResourceList->List[0].PartialResourceList.Count; i++)
151 {
152 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[i];
153 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptorTranslated = &ResourceListTranslated->List[0].PartialResourceList.PartialDescriptors[i];
154
155 switch (PartialDescriptor->Type)
156 {
157 case CmResourceTypePort:
158 DPRINT("Port: BaseAddress 0x%lx Length %lu\n",
159 PartialDescriptor->u.Port.Start.u.LowPart,
160 PartialDescriptor->u.Port.Length);
161
162 if (DeviceExtension->BaseAddress == 0)
163 {
164 if (PartialDescriptor->u.Port.Length < 4)
165 return STATUS_INSUFFICIENT_RESOURCES;
166
167 DeviceExtension->BaseAddress = PartialDescriptor->u.Port.Start.u.LowPart;
168 }
169 break;
170
171 case CmResourceTypeInterrupt:
172 DPRINT("Interrupt: Level %lu Vector %lu\n",
173 PartialDescriptorTranslated->u.Interrupt.Level,
174 PartialDescriptorTranslated->u.Interrupt.Vector);
175
176 // Dirql = (KIRQL)PartialDescriptorTranslated->u.Interrupt.Level;
177 // Vector = PartialDescriptorTranslated->u.Interrupt.Vector;
178 // Affinity = PartialDescriptorTranslated->u.Interrupt.Affinity;
179
180 // if (PartialDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
181 // InterruptMode = Latched;
182 // else
183 // InterruptMode = LevelSensitive;
184
185 // ShareInterrupt = (PartialDescriptorTranslated->ShareDisposition == CmResourceShareShared);
186 break;
187
188 default:
189 DPRINT1("Other ressource: \n");
190 break;
191 }
192 }
193
194 DPRINT("New LPT port: Base 0x%lx\n",
195 DeviceExtension->BaseAddress);
196
197 if (!DeviceExtension->BaseAddress)
198 return STATUS_INSUFFICIENT_RESOURCES;
199
200 #if 0
201 if (!Dirql)
202 return STATUS_INSUFFICIENT_RESOURCES;
203 #endif
204
205 DeviceExtension->Common.PnpState = dsStarted;
206
207
208 /* We don't really care if the call succeeded or not... */
209
210 return STATUS_SUCCESS;
211 }
212
213
214 static
215 NTSTATUS
216 FdoCreateRawParallelPdo(
217 IN PDEVICE_OBJECT DeviceObject)
218 {
219 PFDO_DEVICE_EXTENSION FdoDeviceExtension;
220 PPDO_DEVICE_EXTENSION PdoDeviceExtension = NULL;
221 PDEVICE_OBJECT Pdo = NULL;
222 WCHAR DeviceNameBuffer[32];
223 WCHAR LinkNameBuffer[32];
224 WCHAR LptPortBuffer[32];
225 UNICODE_STRING DeviceName;
226 UNICODE_STRING LinkName;
227 UNICODE_STRING LptPort;
228 OBJECT_ATTRIBUTES ObjectAttributes;
229 UNICODE_STRING KeyName;
230 HANDLE KeyHandle;
231 NTSTATUS Status;
232
233 DPRINT("FdoCreateRawParallelPdo()\n");
234
235 FdoDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
236
237 /* Create new device object */
238 swprintf(DeviceNameBuffer,
239 L"\\Device\\Parallel%lu",
240 FdoDeviceExtension->PortNumber);
241 RtlInitUnicodeString(&DeviceName,
242 DeviceNameBuffer);
243
244 Status = IoCreateDevice(DeviceObject->DriverObject,
245 sizeof(PDO_DEVICE_EXTENSION),
246 &DeviceName,
247 FILE_DEVICE_CONTROLLER,
248 0,
249 FALSE,
250 &Pdo);
251 if (!NT_SUCCESS(Status))
252 {
253 DPRINT1("IoCreateDevice() failed with status 0x%08x\n", Status);
254 goto done;
255 }
256
257 Pdo->Flags |= DO_BUS_ENUMERATED_DEVICE;
258 Pdo->Flags |= DO_POWER_PAGABLE;
259
260 PdoDeviceExtension = (PPDO_DEVICE_EXTENSION)Pdo->DeviceExtension;
261 RtlZeroMemory(PdoDeviceExtension, sizeof(PDO_DEVICE_EXTENSION));
262
263 PdoDeviceExtension->Common.IsFDO = FALSE;
264 PdoDeviceExtension->Common.PnpState = dsStopped;
265
266 Pdo->StackSize = DeviceObject->StackSize + 1;
267
268 FdoDeviceExtension->AttachedRawPdo = Pdo;
269 PdoDeviceExtension->AttachedFdo = DeviceObject;
270
271 PdoDeviceExtension->PortNumber = FdoDeviceExtension->PortNumber;
272 PdoDeviceExtension->LptPort = PdoDeviceExtension->PortNumber + 1;
273
274
275 /* Create link \DosDevices\LPTX -> \Device\ParallelY */
276 swprintf(LinkNameBuffer, L"\\DosDevices\\LPT%lu", PdoDeviceExtension->LptPort);
277 RtlInitUnicodeString(&LinkName, LinkNameBuffer);
278 Status = IoCreateSymbolicLink(&LinkName,
279 &DeviceName);
280 if (!NT_SUCCESS(Status))
281 {
282 DPRINT1("IoCreateSymbolicLink() failed with status 0x%08x\n", Status);
283 goto done;
284 }
285
286 swprintf(LptPortBuffer, L"LPT%lu", PdoDeviceExtension->LptPort);
287 RtlInitUnicodeString(&LptPort, LptPortBuffer);
288
289 /* Write an entry value under HKLM\HARDWARE\DeviceMap\PARALLEL PORTS. */
290 /* This step is not mandatory, so do not exit in case of error. */
291 RtlInitUnicodeString(&KeyName,
292 L"\\Registry\\Machine\\HARDWARE\\DeviceMap\\PARALLEL PORTS");
293 InitializeObjectAttributes(&ObjectAttributes,
294 &KeyName,
295 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
296 NULL,
297 NULL);
298
299 Status = ZwCreateKey(&KeyHandle,
300 KEY_SET_VALUE,
301 &ObjectAttributes,
302 0,
303 NULL,
304 REG_OPTION_VOLATILE,
305 NULL);
306 if (NT_SUCCESS(Status))
307 {
308 /* Key = \Device\Parallelx, Value = LPTx */
309 ZwSetValueKey(KeyHandle,
310 &DeviceName,
311 0,
312 REG_SZ,
313 LptPortBuffer,
314 LptPort.Length + sizeof(WCHAR));
315 ZwClose(KeyHandle);
316 }
317
318 Pdo->Flags |= DO_BUFFERED_IO;
319 Pdo->Flags &= ~DO_DEVICE_INITIALIZING;
320
321 done:
322 if (!NT_SUCCESS(Status))
323 {
324 if (Pdo)
325 {
326 ASSERT(PdoDeviceExtension);
327 IoDeleteDevice(Pdo);
328 }
329 }
330
331 return Status;
332 }
333
334
335 static
336 NTSTATUS
337 FdoQueryBusRelations(
338 IN PDEVICE_OBJECT DeviceObject,
339 IN PIRP Irp,
340 PIO_STACK_LOCATION IrpSp)
341 {
342 PFDO_DEVICE_EXTENSION DeviceExtension;
343 PDEVICE_RELATIONS DeviceRelations;
344 ULONG Size;
345 ULONG i;
346 ULONG PdoCount = 0;
347 NTSTATUS Status;
348
349 UNREFERENCED_PARAMETER(IrpSp);
350
351 DPRINT("FdoQueryBusRelations()\n");
352
353 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
354 ASSERT(DeviceExtension->Common.IsFDO);
355
356 /* TODO: Enumerate parallel devices and create their PDOs */
357
358 Status = FdoCreateRawParallelPdo(DeviceObject);
359 if (!NT_SUCCESS(Status))
360 return Status;
361
362 PdoCount++;
363
364 /* Allocate a buffer for the device relations */
365 Size = sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT) * (PdoCount - 1);
366 DeviceRelations = ExAllocatePoolWithTag(PagedPool, Size, PARPORT_TAG);
367 if (DeviceRelations == NULL)
368 return STATUS_INSUFFICIENT_RESOURCES;
369
370 /* Fill the buffer */
371 i = 0;
372 ObReferenceObject(DeviceExtension->AttachedRawPdo);
373 DeviceRelations->Objects[i] = DeviceExtension->AttachedRawPdo;
374
375 Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
376
377 DPRINT("Done\n");
378
379 return STATUS_SUCCESS;
380 }
381
382
383 /* PUBLIC FUNCTIONS *********************************************************/
384
385 NTSTATUS
386 NTAPI
387 AddDevice(IN PDRIVER_OBJECT DriverObject,
388 IN PDEVICE_OBJECT Pdo)
389 {
390 DPRINT("AddDevice(%p %p)\n", DriverObject, Pdo);
391
392 /* Serial.sys is a legacy driver. AddDevice is called once
393 * with a NULL Pdo just after the driver initialization.
394 * Detect this case and return success.
395 */
396 if (Pdo == NULL)
397 return STATUS_SUCCESS;
398
399 /* We have here a PDO not null. It represents a real serial
400 * port. So call the internal AddDevice function.
401 */
402 return AddDeviceInternal(DriverObject, Pdo, NULL, NULL);
403 }
404
405
406 NTSTATUS
407 NTAPI
408 FdoCreate(IN PDEVICE_OBJECT DeviceObject,
409 IN PIRP Irp)
410 {
411 PFDO_DEVICE_EXTENSION DeviceExtension;
412 PIO_STACK_LOCATION Stack;
413 NTSTATUS Status = STATUS_SUCCESS;
414
415 DPRINT("FdoCreate()\n");
416
417 Stack = IoGetCurrentIrpStackLocation(Irp);
418 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
419
420 if (Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
421 {
422 DPRINT1("Not a directory\n");
423 Status = STATUS_NOT_A_DIRECTORY;
424 goto done;
425 }
426
427 DPRINT("Open parallel port %lu: successful\n", DeviceExtension->PortNumber);
428 DeviceExtension->OpenCount++;
429
430 done:
431 Irp->IoStatus.Status = Status;
432 Irp->IoStatus.Information = 0;
433 IoCompleteRequest(Irp, IO_NO_INCREMENT);
434
435 return Status;
436 }
437
438
439 NTSTATUS
440 NTAPI
441 FdoClose(IN PDEVICE_OBJECT DeviceObject,
442 IN PIRP Irp)
443 {
444 PFDO_DEVICE_EXTENSION pDeviceExtension;
445
446 DPRINT("FdoClose()\n");
447
448 pDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
449 pDeviceExtension->OpenCount--;
450
451 Irp->IoStatus.Information = 0;
452 Irp->IoStatus.Status = STATUS_SUCCESS;
453 IoCompleteRequest(Irp, IO_NO_INCREMENT);
454
455 return STATUS_SUCCESS;
456 }
457
458
459 NTSTATUS
460 NTAPI
461 FdoCleanup(IN PDEVICE_OBJECT DeviceObject,
462 IN PIRP Irp)
463 {
464 DPRINT("FdoCleanup()\n");
465
466 Irp->IoStatus.Information = 0;
467 Irp->IoStatus.Status = STATUS_SUCCESS;
468 IoCompleteRequest(Irp, IO_NO_INCREMENT);
469
470 return STATUS_SUCCESS;
471 }
472
473
474 NTSTATUS
475 NTAPI
476 FdoRead(IN PDEVICE_OBJECT DeviceObject,
477 IN PIRP Irp)
478 {
479 DPRINT("FdoRead()\n");
480
481 Irp->IoStatus.Information = 0;
482 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
483 IoCompleteRequest(Irp, IO_NO_INCREMENT);
484 return STATUS_NOT_SUPPORTED;
485 }
486
487
488 NTSTATUS
489 NTAPI
490 FdoWrite(IN PDEVICE_OBJECT DeviceObject,
491 IN PIRP Irp)
492 {
493 DPRINT("FdoWrite()\n");
494
495 Irp->IoStatus.Information = 0;
496 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
497 IoCompleteRequest(Irp, IO_NO_INCREMENT);
498 return STATUS_NOT_SUPPORTED;
499 }
500
501
502 NTSTATUS
503 NTAPI
504 FdoPnp(IN PDEVICE_OBJECT DeviceObject,
505 IN PIRP Irp)
506 {
507 ULONG MinorFunction;
508 PIO_STACK_LOCATION Stack;
509 ULONG_PTR Information = 0;
510 NTSTATUS Status;
511
512 DPRINT("FdoPnp()\n");
513
514 Stack = IoGetCurrentIrpStackLocation(Irp);
515 MinorFunction = Stack->MinorFunction;
516
517 switch (MinorFunction)
518 {
519 /* FIXME: do all these minor functions
520 IRP_MN_QUERY_REMOVE_DEVICE 0x1
521 IRP_MN_REMOVE_DEVICE 0x2
522 {
523 TRACE_(SERIAL, "IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
524 IoAcquireRemoveLock
525 IoReleaseRemoveLockAndWait
526 pass request to DeviceExtension-LowerDriver
527 disable interface
528 IoDeleteDevice(Fdo) and/or IoDetachDevice
529 break;
530 }
531 IRP_MN_CANCEL_REMOVE_DEVICE 0x3
532 IRP_MN_STOP_DEVICE 0x4
533 IRP_MN_QUERY_STOP_DEVICE 0x5
534 IRP_MN_CANCEL_STOP_DEVICE 0x6
535 IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) 0x7
536 IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) 0x7
537 IRP_MN_QUERY_INTERFACE (optional) 0x8
538 IRP_MN_QUERY_CAPABILITIES (optional) 0x9
539 IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) 0xd
540 IRP_MN_QUERY_PNP_DEVICE_STATE (optional) 0x14
541 IRP_MN_DEVICE_USAGE_NOTIFICATION (required or optional) 0x16
542 IRP_MN_SURPRISE_REMOVAL 0x17
543 */
544 case IRP_MN_START_DEVICE: /* 0x0 */
545 DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
546
547 ASSERT(((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Common.PnpState == dsStopped);
548
549 /* Call lower driver */
550 Status = ForwardIrpAndWait(DeviceObject, Irp);
551 if (NT_SUCCESS(Status))
552 {
553 Status = FdoStartDevice(DeviceObject,
554 Stack->Parameters.StartDevice.AllocatedResources,
555 Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
556 }
557 break;
558
559 case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x7 */
560 switch (Stack->Parameters.QueryDeviceRelations.Type)
561 {
562 case BusRelations:
563 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
564 Status = FdoQueryBusRelations(DeviceObject, Irp, Stack);
565 Irp->IoStatus.Status = Status;
566 IoCompleteRequest(Irp, IO_NO_INCREMENT);
567 return Status;
568
569 case RemovalRelations:
570 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
571 return ForwardIrpAndForget(DeviceObject, Irp);
572
573 default:
574 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
575 Stack->Parameters.QueryDeviceRelations.Type);
576 return ForwardIrpAndForget(DeviceObject, Irp);
577 }
578 break;
579
580 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0xd */
581 DPRINT("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
582 return ForwardIrpAndForget(DeviceObject, Irp);
583
584 default:
585 DPRINT("Unknown minor function 0x%x\n", MinorFunction);
586 return ForwardIrpAndForget(DeviceObject, Irp);
587 }
588
589 Irp->IoStatus.Information = Information;
590 Irp->IoStatus.Status = Status;
591 IoCompleteRequest(Irp, IO_NO_INCREMENT);
592
593 return Status;
594 }
595
596
597 NTSTATUS
598 NTAPI
599 FdoPower(IN PDEVICE_OBJECT DeviceObject,
600 IN PIRP Irp)
601 {
602 DPRINT("FdoPower()\n");
603
604 Irp->IoStatus.Information = 0;
605 Irp->IoStatus.Status = STATUS_SUCCESS;
606 IoCompleteRequest(Irp, IO_NO_INCREMENT);
607
608 return STATUS_SUCCESS;
609 }
610
611 /* EOF */