2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: Serial port driver
4 * FILE: drivers/dd/serial/pnp.c
5 * PURPOSE: Serial IRP_MJ_PNP operations
7 * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
9 /* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
15 SerialAddDeviceInternal(
16 IN PDRIVER_OBJECT DriverObject
,
17 IN PDEVICE_OBJECT Pdo
,
18 IN UART_TYPE UartType
,
19 IN PULONG pComPortNumber OPTIONAL
,
20 OUT PDEVICE_OBJECT
* pFdo OPTIONAL
)
22 PDEVICE_OBJECT Fdo
= NULL
;
23 PSERIAL_DEVICE_EXTENSION DeviceExtension
= NULL
;
25 WCHAR DeviceNameBuffer
[32];
26 UNICODE_STRING DeviceName
;
27 static ULONG DeviceNumber
= 0;
28 static ULONG ComPortNumber
= 1;
30 TRACE_(SERIAL
, "SerialAddDeviceInternal()\n");
35 /* Create new device object */
36 swprintf(DeviceNameBuffer
, L
"\\Device\\Serial%lu", DeviceNumber
);
37 RtlInitUnicodeString(&DeviceName
, DeviceNameBuffer
);
38 Status
= IoCreateDevice(DriverObject
,
39 sizeof(SERIAL_DEVICE_EXTENSION
),
41 FILE_DEVICE_SERIAL_PORT
,
42 FILE_DEVICE_SECURE_OPEN
,
45 if (!NT_SUCCESS(Status
))
47 WARN_(SERIAL
, "IoCreateDevice() failed with status 0x%08x\n", Status
);
51 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)Fdo
->DeviceExtension
;
52 RtlZeroMemory(DeviceExtension
, sizeof(SERIAL_DEVICE_EXTENSION
));
54 /* Register device interface */
55 Status
= IoRegisterDeviceInterface(Pdo
, &GUID_DEVINTERFACE_COMPORT
, NULL
, &DeviceExtension
->SerialInterfaceName
);
56 if (!NT_SUCCESS(Status
))
58 WARN_(SERIAL
, "IoRegisterDeviceInterface() failed with status 0x%08x\n", Status
);
62 DeviceExtension
->SerialPortNumber
= DeviceNumber
++;
63 if (pComPortNumber
== NULL
)
64 DeviceExtension
->ComPort
= ComPortNumber
++;
66 DeviceExtension
->ComPort
= *pComPortNumber
;
67 DeviceExtension
->Pdo
= Pdo
;
68 DeviceExtension
->PnpState
= dsStopped
;
69 DeviceExtension
->UartType
= UartType
;
70 Status
= InitializeCircularBuffer(&DeviceExtension
->InputBuffer
, 16);
71 if (!NT_SUCCESS(Status
)) goto ByeBye
;
72 Status
= InitializeCircularBuffer(&DeviceExtension
->OutputBuffer
, 16);
73 if (!NT_SUCCESS(Status
)) goto ByeBye
;
74 IoInitializeRemoveLock(&DeviceExtension
->RemoveLock
, SERIAL_TAG
, 0, 0);
75 KeInitializeSpinLock(&DeviceExtension
->InputBufferLock
);
76 KeInitializeSpinLock(&DeviceExtension
->OutputBufferLock
);
77 KeInitializeEvent(&DeviceExtension
->InputBufferNotEmpty
, NotificationEvent
, FALSE
);
78 KeInitializeDpc(&DeviceExtension
->ReceivedByteDpc
, SerialReceiveByte
, DeviceExtension
);
79 KeInitializeDpc(&DeviceExtension
->SendByteDpc
, SerialSendByte
, DeviceExtension
);
80 KeInitializeDpc(&DeviceExtension
->CompleteIrpDpc
, SerialCompleteIrp
, DeviceExtension
);
81 Status
= IoAttachDeviceToDeviceStackSafe(Fdo
, Pdo
, &DeviceExtension
->LowerDevice
);
82 if (!NT_SUCCESS(Status
))
84 WARN_(SERIAL
, "IoAttachDeviceToDeviceStackSafe() failed with status 0x%08x\n", Status
);
87 if (DeviceExtension
->LowerDevice
->Flags
& DO_POWER_PAGABLE
)
88 Fdo
->Flags
|= DO_POWER_PAGABLE
;
89 if (DeviceExtension
->LowerDevice
->Flags
& DO_BUFFERED_IO
)
90 Fdo
->Flags
|= DO_BUFFERED_IO
;
91 if (DeviceExtension
->LowerDevice
->Flags
& DO_DIRECT_IO
)
92 Fdo
->Flags
|= DO_DIRECT_IO
;
94 /* Choose default strategy */
95 if ((Fdo
->Flags
& (DO_BUFFERED_IO
| DO_DIRECT_IO
)) == 0)
96 Fdo
->Flags
|= DO_BUFFERED_IO
;
98 Fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
104 return STATUS_SUCCESS
;
109 FreeCircularBuffer(&DeviceExtension
->InputBuffer
);
110 FreeCircularBuffer(&DeviceExtension
->OutputBuffer
);
118 IN PDRIVER_OBJECT DriverObject
,
119 IN PDEVICE_OBJECT Pdo
)
121 /* Serial.sys is a legacy driver. AddDevice is called once
122 * with a NULL Pdo just after the driver initialization.
123 * Detect this case and return success.
126 return STATUS_SUCCESS
;
128 /* We have here a PDO not null. It represents a real serial
129 * port. So call the internal AddDevice function.
131 return SerialAddDeviceInternal(DriverObject
, Pdo
, UartUnknown
, NULL
, NULL
);
135 SerialPnpStartDevice(
136 IN PDEVICE_OBJECT DeviceObject
,
137 IN PCM_RESOURCE_LIST ResourceList
,
138 IN PCM_RESOURCE_LIST ResourceListTranslated
)
140 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
141 WCHAR DeviceNameBuffer
[32];
142 UNICODE_STRING DeviceName
;
143 WCHAR LinkNameBuffer
[32];
144 UNICODE_STRING LinkName
;
145 WCHAR ComPortBuffer
[32];
146 UNICODE_STRING ComPort
;
151 KAFFINITY Affinity
= 0;
152 KINTERRUPT_MODE InterruptMode
= Latched
;
153 BOOLEAN ShareInterrupt
= TRUE
;
154 OBJECT_ATTRIBUTES objectAttributes
;
156 UNICODE_STRING KeyName
;
160 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
162 ASSERT(ResourceList
);
163 ASSERT(DeviceExtension
);
164 ASSERT(DeviceExtension
->PnpState
== dsStopped
);
168 WARN_(SERIAL
, "No allocated resources sent to driver\n");
169 return STATUS_INSUFFICIENT_RESOURCES
;
171 if (ResourceList
->Count
!= 1)
173 WARN_(SERIAL
, "Wrong number of allocated resources sent to driver\n");
174 return STATUS_INSUFFICIENT_RESOURCES
;
176 if (ResourceList
->List
[0].PartialResourceList
.Version
!= 1
177 || ResourceList
->List
[0].PartialResourceList
.Revision
!= 1
178 || ResourceListTranslated
->List
[0].PartialResourceList
.Version
!= 1
179 || ResourceListTranslated
->List
[0].PartialResourceList
.Revision
!= 1)
181 WARN_(SERIAL
, "Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1\n",
182 ResourceList
->List
[0].PartialResourceList
.Version
,
183 ResourceList
->List
[0].PartialResourceList
.Revision
,
184 ResourceListTranslated
->List
[0].PartialResourceList
.Version
,
185 ResourceListTranslated
->List
[0].PartialResourceList
.Revision
);
186 return STATUS_REVISION_MISMATCH
;
189 DeviceExtension
->BaudRate
= 19200;
190 DeviceExtension
->BaseAddress
= 0;
192 for (i
= 0; i
< ResourceList
->List
[0].PartialResourceList
.Count
; i
++)
194 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor
= &ResourceList
->List
[0].PartialResourceList
.PartialDescriptors
[i
];
195 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptorTranslated
= &ResourceListTranslated
->List
[0].PartialResourceList
.PartialDescriptors
[i
];
196 switch (PartialDescriptor
->Type
)
198 case CmResourceTypePort
:
199 if (PartialDescriptor
->u
.Port
.Length
< 7)
200 return STATUS_INSUFFICIENT_RESOURCES
;
201 if (DeviceExtension
->BaseAddress
!= 0)
202 return STATUS_UNSUCCESSFUL
;
203 DeviceExtension
->BaseAddress
= PartialDescriptor
->u
.Port
.Start
.u
.LowPart
;
205 case CmResourceTypeInterrupt
:
206 Dirql
= (KIRQL
)PartialDescriptorTranslated
->u
.Interrupt
.Level
;
207 Vector
= PartialDescriptorTranslated
->u
.Interrupt
.Vector
;
208 Affinity
= PartialDescriptorTranslated
->u
.Interrupt
.Affinity
;
209 if (PartialDescriptorTranslated
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
)
210 InterruptMode
= Latched
;
212 InterruptMode
= LevelSensitive
;
213 ShareInterrupt
= (PartialDescriptorTranslated
->ShareDisposition
== CmResourceShareShared
);
217 INFO_(SERIAL
, "New COM port. Base = 0x%lx, Irql = %u\n",
218 DeviceExtension
->BaseAddress
, Dirql
);
219 if (!DeviceExtension
->BaseAddress
)
220 return STATUS_INSUFFICIENT_RESOURCES
;
222 return STATUS_INSUFFICIENT_RESOURCES
;
223 ComPortBase
= ULongToPtr(DeviceExtension
->BaseAddress
);
225 /* Test if we are trying to start the serial port used for debugging */
226 INFO_(SERIAL
, "Comparing addresses: KdComPortInUse: %p, ComPortBase: %p\n", KdComPortInUse
, ComPortBase
);
227 if (KdComPortInUse
== ComPortBase
)
229 INFO_(SERIAL
, "Failing IRP_MN_START_DEVICE as this serial port is used for debugging\n");
230 return STATUS_INSUFFICIENT_RESOURCES
;
233 if (DeviceExtension
->UartType
== UartUnknown
)
234 DeviceExtension
->UartType
= SerialDetectUartType(ComPortBase
);
236 /* Get current settings */
237 DeviceExtension
->MCR
= READ_PORT_UCHAR(SER_MCR(ComPortBase
));
238 DeviceExtension
->MSR
= READ_PORT_UCHAR(SER_MSR(ComPortBase
));
239 DeviceExtension
->WaitMask
= 0;
242 Status
= SerialSetBaudRate(DeviceExtension
, DeviceExtension
->BaudRate
);
243 if (!NT_SUCCESS(Status
))
245 WARN_(SERIAL
, "SerialSetBaudRate() failed with status 0x%08x\n", Status
);
249 /* Set line control */
250 DeviceExtension
->SerialLineControl
.StopBits
= STOP_BIT_1
;
251 DeviceExtension
->SerialLineControl
.Parity
= NO_PARITY
;
252 DeviceExtension
->SerialLineControl
.WordLength
= 8;
253 Status
= SerialSetLineControl(DeviceExtension
, &DeviceExtension
->SerialLineControl
);
254 if (!NT_SUCCESS(Status
))
256 WARN_(SERIAL
, "SerialSetLineControl() failed with status 0x%08x\n", Status
);
260 /* Clear receive/transmit buffers */
261 if (DeviceExtension
->UartType
>= Uart16550A
)
263 /* 16550 UARTs also have FIFO queues, but they are unusable due to a bug */
264 WRITE_PORT_UCHAR(SER_FCR(ComPortBase
),
265 SR_FCR_CLEAR_RCVR
| SR_FCR_CLEAR_XMIT
);
268 /* Create link \DosDevices\COMX -> \Device\SerialX */
269 swprintf(DeviceNameBuffer
, L
"\\Device\\Serial%lu", DeviceExtension
->SerialPortNumber
);
270 swprintf(LinkNameBuffer
, L
"\\DosDevices\\COM%lu", DeviceExtension
->ComPort
);
271 swprintf(ComPortBuffer
, L
"COM%lu", DeviceExtension
->ComPort
);
272 RtlInitUnicodeString(&DeviceName
, DeviceNameBuffer
);
273 RtlInitUnicodeString(&LinkName
, LinkNameBuffer
);
274 RtlInitUnicodeString(&ComPort
, ComPortBuffer
);
275 Status
= IoCreateSymbolicLink(&LinkName
, &DeviceName
);
276 if (!NT_SUCCESS(Status
))
278 WARN_(SERIAL
, "IoCreateSymbolicLink() failed with status 0x%08x\n", Status
);
282 /* Connect interrupt and enable them */
283 Status
= IoConnectInterrupt(
284 &DeviceExtension
->Interrupt
, SerialInterruptService
,
286 Vector
, Dirql
, Dirql
,
287 InterruptMode
, ShareInterrupt
,
289 if (!NT_SUCCESS(Status
))
291 WARN_(SERIAL
, "IoConnectInterrupt() failed with status 0x%08x\n", Status
);
292 IoSetDeviceInterfaceState(&DeviceExtension
->SerialInterfaceName
, FALSE
);
293 IoDeleteSymbolicLink(&LinkName
);
297 /* Write an entry value under HKLM\HARDWARE\DeviceMap\SERIALCOMM */
298 /* This step is not mandatory, so don't exit in case of error */
299 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\HARDWARE\\DeviceMap\\SERIALCOMM");
300 InitializeObjectAttributes(&objectAttributes
, &KeyName
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
301 Status
= ZwCreateKey(&hKey
, KEY_SET_VALUE
, &objectAttributes
, 0, NULL
, REG_OPTION_VOLATILE
, NULL
);
302 if (NT_SUCCESS(Status
))
304 /* Key = \Device\Serialx, Value = COMx */
305 ZwSetValueKey(hKey
, &DeviceName
, 0, REG_SZ
, ComPortBuffer
, ComPort
.Length
+ sizeof(WCHAR
));
309 DeviceExtension
->PnpState
= dsStarted
;
311 /* Activate interrupt modes */
312 IER
= READ_PORT_UCHAR(SER_IER(ComPortBase
));
313 IER
|= SR_IER_DATA_RECEIVED
| SR_IER_THR_EMPTY
| SR_IER_LSR_CHANGE
| SR_IER_MSR_CHANGE
;
314 WRITE_PORT_UCHAR(SER_IER(ComPortBase
), IER
);
316 /* Activate DTR, RTS */
317 DeviceExtension
->MCR
|= SR_MCR_DTR
| SR_MCR_RTS
;
318 WRITE_PORT_UCHAR(SER_MCR(ComPortBase
), DeviceExtension
->MCR
);
320 /* Activate serial interface */
321 IoSetDeviceInterfaceState(&DeviceExtension
->SerialInterfaceName
, TRUE
);
322 /* We don't really care if the call succeeded or not... */
324 return STATUS_SUCCESS
;
329 IN PDEVICE_OBJECT DeviceObject
,
333 PIO_STACK_LOCATION Stack
;
334 ULONG_PTR Information
= 0;
337 Stack
= IoGetCurrentIrpStackLocation(Irp
);
338 MinorFunction
= Stack
->MinorFunction
;
340 switch (MinorFunction
)
342 /* FIXME: do all these minor functions
343 IRP_MN_QUERY_REMOVE_DEVICE 0x1
344 IRP_MN_REMOVE_DEVICE 0x2
346 TRACE_(SERIAL, "IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
348 IoReleaseRemoveLockAndWait
349 pass request to DeviceExtension-LowerDriver
351 IoDeleteDevice(Fdo) and/or IoDetachDevice
354 IRP_MN_CANCEL_REMOVE_DEVICE 0x3
355 IRP_MN_STOP_DEVICE 0x4
356 IRP_MN_QUERY_STOP_DEVICE 0x5
357 IRP_MN_CANCEL_STOP_DEVICE 0x6
358 IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) 0x7
359 IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) 0x7
360 IRP_MN_QUERY_INTERFACE (optional) 0x8
361 IRP_MN_QUERY_CAPABILITIES (optional) 0x9
362 IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) 0xd
363 IRP_MN_QUERY_PNP_DEVICE_STATE (optional) 0x14
364 IRP_MN_DEVICE_USAGE_NOTIFICATION (required or optional) 0x16
365 IRP_MN_SURPRISE_REMOVAL 0x17
367 case IRP_MN_START_DEVICE
: /* 0x0 */
369 TRACE_(SERIAL
, "IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
371 ASSERT(((PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
)->PnpState
== dsStopped
);
373 /* Call lower driver */
374 Status
= ForwardIrpAndWait(DeviceObject
, Irp
);
375 if (NT_SUCCESS(Status
))
376 Status
= SerialPnpStartDevice(
378 Stack
->Parameters
.StartDevice
.AllocatedResources
,
379 Stack
->Parameters
.StartDevice
.AllocatedResourcesTranslated
);
382 case IRP_MN_QUERY_DEVICE_RELATIONS
: /* (optional) 0x7 */
384 switch (Stack
->Parameters
.QueryDeviceRelations
.Type
)
388 TRACE_(SERIAL
, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
389 return ForwardIrpAndForget(DeviceObject
, Irp
);
391 case RemovalRelations
:
393 TRACE_(SERIAL
, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
394 return ForwardIrpAndForget(DeviceObject
, Irp
);
397 TRACE_(SERIAL
, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
398 Stack
->Parameters
.QueryDeviceRelations
.Type
);
399 return ForwardIrpAndForget(DeviceObject
, Irp
);
403 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS
: /* (optional) 0xd */
405 TRACE_(SERIAL
, "IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS\n");
406 return ForwardIrpAndForget(DeviceObject
, Irp
);
410 TRACE_(SERIAL
, "Unknown minor function 0x%x\n", MinorFunction
);
411 return ForwardIrpAndForget(DeviceObject
, Irp
);
415 Irp
->IoStatus
.Information
= Information
;
416 Irp
->IoStatus
.Status
= Status
;
417 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);