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