this waring break the head for me
[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 OUT PDEVICE_OBJECT* pFdo OPTIONAL)
21 {
22 PDEVICE_OBJECT Fdo = NULL;
23 PSERIAL_DEVICE_EXTENSION DeviceExtension = NULL;
24 NTSTATUS Status;
25 WCHAR DeviceNameBuffer[32];
26 UNICODE_STRING DeviceName;
27 //UNICODE_STRING SymbolicLinkName;
28 static ULONG DeviceNumber = 0;
29
30 DPRINT("Serial: SerialAddDeviceInternal called\n");
31
32 /* Create new device object */
33 swprintf(DeviceNameBuffer, L"\\Device\\Serial%lu", DeviceNumber);
34 RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
35 Status = IoCreateDevice(DriverObject,
36 sizeof(SERIAL_DEVICE_EXTENSION),
37 &DeviceName,
38 FILE_DEVICE_SERIAL_PORT,
39 FILE_DEVICE_SECURE_OPEN,
40 FALSE,
41 &Fdo);
42 if (!NT_SUCCESS(Status))
43 {
44 DPRINT("Serial: IoCreateDevice() failed with status 0x%08x\n", Status);
45 Fdo = NULL;
46 goto ByeBye;
47 }
48 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)Fdo->DeviceExtension;
49 RtlZeroMemory(DeviceExtension, sizeof(SERIAL_DEVICE_EXTENSION));
50
51 /* Register device interface */
52 #if 0 /* FIXME: activate */
53 Status = IoRegisterDeviceInterface(Pdo, &GUID_DEVINTERFACE_COMPORT, NULL, &SymbolicLinkName);
54 if (!NT_SUCCESS(Status))
55 {
56 DPRINT("Serial: IoRegisterDeviceInterface() failed with status 0x%08x\n", Status);
57 goto ByeBye;
58 }
59 DPRINT1("Serial: IoRegisterDeviceInterface() returned '%wZ'\n", &SymbolicLinkName);
60 Status = IoSetDeviceInterfaceState(&SymbolicLinkName, TRUE);
61 if (!NT_SUCCESS(Status))
62 {
63 DPRINT("Serial: IoSetDeviceInterfaceState() failed with status 0x%08x\n", Status);
64 goto ByeBye;
65 }
66 RtlFreeUnicodeString(&SymbolicLinkName);
67 #endif
68
69 DeviceExtension->SerialPortNumber = DeviceNumber++;
70 DeviceExtension->Pdo = Pdo;
71 DeviceExtension->PnpState = dsStopped;
72 Status = InitializeCircularBuffer(&DeviceExtension->InputBuffer, 16);
73 if (!NT_SUCCESS(Status)) goto ByeBye;
74 Status = InitializeCircularBuffer(&DeviceExtension->OutputBuffer, 16);
75 if (!NT_SUCCESS(Status)) goto ByeBye;
76 IoInitializeRemoveLock(&DeviceExtension->RemoveLock, SERIAL_TAG, 0, 0);
77 //Fdo->Flags |= DO_POWER_PAGEABLE (or DO_POWER_INRUSH?)
78 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
79 if (!NT_SUCCESS(Status))
80 {
81 DPRINT("Serial: IoAttachDeviceToDeviceStackSafe() failed with status 0x%08x\n", Status);
82 goto ByeBye;
83 }
84 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
85 if (pFdo)
86 {
87 *pFdo = Fdo;
88 }
89
90 return STATUS_SUCCESS;
91
92 ByeBye:
93 if (Fdo)
94 {
95 FreeCircularBuffer(&DeviceExtension->InputBuffer);
96 FreeCircularBuffer(&DeviceExtension->OutputBuffer);
97 IoDeleteDevice(Fdo);
98 }
99 return Status;
100 }
101
102 NTSTATUS STDCALL
103 SerialAddDevice(
104 IN PDRIVER_OBJECT DriverObject,
105 IN PDEVICE_OBJECT Pdo)
106 {
107 /* Serial.sys is a legacy driver. AddDevice is called once
108 * with a NULL Pdo just after the driver initialization.
109 * Detect this case and return success.
110 */
111 if (Pdo == NULL)
112 return STATUS_SUCCESS;
113
114 /* We have here a PDO that does not correspond to a legacy
115 * serial port. So call the internal AddDevice function.
116 */
117 DPRINT1("Serial: SerialAddDevice() called. Pdo 0x%p (should be NULL)\n", Pdo);
118 /* FIXME: due to a bug, previously described AddDevice is
119 * not called with a NULL Pdo. Block this call (blocks
120 * unfortunately all the other PnP serial ports devices).
121 */
122 //return SerialAddDeviceInternal(DriverObject, Pdo, NULL);
123 return STATUS_UNSUCCESSFUL;
124 }
125
126 NTSTATUS STDCALL
127 SerialPnpStartDevice(
128 IN PDEVICE_OBJECT DeviceObject,
129 IN PCM_RESOURCE_LIST ResourceList)
130 {
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;
138 ULONG Vector = 0;
139 ULONG i, j;
140 KIRQL Dirql;
141 KAFFINITY Affinity = 0;
142 KINTERRUPT_MODE InterruptMode = Latched;
143 BOOLEAN ShareInterrupt = TRUE;
144 OBJECT_ATTRIBUTES objectAttributes;
145 UNICODE_STRING KeyName;
146 HANDLE hKey;
147 NTSTATUS Status;
148
149 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
150
151 /* FIXME: actually, IRP_MN_START_DEVICE is sent twice to each serial device:
152 * - one when loading serial.sys
153 * - one when loading attached upper filter serenum.sys
154 * This behaviour MUST NOT exist.
155 * As PnP handling isn't right anyway, I didn't search how to correct this.
156 */
157 if (DeviceExtension->PnpState == dsStarted) return STATUS_SUCCESS;
158
159 DeviceExtension->ComPort = DeviceExtension->SerialPortNumber + 1;
160 DeviceExtension->BaudRate = 19200 | SERIAL_BAUD_USER;
161 DeviceExtension->BaseAddress = 0;
162 Dirql = 0;
163 for (i = 0; i < ResourceList->Count; i++)
164 {
165 for (j = 0; j < ResourceList->List[i].PartialResourceList.Count; j++)
166 {
167 PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor = &ResourceList->List[i].PartialResourceList.PartialDescriptors[j];
168 switch (PartialDescriptor->Type)
169 {
170 case CmResourceTypePort:
171 if (PartialDescriptor->u.Port.Length < 8)
172 return STATUS_INSUFFICIENT_RESOURCES;
173 if (DeviceExtension->BaseAddress != 0)
174 return STATUS_UNSUCCESSFUL;
175 DeviceExtension->BaseAddress = PartialDescriptor->u.Port.Start.u.LowPart;
176 break;
177 case CmResourceTypeInterrupt:
178 if (Dirql != 0)
179 return STATUS_UNSUCCESSFUL;
180 Dirql = (KIRQL)PartialDescriptor->u.Interrupt.Level;
181 Vector = PartialDescriptor->u.Interrupt.Vector;
182 Affinity = PartialDescriptor->u.Interrupt.Affinity;
183 if (PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
184 InterruptMode = Latched;
185 else
186 InterruptMode = LevelSensitive;
187 ShareInterrupt = (PartialDescriptor->ShareDisposition == CmResourceShareShared);
188 break;
189 }
190 }
191 }
192 DPRINT("Serial: New COM port. Base = 0x%lx, Irql = %u\n",
193 DeviceExtension->BaseAddress, Dirql);
194 if (!DeviceExtension->BaseAddress)
195 return STATUS_INSUFFICIENT_RESOURCES;
196 /* FIXME: we should be able to continue and use polling method
197 * for read/write if we don't have an interrupt */
198 if (!Dirql)
199 return STATUS_INSUFFICIENT_RESOURCES;
200
201 /* Get current settings */
202 DeviceExtension->IER = READ_PORT_UCHAR(SER_IER((PUCHAR)DeviceExtension->BaseAddress));
203 DeviceExtension->MCR = READ_PORT_UCHAR(SER_MCR((PUCHAR)DeviceExtension->BaseAddress));
204 DeviceExtension->MSR = READ_PORT_UCHAR(SER_MSR((PUCHAR)DeviceExtension->BaseAddress));
205 DeviceExtension->WaitMask = 0;
206
207 /* Set baud rate */
208 Status = SerialSetBaudRate(DeviceExtension, DeviceExtension->BaudRate);
209 if (!NT_SUCCESS(Status))
210 {
211 DPRINT("Serial: SerialSetBaudRate() failed with status 0x%08x\n", Status);
212 return Status;
213 }
214
215 /* Set line control */
216 DeviceExtension->SerialLineControl.StopBits = STOP_BIT_1;
217 DeviceExtension->SerialLineControl.Parity = NO_PARITY;
218 DeviceExtension->SerialLineControl.WordLength = 8;
219 Status = SerialSetLineControl(DeviceExtension, &DeviceExtension->SerialLineControl);
220 if (!NT_SUCCESS(Status))
221 {
222 DPRINT("Serial: SerialSetLineControl() failed with status 0x%08x\n", Status);
223 return Status;
224 }
225
226 /* Create link \DosDevices\COMX -> \Device\SerialX */
227 swprintf(DeviceNameBuffer, L"\\Device\\Serial%lu", DeviceExtension->SerialPortNumber);
228 swprintf(LinkNameBuffer, L"\\DosDevices\\COM%lu", DeviceExtension->ComPort);
229 swprintf(ComPortBuffer, L"COM%lu", DeviceExtension->ComPort);
230 RtlInitUnicodeString(&DeviceName, DeviceNameBuffer);
231 RtlInitUnicodeString(&LinkName, LinkNameBuffer);
232 RtlInitUnicodeString(&ComPort, ComPortBuffer);
233 Status = IoCreateSymbolicLink(&LinkName, &DeviceName);
234 if (!NT_SUCCESS(Status))
235 {
236 DPRINT("Serial: IoCreateSymbolicLink() failed with status 0x%08x\n", Status);
237 return Status;
238 }
239
240 /* Connect interrupt and enable them */
241 Status = IoConnectInterrupt(
242 &DeviceExtension->Interrupt, SerialInterruptService,
243 DeviceObject, NULL,
244 Vector, Dirql, Dirql,
245 InterruptMode, ShareInterrupt,
246 Affinity, FALSE);
247 if (!NT_SUCCESS(Status))
248 {
249 DPRINT("Serial: IoConnectInterrupt() failed with status 0x%08x\n", Status);
250 IoDeleteSymbolicLink(&LinkName);
251 return Status;
252 }
253
254 /* Write an entry value under HKLM\HARDWARE\DeviceMap\SERIALCOMM */
255 /* This step is not mandatory, so don't exit in case of error */
256 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\HARDWARE\\DeviceMap\\SERIALCOMM");
257 InitializeObjectAttributes(&objectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL);
258 Status = ZwCreateKey(&hKey, KEY_SET_VALUE, &objectAttributes, 0, NULL, REG_OPTION_VOLATILE, NULL);
259 if (NT_SUCCESS(Status))
260 {
261 /* Key = \Device\Serialx, Value = COMx */
262 ZwSetValueKey(hKey, &DeviceName, 0, REG_SZ, &ComPortBuffer, ComPort.Length + sizeof(WCHAR));
263 ZwClose(hKey);
264 }
265
266 DeviceExtension->PnpState = dsStarted;
267
268 DeviceExtension->IER |= 0x1f; /* Activate interrupt mode */
269 DeviceExtension->IER &= ~1; /* FIXME: Disable receive byte interrupt */
270 WRITE_PORT_UCHAR(SER_IER((PUCHAR)DeviceExtension->BaseAddress), DeviceExtension->IER);
271
272 DeviceExtension->MCR |= 0x03; /* Activate DTR, RTS */
273 WRITE_PORT_UCHAR(SER_MCR((PUCHAR)DeviceExtension->BaseAddress), DeviceExtension->MCR);
274
275 return STATUS_SUCCESS;
276 }
277
278 NTSTATUS STDCALL
279 SerialPnp(
280 IN PDEVICE_OBJECT DeviceObject,
281 IN PIRP Irp)
282 {
283 ULONG MinorFunction;
284 PIO_STACK_LOCATION Stack;
285 ULONG Information = 0;
286 NTSTATUS Status;
287
288 Stack = IoGetCurrentIrpStackLocation(Irp);
289 MinorFunction = Stack->MinorFunction;
290
291 switch (MinorFunction)
292 {
293 case IRP_MN_START_DEVICE:
294 {
295 DPRINT("Serial: IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
296
297 /* Call lower driver */
298 Status = ForwardIrpAndWait(DeviceObject, Irp);
299 if (NT_SUCCESS(Status))
300 Status = SerialPnpStartDevice(
301 DeviceObject,
302 Stack->Parameters.StartDevice.AllocatedResources);
303 break;
304 }
305 /* IRP_MN_QUERY_STOP_DEVICE (FIXME: required) */
306 /* IRP_MN_STOP_DEVICE (FIXME: required) */
307 /* IRP_MN_CANCEL_STOP_DEVICE (FIXME: required) */
308 /* IRP_MN_QUERY_REMOVE_DEVICE (FIXME: required) */
309 /* case IRP_MN_REMOVE_DEVICE (FIXME: required) */
310 /*{
311 DPRINT("Serial: IRP_MJ_PNP / IRP_MN_REMOVE_DEVICE\n");
312 IoAcquireRemoveLock
313 IoReleaseRemoveLockAndWait
314 pass request to DeviceExtension-LowerDriver
315 IoDeleteDevice(Fdo) and/or IoDetachDevice
316 break;
317 }*/
318 /* IRP_MN_CANCEL_REMOVE_DEVICE (FIXME: required) */
319 /* IRP_MN_SURPRISE_REMOVAL (FIXME: required) */
320 /* IRP_MN_QUERY_CAPABILITIES (optional) */
321 /* IRP_MN_QUERY_PNP_DEVICE_STATE (optional) */
322 /* IRP_MN_FILTER_RESOURCE_REQUIREMENTS (optional) */
323 /* IRP_MN_DEVICE_USAGE_NOTIFICATION (FIXME: required or optional ???) */
324 /* IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations (optional) */
325 /* IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations (optional) */
326 /* IRP_MN_QUERY_INTERFACE (optional) */
327 default:
328 {
329 DPRINT1("Serial: unknown minor function 0x%x\n", MinorFunction);
330 return ForwardIrpAndForget(DeviceObject, Irp);
331 }
332 }
333
334 Irp->IoStatus.Information = Information;
335 Irp->IoStatus.Status = Status;
336 IoCompleteRequest(Irp, IO_NO_INCREMENT);
337 return Status;
338 }