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