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.com)
9 /* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
16 SerialAddDeviceInternal(
17 IN PDRIVER_OBJECT DriverObject
,
18 IN PDEVICE_OBJECT Pdo
,
19 IN UART_TYPE UartType
,
20 IN PULONG pComPortNumber OPTIONAL
,
21 OUT PDEVICE_OBJECT
* pFdo OPTIONAL
)
23 PDEVICE_OBJECT Fdo
= NULL
;
24 PSERIAL_DEVICE_EXTENSION DeviceExtension
= NULL
;
26 WCHAR DeviceNameBuffer
[32];
27 UNICODE_STRING DeviceName
;
28 static ULONG DeviceNumber
= 0;
29 static ULONG ComPortNumber
= 1;
31 DPRINT("Serial: SerialAddDeviceInternal called\n");
36 /* Create new device object */
37 swprintf(DeviceNameBuffer
, L
"\\Device\\Serial%lu", DeviceNumber
);
38 RtlInitUnicodeString(&DeviceName
, DeviceNameBuffer
);
39 Status
= IoCreateDevice(DriverObject
,
40 sizeof(SERIAL_DEVICE_EXTENSION
),
42 FILE_DEVICE_SERIAL_PORT
,
43 FILE_DEVICE_SECURE_OPEN
,
46 if (!NT_SUCCESS(Status
))
48 DPRINT("Serial: IoCreateDevice() failed with status 0x%08x\n", Status
);
52 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)Fdo
->DeviceExtension
;
53 RtlZeroMemory(DeviceExtension
, sizeof(SERIAL_DEVICE_EXTENSION
));
55 /* Register device interface */
56 Status
= IoRegisterDeviceInterface(Pdo
, &GUID_DEVINTERFACE_COMPORT
, NULL
, &DeviceExtension
->SerialInterfaceName
);
57 if (!NT_SUCCESS(Status
))
59 DPRINT("Serial: IoRegisterDeviceInterface() failed with status 0x%08x\n", Status
);
63 DeviceExtension
->SerialPortNumber
= DeviceNumber
++;
64 if (pComPortNumber
== NULL
)
65 DeviceExtension
->ComPort
= ComPortNumber
++;
67 DeviceExtension
->ComPort
= *pComPortNumber
;
68 DeviceExtension
->Pdo
= Pdo
;
69 DeviceExtension
->PnpState
= dsStopped
;
70 DeviceExtension
->UartType
= UartType
;
71 Status
= InitializeCircularBuffer(&DeviceExtension
->InputBuffer
, 16);
72 if (!NT_SUCCESS(Status
)) goto ByeBye
;
73 Status
= InitializeCircularBuffer(&DeviceExtension
->OutputBuffer
, 16);
74 if (!NT_SUCCESS(Status
)) goto ByeBye
;
75 IoInitializeRemoveLock(&DeviceExtension
->RemoveLock
, SERIAL_TAG
, 0, 0);
76 KeInitializeSpinLock(&DeviceExtension
->InputBufferLock
);
77 KeInitializeSpinLock(&DeviceExtension
->OutputBufferLock
);
78 KeInitializeEvent(&DeviceExtension
->InputBufferNotEmpty
, NotificationEvent
, FALSE
);
79 KeInitializeDpc(&DeviceExtension
->ReceivedByteDpc
, SerialReceiveByte
, DeviceExtension
);
80 KeInitializeDpc(&DeviceExtension
->SendByteDpc
, SerialSendByte
, DeviceExtension
);
81 Fdo
->Flags
|= DO_POWER_PAGABLE
;
82 Status
= IoAttachDeviceToDeviceStackSafe(Fdo
, Pdo
, &DeviceExtension
->LowerDevice
);
83 if (!NT_SUCCESS(Status
))
85 DPRINT("Serial: IoAttachDeviceToDeviceStackSafe() failed with status 0x%08x\n", Status
);
88 Fdo
->Flags
|= DO_BUFFERED_IO
;
89 Fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
95 return STATUS_SUCCESS
;
100 FreeCircularBuffer(&DeviceExtension
->InputBuffer
);
101 FreeCircularBuffer(&DeviceExtension
->OutputBuffer
);
109 IN PDRIVER_OBJECT DriverObject
,
110 IN PDEVICE_OBJECT Pdo
)
112 /* Serial.sys is a legacy driver. AddDevice is called once
113 * with a NULL Pdo just after the driver initialization.
114 * Detect this case and return success.
117 return STATUS_SUCCESS
;
119 /* We have here a PDO not null. It represents a real serial
120 * port. So call the internal AddDevice function.
122 return SerialAddDeviceInternal(DriverObject
, Pdo
, UartUnknown
, NULL
, NULL
);
126 SerialPnpStartDevice(
127 IN PDEVICE_OBJECT DeviceObject
,
128 IN PCM_RESOURCE_LIST ResourceList
,
129 IN PCM_RESOURCE_LIST ResourceListTranslated
)
131 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
132 WCHAR DeviceNameBuffer
[32];
133 UNICODE_STRING DeviceName
;
134 WCHAR LinkNameBuffer
[32];
135 UNICODE_STRING LinkName
;
136 WCHAR ComPortBuffer
[32];
137 UNICODE_STRING ComPort
;
142 KAFFINITY Affinity
= 0;
143 KINTERRUPT_MODE InterruptMode
= Latched
;
144 BOOLEAN ShareInterrupt
= TRUE
;
145 OBJECT_ATTRIBUTES objectAttributes
;
147 UNICODE_STRING KeyName
;
151 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
153 ASSERT(ResourceList
);
154 ASSERT(DeviceExtension
);
155 ASSERT(DeviceExtension
->PnpState
== dsStopped
);
157 DeviceExtension
->BaudRate
= 19200;
158 DeviceExtension
->BaseAddress
= 0;
160 for (i
= 0; i
< ResourceList
->Count
; i
++)
162 for (j
= 0; j
< ResourceList
->List
[i
].PartialResourceList
.Count
; j
++)
164 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor
= &ResourceList
->List
[i
].PartialResourceList
.PartialDescriptors
[j
];
165 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptorTranslated
= &ResourceListTranslated
->List
[i
].PartialResourceList
.PartialDescriptors
[j
];
166 switch (PartialDescriptor
->Type
)
168 case CmResourceTypePort
:
169 if (PartialDescriptor
->u
.Port
.Length
< 8)
170 return STATUS_INSUFFICIENT_RESOURCES
;
171 if (DeviceExtension
->BaseAddress
!= 0)
172 return STATUS_UNSUCCESSFUL
;
173 DeviceExtension
->BaseAddress
= PartialDescriptor
->u
.Port
.Start
.u
.LowPart
;
175 case CmResourceTypeInterrupt
:
176 Dirql
= (KIRQL
)PartialDescriptorTranslated
->u
.Interrupt
.Level
;
177 Vector
= PartialDescriptorTranslated
->u
.Interrupt
.Vector
;
178 Affinity
= PartialDescriptorTranslated
->u
.Interrupt
.Affinity
;
179 if (PartialDescriptorTranslated
->Flags
& CM_RESOURCE_INTERRUPT_LATCHED
)
180 InterruptMode
= Latched
;
182 InterruptMode
= LevelSensitive
;
183 ShareInterrupt
= (PartialDescriptorTranslated
->ShareDisposition
== CmResourceShareShared
);
188 DPRINT("Serial: New COM port. Base = 0x%lx, Irql = %u\n",
189 DeviceExtension
->BaseAddress
, Dirql
);
190 if (!DeviceExtension
->BaseAddress
)
191 return STATUS_INSUFFICIENT_RESOURCES
;
193 return STATUS_INSUFFICIENT_RESOURCES
;
194 ComPortBase
= (PUCHAR
)DeviceExtension
->BaseAddress
;
196 if (DeviceExtension
->UartType
== UartUnknown
)
197 DeviceExtension
->UartType
= SerialDetectUartType(ComPortBase
);
199 /* Get current settings */
200 DeviceExtension
->MCR
= READ_PORT_UCHAR(SER_MCR(ComPortBase
));
201 DeviceExtension
->MSR
= READ_PORT_UCHAR(SER_MSR(ComPortBase
));
202 DeviceExtension
->WaitMask
= 0;
205 Status
= SerialSetBaudRate(DeviceExtension
, DeviceExtension
->BaudRate
);
206 if (!NT_SUCCESS(Status
))
208 DPRINT("Serial: SerialSetBaudRate() failed with status 0x%08x\n", Status
);
212 /* Set line control */
213 DeviceExtension
->SerialLineControl
.StopBits
= STOP_BIT_1
;
214 DeviceExtension
->SerialLineControl
.Parity
= NO_PARITY
;
215 DeviceExtension
->SerialLineControl
.WordLength
= 8;
216 Status
= SerialSetLineControl(DeviceExtension
, &DeviceExtension
->SerialLineControl
);
217 if (!NT_SUCCESS(Status
))
219 DPRINT("Serial: SerialSetLineControl() failed with status 0x%08x\n", Status
);
223 /* Clear receive/transmit buffers */
224 if (DeviceExtension
->UartType
>= Uart16550A
)
226 /* 16550 UARTs also have FIFO queues, but they are unusable due to a bug */
227 WRITE_PORT_UCHAR(SER_FCR(ComPortBase
),
228 SR_FCR_CLEAR_RCVR
| SR_FCR_CLEAR_XMIT
);
231 /* Create link \DosDevices\COMX -> \Device\SerialX */
232 swprintf(DeviceNameBuffer
, L
"\\Device\\Serial%lu", DeviceExtension
->SerialPortNumber
);
233 swprintf(LinkNameBuffer
, L
"\\DosDevices\\COM%lu", DeviceExtension
->ComPort
);
234 swprintf(ComPortBuffer
, L
"COM%lu", DeviceExtension
->ComPort
);
235 RtlInitUnicodeString(&DeviceName
, DeviceNameBuffer
);
236 RtlInitUnicodeString(&LinkName
, LinkNameBuffer
);
237 RtlInitUnicodeString(&ComPort
, ComPortBuffer
);
238 Status
= IoCreateSymbolicLink(&LinkName
, &DeviceName
);
239 if (!NT_SUCCESS(Status
))
241 DPRINT("Serial: IoCreateSymbolicLink() failed with status 0x%08x\n", Status
);
245 /* Activate serial interface */
246 Status
= IoSetDeviceInterfaceState(&DeviceExtension
->SerialInterfaceName
, TRUE
);
247 if (!NT_SUCCESS(Status
))
249 DPRINT("Serial: IoSetDeviceInterfaceState() failed with status 0x%08x\n", Status
);
250 IoDeleteSymbolicLink(&LinkName
);
254 /* Connect interrupt and enable them */
255 Status
= IoConnectInterrupt(
256 &DeviceExtension
->Interrupt
, SerialInterruptService
,
258 Vector
, Dirql
, Dirql
,
259 InterruptMode
, ShareInterrupt
,
261 if (!NT_SUCCESS(Status
))
263 DPRINT("Serial: IoConnectInterrupt() failed with status 0x%08x\n", Status
);
264 IoSetDeviceInterfaceState(&DeviceExtension
->SerialInterfaceName
, FALSE
);
265 IoDeleteSymbolicLink(&LinkName
);
269 /* Write an entry value under HKLM\HARDWARE\DeviceMap\SERIALCOMM */
270 /* This step is not mandatory, so don't exit in case of error */
271 RtlInitUnicodeString(&KeyName
, L
"\\Registry\\Machine\\HARDWARE\\DeviceMap\\SERIALCOMM");
272 InitializeObjectAttributes(&objectAttributes
, &KeyName
, OBJ_CASE_INSENSITIVE
, NULL
, NULL
);
273 Status
= ZwCreateKey(&hKey
, KEY_SET_VALUE
, &objectAttributes
, 0, NULL
, REG_OPTION_VOLATILE
, NULL
);
274 if (NT_SUCCESS(Status
))
276 /* Key = \Device\Serialx, Value = COMx */
277 ZwSetValueKey(hKey
, &DeviceName
, 0, REG_SZ
, ComPortBuffer
, ComPort
.Length
+ sizeof(WCHAR
));
281 DeviceExtension
->PnpState
= dsStarted
;
283 /* Activate interrupt modes */
284 IER
= READ_PORT_UCHAR(SER_IER(ComPortBase
));
285 IER
|= SR_IER_DATA_RECEIVED
| SR_IER_THR_EMPTY
| SR_IER_LSR_CHANGE
| SR_IER_MSR_CHANGE
;
286 WRITE_PORT_UCHAR(SER_IER(ComPortBase
), IER
);
288 /* Activate DTR, RTS */
289 DeviceExtension
->MCR
|= SR_MCR_DTR
| SR_MCR_RTS
;
290 WRITE_PORT_UCHAR(SER_MCR(ComPortBase
), DeviceExtension
->MCR
);
292 return STATUS_SUCCESS
;
297 IN PDEVICE_OBJECT DeviceObject
,
301 PIO_STACK_LOCATION Stack
;
302 ULONG_PTR Information
= 0;
305 Stack
= IoGetCurrentIrpStackLocation(Irp
);
306 MinorFunction
= Stack
->MinorFunction
;
308 switch (MinorFunction
)
310 /* FIXME: do all these minor functions
311 IRP_MN_QUERY_REMOVE_DEVICE 0x1
312 IRP_MN_REMOVE_DEVICE 0x2
314 DPRINT("Serial: IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
316 IoReleaseRemoveLockAndWait
317 pass request to DeviceExtension-LowerDriver
319 IoDeleteDevice(Fdo) and/or IoDetachDevice
322 IRP_MN_CANCEL_REMOVE_DEVICE 0x3
323 IRP_MN_STOP_DEVICE 0x4
324 IRP_MN_QUERY_STOP_DEVICE 0x5
325 IRP_MN_CANCEL_STOP_DEVICE 0x6
326 IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) 0x7
327 IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) 0x7
328 IRP_MN_QUERY_INTERFACE (optional) 0x8
329 IRP_MN_QUERY_CAPABILITIES (optional) 0x9
330 IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) 0xd
331 IRP_MN_QUERY_PNP_DEVICE_STATE (optional) 0x14
332 IRP_MN_DEVICE_USAGE_NOTIFICATION (required or optional) 0x16
333 IRP_MN_SURPRISE_REMOVAL 0x17
335 case IRP_MN_START_DEVICE
: /* 0x0 */
337 BOOLEAN ConflictDetected
;
338 DPRINT("Serial: IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
340 ASSERT(((PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
)->PnpState
== dsStopped
);
342 /* FIXME: HACK: verify that we have some allocated resources.
343 * It seems not to be always the case on some hardware
345 if (Stack
->Parameters
.StartDevice
.AllocatedResources
== NULL
)
347 DPRINT1("Serial: no allocated resources. Can't start COM%lu\n",
348 ((PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
)->ComPort
);
349 Status
= STATUS_INSUFFICIENT_RESOURCES
;
352 /* FIXME: HACK: verify that we don't have resource conflict,
353 * because PnP manager doesn't do it automatically
355 Status
= IoReportResourceForDetection(
356 DeviceObject
->DriverObject
, Stack
->Parameters
.StartDevice
.AllocatedResources
, 0,
359 if (!NT_SUCCESS(Status
))
361 Irp
->IoStatus
.Information
= 0;
362 Irp
->IoStatus
.Status
= Status
;
363 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
367 /* Call lower driver */
368 Status
= ForwardIrpAndWait(DeviceObject
, Irp
);
369 if (NT_SUCCESS(Status
))
370 Status
= SerialPnpStartDevice(
372 Stack
->Parameters
.StartDevice
.AllocatedResources
,
373 Stack
->Parameters
.StartDevice
.AllocatedResourcesTranslated
);
376 case IRP_MN_QUERY_DEVICE_RELATIONS
: /* (optional) 0x7 */
378 switch (Stack
->Parameters
.QueryDeviceRelations
.Type
)
382 DPRINT("Serial: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations\n");
383 return ForwardIrpAndForget(DeviceObject
, Irp
);
385 case RemovalRelations
:
387 DPRINT("Serial: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations\n");
388 return ForwardIrpAndForget(DeviceObject
, Irp
);
391 DPRINT1("Serial: IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx\n",
392 Stack
->Parameters
.QueryDeviceRelations
.Type
);
393 return ForwardIrpAndForget(DeviceObject
, Irp
);
399 DPRINT1("Serial: unknown minor function 0x%x\n", MinorFunction
);
400 return ForwardIrpAndForget(DeviceObject
, Irp
);
404 Irp
->IoStatus
.Information
= Information
;
405 Irp
->IoStatus
.Status
= Status
;
406 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);