[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 DeviceRelations->Count = 1;
375
376 Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
377
378 DPRINT("Done\n");
379
380 return STATUS_SUCCESS;
381 }
382
383
384 /* PUBLIC FUNCTIONS *********************************************************/
385
386 NTSTATUS
387 NTAPI
388 AddDevice(IN PDRIVER_OBJECT DriverObject,
389 IN PDEVICE_OBJECT Pdo)
390 {
391 DPRINT("AddDevice(%p %p)\n", DriverObject, Pdo);
392
393 /* Serial.sys is a legacy driver. AddDevice is called once
394 * with a NULL Pdo just after the driver initialization.
395 * Detect this case and return success.
396 */
397 if (Pdo == NULL)
398 return STATUS_SUCCESS;
399
400 /* We have here a PDO not null. It represents a real serial
401 * port. So call the internal AddDevice function.
402 */
403 return AddDeviceInternal(DriverObject, Pdo, NULL, NULL);
404 }
405
406
407 NTSTATUS
408 NTAPI
409 FdoCreate(IN PDEVICE_OBJECT DeviceObject,
410 IN PIRP Irp)
411 {
412 PFDO_DEVICE_EXTENSION DeviceExtension;
413 PIO_STACK_LOCATION Stack;
414 NTSTATUS Status = STATUS_SUCCESS;
415
416 DPRINT("FdoCreate()\n");
417
418 Stack = IoGetCurrentIrpStackLocation(Irp);
419 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
420
421 if (Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
422 {
423 DPRINT1("Not a directory\n");
424 Status = STATUS_NOT_A_DIRECTORY;
425 goto done;
426 }
427
428 DPRINT("Open parallel port %lu: successful\n", DeviceExtension->PortNumber);
429 DeviceExtension->OpenCount++;
430
431 done:
432 Irp->IoStatus.Status = Status;
433 Irp->IoStatus.Information = 0;
434 IoCompleteRequest(Irp, IO_NO_INCREMENT);
435
436 return Status;
437 }
438
439
440 NTSTATUS
441 NTAPI
442 FdoClose(IN PDEVICE_OBJECT DeviceObject,
443 IN PIRP Irp)
444 {
445 PFDO_DEVICE_EXTENSION pDeviceExtension;
446
447 DPRINT("FdoClose()\n");
448
449 pDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
450 pDeviceExtension->OpenCount--;
451
452 Irp->IoStatus.Information = 0;
453 Irp->IoStatus.Status = STATUS_SUCCESS;
454 IoCompleteRequest(Irp, IO_NO_INCREMENT);
455
456 return STATUS_SUCCESS;
457 }
458
459
460 NTSTATUS
461 NTAPI
462 FdoCleanup(IN PDEVICE_OBJECT DeviceObject,
463 IN PIRP Irp)
464 {
465 DPRINT("FdoCleanup()\n");
466
467 Irp->IoStatus.Information = 0;
468 Irp->IoStatus.Status = STATUS_SUCCESS;
469 IoCompleteRequest(Irp, IO_NO_INCREMENT);
470
471 return STATUS_SUCCESS;
472 }
473
474
475 NTSTATUS
476 NTAPI
477 FdoRead(IN PDEVICE_OBJECT DeviceObject,
478 IN PIRP Irp)
479 {
480 DPRINT("FdoRead()\n");
481
482 Irp->IoStatus.Information = 0;
483 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
484 IoCompleteRequest(Irp, IO_NO_INCREMENT);
485 return STATUS_NOT_SUPPORTED;
486 }
487
488
489 NTSTATUS
490 NTAPI
491 FdoWrite(IN PDEVICE_OBJECT DeviceObject,
492 IN PIRP Irp)
493 {
494 DPRINT("FdoWrite()\n");
495
496 Irp->IoStatus.Information = 0;
497 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
498 IoCompleteRequest(Irp, IO_NO_INCREMENT);
499 return STATUS_NOT_SUPPORTED;
500 }
501
502
503 NTSTATUS
504 NTAPI
505 FdoPnp(IN PDEVICE_OBJECT DeviceObject,
506 IN PIRP Irp)
507 {
508 ULONG MinorFunction;
509 PIO_STACK_LOCATION Stack;
510 ULONG_PTR Information = 0;
511 NTSTATUS Status;
512
513 DPRINT("FdoPnp()\n");
514
515 Stack = IoGetCurrentIrpStackLocation(Irp);
516 MinorFunction = Stack->MinorFunction;
517
518 switch (MinorFunction)
519 {
520 /* FIXME: do all these minor functions
521 IRP_MN_QUERY_REMOVE_DEVICE 0x1
522 IRP_MN_REMOVE_DEVICE 0x2
523 {
524 TRACE_(SERIAL, "IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
525 IoAcquireRemoveLock
526 IoReleaseRemoveLockAndWait
527 pass request to DeviceExtension-LowerDriver
528 disable interface
529 IoDeleteDevice(Fdo) and/or IoDetachDevice
530 break;
531 }
532 IRP_MN_CANCEL_REMOVE_DEVICE 0x3
533 IRP_MN_STOP_DEVICE 0x4
534 IRP_MN_QUERY_STOP_DEVICE 0x5
535 IRP_MN_CANCEL_STOP_DEVICE 0x6
536 IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) 0x7
537 IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) 0x7
538 IRP_MN_QUERY_INTERFACE (optional) 0x8
539 IRP_MN_QUERY_CAPABILITIES (optional) 0x9
540 IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) 0xd
541 IRP_MN_QUERY_PNP_DEVICE_STATE (optional) 0x14
542 IRP_MN_DEVICE_USAGE_NOTIFICATION (required or optional) 0x16
543 IRP_MN_SURPRISE_REMOVAL 0x17
544 */
545 case IRP_MN_START_DEVICE: /* 0x0 */
546 DPRINT("IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
547
548 ASSERT(((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Common.PnpState == dsStopped);
549
550 /* Call lower driver */
551 Status = ForwardIrpAndWait(DeviceObject, Irp);
552 if (NT_SUCCESS(Status))
553 {
554 Status = FdoStartDevice(DeviceObject,
555 Stack->Parameters.StartDevice.AllocatedResources,
556 Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
557 }
558 break;
559
560 case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x7 */
561 switch (Stack->Parameters.QueryDeviceRelations.Type)
562 {
563 case BusRelations:
564 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
565 Status = FdoQueryBusRelations(DeviceObject, Irp, Stack);
566 Irp->IoStatus.Status = Status;
567 IoCompleteRequest(Irp, IO_NO_INCREMENT);
568 return Status;
569
570 case RemovalRelations:
571 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
572 return ForwardIrpAndForget(DeviceObject, Irp);
573
574 default:
575 DPRINT("IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
576 Stack->Parameters.QueryDeviceRelations.Type);
577 return ForwardIrpAndForget(DeviceObject, Irp);
578 }
579 break;
580
581 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0xd */
582 DPRINT("IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
583 return ForwardIrpAndForget(DeviceObject, Irp);
584
585 default:
586 DPRINT("Unknown minor function 0x%x\n", MinorFunction);
587 return ForwardIrpAndForget(DeviceObject, Irp);
588 }
589
590 Irp->IoStatus.Information = Information;
591 Irp->IoStatus.Status = Status;
592 IoCompleteRequest(Irp, IO_NO_INCREMENT);
593
594 return Status;
595 }
596
597
598 NTSTATUS
599 NTAPI
600 FdoPower(IN PDEVICE_OBJECT DeviceObject,
601 IN PIRP Irp)
602 {
603 DPRINT("FdoPower()\n");
604
605 Irp->IoStatus.Information = 0;
606 Irp->IoStatus.Status = STATUS_SUCCESS;
607 IoCompleteRequest(Irp, IO_NO_INCREMENT);
608
609 return STATUS_SUCCESS;
610 }
611
612 /* EOF */