Copy wininet to branch
[reactos.git] / reactos / drivers / dd / serial / pnp.c
1 /* $Id:
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: drivers/dd/serial/pnp.c
6 * PURPOSE: Serial IRP_MJ_PNP operations
7 *
8 * PROGRAMMERS: Hervé Poussineau (poussine@freesurf.fr)
9 */
10 /* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
11
12 #define INITGUID
13 #define NDEBUG
14 #include "serial.h"
15
16 NTSTATUS STDCALL
17 SerialAddDeviceInternal(
18 IN PDRIVER_OBJECT DriverObject,
19 IN PDEVICE_OBJECT Pdo,
20 IN UART_TYPE UartType,
21 IN PULONG pComPortNumber OPTIONAL,
22 OUT PDEVICE_OBJECT* pFdo OPTIONAL)
23 {
24 PDEVICE_OBJECT Fdo = NULL;
25 PSERIAL_DEVICE_EXTENSION DeviceExtension = NULL;
26 NTSTATUS Status;
27 WCHAR DeviceNameBuffer[32];
28 UNICODE_STRING DeviceName;
29 //UNICODE_STRING SymbolicLinkName;
30 static ULONG DeviceNumber = 0;
31 static ULONG ComPortNumber = 1;
32
33 DPRINT("Serial: SerialAddDeviceInternal called\n");
34
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),
40 &DeviceName,
41 FILE_DEVICE_SERIAL_PORT,
42 FILE_DEVICE_SECURE_OPEN,
43 FALSE,
44 &Fdo);
45 if (!NT_SUCCESS(Status))
46 {
47 DPRINT("Serial: IoCreateDevice() failed with status 0x%08x\n", Status);
48 Fdo = NULL;
49 goto ByeBye;
50 }
51 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
52 RtlZeroMemory(DeviceExtension, sizeof(SERIAL_DEVICE_EXTENSION));
53
54 /* Register device interface */
55 #if 0 /* FIXME: activate */
56 Status = IoRegisterDeviceInterface(Pdo, &GUID_DEVINTERFACE_COMPORT, NULL, &SymbolicLinkName);
57 if (!NT_SUCCESS(Status))
58 {
59 DPRINT("Serial: IoRegisterDeviceInterface() failed with status 0x%08x\n", Status);
60 goto ByeBye;
61 }
62 DPRINT1("Serial: IoRegisterDeviceInterface() returned '%wZ'\n", &SymbolicLinkName);
63 Status = IoSetDeviceInterfaceState(&SymbolicLinkName, TRUE);
64 if (!NT_SUCCESS(Status))
65 {
66 DPRINT("Serial: IoSetDeviceInterfaceState() failed with status 0x%08x\n", Status);
67 goto ByeBye;
68 }
69 RtlFreeUnicodeString(&SymbolicLinkName);
70 #endif
71
72 DeviceExtension->SerialPortNumber = DeviceNumber++;
73 if (pComPortNumber == NULL)
74 DeviceExtension->ComPort = ComPortNumber++;
75 else
76 DeviceExtension->ComPort = *pComPortNumber;
77 DeviceExtension->Pdo = Pdo;
78 DeviceExtension->PnpState = dsStopped;
79 DeviceExtension->UartType = UartType;
80 Status = InitializeCircularBuffer(&DeviceExtension->InputBuffer, 16);
81 if (!NT_SUCCESS(Status)) goto ByeBye;
82 Status = InitializeCircularBuffer(&DeviceExtension->OutputBuffer, 16);
83 if (!NT_SUCCESS(Status)) goto ByeBye;
84 IoInitializeRemoveLock(&DeviceExtension->RemoveLock, SERIAL_TAG, 0, 0);
85 KeInitializeSpinLock(&DeviceExtension->InputBufferLock);
86 KeInitializeSpinLock(&DeviceExtension->OutputBufferLock);
87 KeInitializeEvent(&DeviceExtension->InputBufferNotEmpty, NotificationEvent, FALSE);
88 KeInitializeDpc(&DeviceExtension->ReceivedByteDpc, SerialReceiveByte, DeviceExtension);
89 KeInitializeDpc(&DeviceExtension->SendByteDpc, SerialSendByte, DeviceExtension);
90 Fdo->Flags |= DO_POWER_PAGABLE;
91 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
92 if (!NT_SUCCESS(Status))
93 {
94 DPRINT("Serial: IoAttachDeviceToDeviceStackSafe() failed with status 0x%08x\n", Status);
95 goto ByeBye;
96 }
97 Fdo->Flags |= DO_BUFFERED_IO;
98 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
99 if (pFdo)
100 {
101 *pFdo = Fdo;
102 }
103
104 return STATUS_SUCCESS;
105
106 ByeBye:
107 if (Fdo)
108 {
109 FreeCircularBuffer(&DeviceExtension->InputBuffer);
110 FreeCircularBuffer(&DeviceExtension->OutputBuffer);
111 IoDeleteDevice(Fdo);
112 }
113 return Status;
114 }
115
116 NTSTATUS STDCALL
117 SerialAddDevice(
118 IN PDRIVER_OBJECT DriverObject,
119 IN PDEVICE_OBJECT Pdo)
120 {
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.
124 */
125 if (Pdo == NULL)
126 return STATUS_SUCCESS;
127
128 /* We have here a PDO that does not correspond to a legacy
129 * serial port. So call the internal AddDevice function.
130 */
131 return SerialAddDeviceInternal(DriverObject, Pdo, UartUnknown, NULL, NULL);
132
133
134 }
135
136 NTSTATUS STDCALL
137 SerialPnpStartDevice(
138 IN PDEVICE_OBJECT DeviceObject,
139 IN PCM_RESOURCE_LIST ResourceList)
140 {
141 PSERIAL_DEVICE_EXTENSION DeviceExtension;
142 WCHAR DeviceNameBuffer[32];
143 UNICODE_STRING DeviceName;
144 WCHAR LinkNameBuffer[32];
145 UNICODE_STRING LinkName;
146 WCHAR ComPortBuffer[32];
147 UNICODE_STRING ComPort;
148 ULONG Vector = 0;
149 ULONG i, j;
150 UCHAR IER;
151 KIRQL Dirql;
152 KAFFINITY Affinity = 0;
153 KINTERRUPT_MODE InterruptMode = Latched;
154 BOOLEAN ShareInterrupt = TRUE;
155 OBJECT_ATTRIBUTES objectAttributes;
156 PUCHAR ComPortBase;
157 UNICODE_STRING KeyName;
158 HANDLE hKey;
159 NTSTATUS Status;
160
161 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
162
163 ASSERT(DeviceExtension->PnpState == dsStopped);
164
165 DeviceExtension->BaudRate = 19200 | SERIAL_BAUD_USER;
166 DeviceExtension->BaseAddress = 0;
167 Dirql = 0;
168 for (i = 0; i < ResourceList->Count; i++)
169 {
170 for (j = 0; j < ResourceList->List[i].PartialResourceList.Count; j++)
171 {
172 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor = &ResourceList->List[i].PartialResourceList.PartialDescriptors[j];
173 switch (PartialDescriptor->Type)
174 {
175 case CmResourceTypePort:
176 if (PartialDescriptor->u.Port.Length < 8)
177 return STATUS_INSUFFICIENT_RESOURCES;
178 if (DeviceExtension->BaseAddress != 0)
179 return STATUS_UNSUCCESSFUL;
180 DeviceExtension->BaseAddress = PartialDescriptor->u.Port.Start.u.LowPart;
181 break;
182 case CmResourceTypeInterrupt:
183 if (Dirql != 0)
184 return STATUS_UNSUCCESSFUL;
185 Dirql = (KIRQL)PartialDescriptor->u.Interrupt.Level;
186 Vector = PartialDescriptor->u.Interrupt.Vector;
187 Affinity = PartialDescriptor->u.Interrupt.Affinity;
188 if (PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
189 InterruptMode = Latched;
190 else
191 InterruptMode = LevelSensitive;
192 ShareInterrupt = (PartialDescriptor->ShareDisposition == CmResourceShareShared);
193 break;
194 }
195 }
196 }
197 DPRINT("Serial: New COM port. Base = 0x%lx, Irql = %u\n",
198 DeviceExtension->BaseAddress, Dirql);
199 if (!DeviceExtension->BaseAddress)
200 return STATUS_INSUFFICIENT_RESOURCES;
201 if (!Dirql)
202 return STATUS_INSUFFICIENT_RESOURCES;
203 ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
204
205 if (DeviceExtension->UartType == UartUnknown)
206 DeviceExtension->UartType = SerialDetectUartType(ComPortBase);
207
208 /* Get current settings */
209 DeviceExtension->MCR = READ_PORT_UCHAR(SER_MCR(ComPortBase));
210 DeviceExtension->MSR = READ_PORT_UCHAR(SER_MSR(ComPortBase));
211 DeviceExtension->WaitMask = 0;
212
213 /* Set baud rate */
214 Status = SerialSetBaudRate(DeviceExtension, DeviceExtension->BaudRate);
215 if (!NT_SUCCESS(Status))
216 {
217 DPRINT("Serial: SerialSetBaudRate() failed with status 0x%08x\n", Status);
218 return Status;
219 }
220
221 /* Set line control */
222 DeviceExtension->SerialLineControl.StopBits = STOP_BIT_1;
223 DeviceExtension->SerialLineControl.Parity = NO_PARITY;
224 DeviceExtension->SerialLineControl.WordLength = 8;
225 Status = SerialSetLineControl(DeviceExtension, &DeviceExtension->SerialLineControl);
226 if (!NT_SUCCESS(Status))
227 {
228 DPRINT("Serial: SerialSetLineControl() failed with status 0x%08x\n", Status);
229 return Status;
230 }
231
232 /* Clear receive/transmit buffers */
233 if (DeviceExtension->UartType >= Uart16550A)
234 {
235 /* 16550 UARTs also have FIFO queues, but they are unusable due to a bug */
236 WRITE_PORT_UCHAR(SER_FCR(ComPortBase),
237 SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
238 }
239
240 /* Create link \DosDevices\COMX -> \Device\SerialX */
241 swprintf(DeviceNameBuffer, L"\\Device\\Serial%lu", DeviceExtension->SerialPortNumber);
242 swprintf(LinkNameBuffer, L"\\DosDevices\\COM%lu", DeviceExtension->ComPort);
243 swprintf(ComPortBuffer, L"COM%lu", DeviceExtension->ComPort);
244 RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
245 RtlInitUnicodeString(&LinkName, LinkNameBuffer);
246 RtlInitUnicodeString(&ComPort, ComPortBuffer);
247 Status = IoCreateSymbolicLink(&LinkName, &DeviceName);
248 if (!NT_SUCCESS(Status))
249 {
250 DPRINT("Serial: IoCreateSymbolicLink() failed with status 0x%08x\n", Status);
251 return Status;
252 }
253
254 /* Connect interrupt and enable them */
255 Status = IoConnectInterrupt(
256 &DeviceExtension->Interrupt, SerialInterruptService,
257 DeviceObject, NULL,
258 Vector, Dirql, Dirql,
259 InterruptMode, ShareInterrupt,
260 Affinity, FALSE);
261 if (!NT_SUCCESS(Status))
262 {
263 DPRINT("Serial: IoConnectInterrupt() failed with status 0x%08x\n", Status);
264 IoDeleteSymbolicLink(&LinkName);
265 return Status;
266 }
267
268 /* Write an entry value under HKLM\HARDWARE\DeviceMap\SERIALCOMM */
269 /* This step is not mandatory, so don't exit in case of error */
270 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\HARDWARE\\DeviceMap\\SERIALCOMM");
271 InitializeObjectAttributes(&objectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
272 Status = ZwCreateKey(&hKey, KEY_SET_VALUE, &objectAttributes, 0, NULL, REG_OPTION_VOLATILE, NULL);
273 if (NT_SUCCESS(Status))
274 {
275 /* Key = \Device\Serialx, Value = COMx */
276 ZwSetValueKey(hKey, &DeviceName, 0, REG_SZ, &ComPortBuffer, ComPort.Length + sizeof(WCHAR));
277 ZwClose(hKey);
278 }
279
280 DeviceExtension->PnpState = dsStarted;
281
282 /* Activate interrupt modes */
283 IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
284 IER |= SR_IER_DATA_RECEIVED | SR_IER_THR_EMPTY | SR_IER_LSR_CHANGE | SR_IER_MSR_CHANGE;
285 WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER);
286
287 /* Activate DTR, RTS */
288 DeviceExtension->MCR |= SR_MCR_DTR | SR_MCR_RTS;
289 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
290
291 return STATUS_SUCCESS;
292 }
293
294 NTSTATUS STDCALL
295 SerialPnp(
296 IN PDEVICE_OBJECT DeviceObject,
297 IN PIRP Irp)
298 {
299 ULONG MinorFunction;
300 PIO_STACK_LOCATION Stack;
301 ULONG_PTR Information = 0;
302 NTSTATUS Status;
303
304 Stack = IoGetCurrentIrpStackLocation(Irp);
305 MinorFunction = Stack->MinorFunction;
306
307 switch (MinorFunction)
308 {
309 case IRP_MN_START_DEVICE:
310 {
311 BOOLEAN ConflictDetected;
312 DPRINT("Serial: IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
313
314 /* FIXME: first HACK: PnP manager can send multiple
315 * IRP_MN_START_DEVICE for one device
316 */
317 if (((PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->PnpState != dsStopped)
318 {
319 DPRINT1("Serial: device already started. Ignoring this irp!\n");
320 Status = STATUS_SUCCESS;
321 break;
322 }
323 /* FIXME: second HACK: verify that we have some allocated resources.
324 * It seems not to be always the case on some hardware
325 */
326 if (Stack->Parameters.StartDevice.AllocatedResources == NULL)
327 {
328 DPRINT1("Serial: no allocated resources. Can't start COM%lu\n",
329 ((PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ComPort);
330 Status = STATUS_INSUFFICIENT_RESOURCES;
331 break;
332 }
333 /* FIXME: third HACK: verify that we don't have resource conflict,
334 * because PnP manager doesn't do it automatically
335 */
336 Status = IoReportResourceForDetection(
337 DeviceObject->DriverObject, Stack->Parameters.StartDevice.AllocatedResources, 0,
338 NULL, NULL, 0,
339 &ConflictDetected);
340 if (!NT_SUCCESS(Status))
341 {
342 Irp->IoStatus.Information = 0;
343 Irp->IoStatus.Status = Status;
344 IoCompleteRequest(Irp, IO_NO_INCREMENT);
345 return Status;
346 }
347
348 /* Call lower driver */
349 Status = ForwardIrpAndWait(DeviceObject, Irp);
350 if (NT_SUCCESS(Status))
351 Status = SerialPnpStartDevice(
352 DeviceObject,
353 Stack->Parameters.StartDevice.AllocatedResources);
354 break;
355 }
356 /* IRP_MN_QUERY_STOP_DEVICE (FIXME: required) */
357 /* IRP_MN_STOP_DEVICE (FIXME: required) */
358 /* IRP_MN_CANCEL_STOP_DEVICE (FIXME: required) */
359 /* IRP_MN_QUERY_REMOVE_DEVICE (FIXME: required) */
360 /* case IRP_MN_REMOVE_DEVICE (FIXME: required) */
361 /*{
362 DPRINT("Serial: IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
363 IoAcquireRemoveLock
364 IoReleaseRemoveLockAndWait
365 pass request to DeviceExtension-LowerDriver
366 IoDeleteDevice(Fdo) and/or IoDetachDevice
367 break;
368 }*/
369 /* IRP_MN_CANCEL_REMOVE_DEVICE (FIXME: required) */
370 /* IRP_MN_SURPRISE_REMOVAL (FIXME: required) */
371 /* IRP_MN_QUERY_CAPABILITIES (optional) */
372 /* IRP_MN_QUERY_PNP_DEVICE_STATE (optional) */
373 /* IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) */
374 /* IRP_MN_DEVICE_USAGE_NOTIFICATION (FIXME: required or optional ???) */
375 /* IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) */
376 /* IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) */
377 /* IRP_MN_QUERY_INTERFACE (optional) */
378 default:
379 {
380 DPRINT1("Serial: unknown minor function 0x%x\n", MinorFunction);
381 return ForwardIrpAndForget(DeviceObject, Irp);
382 }
383 }
384
385 Irp->IoStatus.Information = Information;
386 Irp->IoStatus.Status = Status;
387 IoCompleteRequest(Irp, IO_NO_INCREMENT);
388 return Status;
389 }