sync with trunk r46493
[reactos.git] / drivers / serial / serial / devctrl.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: Serial port driver
4 * FILE: drivers/dd/serial/devctrl.c
5 * PURPOSE: Serial IRP_MJ_DEVICE_CONTROL operations
6 *
7 * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
8 */
9
10 #include "serial.h"
11
12 #define IO_METHOD_FROM_CTL_CODE(ctlCode) (ctlCode&0x00000003)
13
14 static VOID
15 SerialGetUserBuffers(
16 IN PIRP Irp,
17 IN ULONG IoControlCode,
18 OUT PVOID* BufferIn,
19 OUT PVOID* BufferOut)
20 {
21 ASSERT(Irp);
22 ASSERT(BufferIn);
23 ASSERT(BufferOut);
24
25 switch (IO_METHOD_FROM_CTL_CODE(IoControlCode))
26 {
27 case METHOD_BUFFERED:
28 *BufferIn = *BufferOut = Irp->AssociatedIrp.SystemBuffer;
29 break;
30 case METHOD_IN_DIRECT:
31 case METHOD_OUT_DIRECT:
32 *BufferIn = Irp->AssociatedIrp.SystemBuffer;
33 *BufferOut = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
34 break;
35 case METHOD_NEITHER:
36 *BufferIn = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.Type3InputBuffer;
37 *BufferOut = Irp->UserBuffer;
38 break;
39 default:
40 /* Should never happen */
41 *BufferIn = NULL;
42 *BufferOut = NULL;
43 break;
44 }
45 }
46
47 NTSTATUS NTAPI
48 SerialSetBaudRate(
49 IN PSERIAL_DEVICE_EXTENSION DeviceExtension,
50 IN ULONG NewBaudRate)
51 {
52 ULONG BaudRate;
53 USHORT divisor;
54 PUCHAR ComPortBase = ULongToPtr(DeviceExtension->BaseAddress);
55 NTSTATUS Status = STATUS_SUCCESS;
56
57 if (NewBaudRate == 0)
58 return STATUS_INVALID_PARAMETER;
59
60 divisor = (USHORT)(BAUD_CLOCK / (CLOCKS_PER_BIT * NewBaudRate));
61 BaudRate = BAUD_CLOCK / (CLOCKS_PER_BIT * divisor);
62
63 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
64 if (NT_SUCCESS(Status))
65 {
66 UCHAR Lcr;
67 TRACE_(SERIAL, "SerialSetBaudRate(COM%lu, %lu Bauds)\n", DeviceExtension->ComPort, BaudRate);
68 /* Set Bit 7 of LCR to expose baud registers */
69 Lcr = READ_PORT_UCHAR(SER_LCR(ComPortBase));
70 WRITE_PORT_UCHAR(SER_LCR(ComPortBase), Lcr | SR_LCR_DLAB);
71 /* Write the baud rate */
72 WRITE_PORT_UCHAR(SER_DLL(ComPortBase), divisor & 0xff);
73 WRITE_PORT_UCHAR(SER_DLM(ComPortBase), divisor >> 8);
74 /* Switch back to normal registers */
75 WRITE_PORT_UCHAR(SER_LCR(ComPortBase), Lcr);
76
77 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
78 }
79
80 if (NT_SUCCESS(Status))
81 DeviceExtension->BaudRate = BaudRate;
82 return Status;
83 }
84
85 NTSTATUS NTAPI
86 SerialSetLineControl(
87 IN PSERIAL_DEVICE_EXTENSION DeviceExtension,
88 IN PSERIAL_LINE_CONTROL NewSettings)
89 {
90 PUCHAR ComPortBase;
91 UCHAR Lcr = 0;
92 NTSTATUS Status;
93
94 ASSERT(DeviceExtension);
95 ASSERT(NewSettings);
96
97 TRACE_(SERIAL, "SerialSetLineControl(COM%lu, Settings { %lu %lu %lu })\n",
98 DeviceExtension->ComPort, NewSettings->StopBits, NewSettings->Parity, NewSettings->WordLength);
99
100 /* Verify parameters */
101 switch (NewSettings->WordLength)
102 {
103 case 5: Lcr |= SR_LCR_CS5; break;
104 case 6: Lcr |= SR_LCR_CS6; break;
105 case 7: Lcr |= SR_LCR_CS7; break;
106 case 8: Lcr |= SR_LCR_CS8; break;
107 default: return STATUS_INVALID_PARAMETER;
108 }
109
110 if (NewSettings->WordLength < 5 || NewSettings->WordLength > 8)
111 return STATUS_INVALID_PARAMETER;
112
113 switch (NewSettings->Parity)
114 {
115 case NO_PARITY: Lcr |= SR_LCR_PNO; break;
116 case ODD_PARITY: Lcr |= SR_LCR_POD; break;
117 case EVEN_PARITY: Lcr |= SR_LCR_PEV; break;
118 case MARK_PARITY: Lcr |= SR_LCR_PMK; break;
119 case SPACE_PARITY: Lcr |= SR_LCR_PSP; break;
120 default: return STATUS_INVALID_PARAMETER;
121 }
122
123 switch (NewSettings->StopBits)
124 {
125 case STOP_BIT_1:
126 Lcr |= SR_LCR_ST1;
127 break;
128 case STOP_BITS_1_5:
129 if (NewSettings->WordLength != 5)
130 return STATUS_INVALID_PARAMETER;
131 Lcr |= SR_LCR_ST2;
132 break;
133 case STOP_BITS_2:
134 if (NewSettings->WordLength < 6 || NewSettings->WordLength > 8)
135 return STATUS_INVALID_PARAMETER;
136 Lcr |= SR_LCR_ST2;
137 break;
138 default:
139 return STATUS_INVALID_PARAMETER;
140 }
141
142 /* Update current parameters */
143 ComPortBase = ULongToPtr(DeviceExtension->BaseAddress);
144 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
145 if (!NT_SUCCESS(Status))
146 return Status;
147 WRITE_PORT_UCHAR(SER_LCR(ComPortBase), Lcr);
148
149 /* Read junk out of RBR */
150 READ_PORT_UCHAR(SER_RBR(ComPortBase));
151 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
152
153 if (NT_SUCCESS(Status))
154 DeviceExtension->SerialLineControl = *NewSettings;
155
156 return Status;
157 }
158
159 static BOOLEAN
160 SerialClearPerfStats(
161 IN PSERIAL_DEVICE_EXTENSION DeviceExtension)
162 {
163 ASSERT(DeviceExtension);
164
165 RtlZeroMemory(&DeviceExtension->SerialPerfStats, sizeof(SERIALPERF_STATS));
166 DeviceExtension->BreakInterruptErrorCount = 0;
167 return TRUE;
168 }
169
170 static BOOLEAN
171 SerialGetPerfStats(IN PIRP pIrp)
172 {
173 PSERIAL_DEVICE_EXTENSION pDeviceExtension;
174
175 ASSERT(pIrp);
176 pDeviceExtension = (PSERIAL_DEVICE_EXTENSION)
177 IoGetCurrentIrpStackLocation(pIrp)->DeviceObject->DeviceExtension;
178
179 /*
180 * we assume buffer is big enough to hold SerialPerfStats structure
181 * caller must verify this
182 */
183 RtlCopyMemory(
184 pIrp->AssociatedIrp.SystemBuffer,
185 &pDeviceExtension->SerialPerfStats,
186 sizeof(SERIALPERF_STATS)
187 );
188 return TRUE;
189 }
190
191 static NTSTATUS
192 SerialGetCommProp(
193 OUT PSERIAL_COMMPROP pCommProp,
194 IN PSERIAL_DEVICE_EXTENSION DeviceExtension)
195 {
196 ASSERT(pCommProp);
197
198 RtlZeroMemory(pCommProp, sizeof(SERIAL_COMMPROP));
199
200 if (!(pCommProp->ProvSpec1 & COMMPROP_INITIALIZED))
201 pCommProp->PacketLength = sizeof(SERIAL_COMMPROP);
202 pCommProp->PacketVersion = 2;
203 pCommProp->ServiceMask = SERIAL_SP_SERIALCOMM;
204 pCommProp->MaxTxQueue = pCommProp->CurrentTxQueue = DeviceExtension->OutputBuffer.Length - 1;
205 pCommProp->MaxRxQueue = pCommProp->CurrentRxQueue = DeviceExtension->InputBuffer.Length - 1;
206 pCommProp->ProvSubType = PST_RS232;
207 pCommProp->ProvCapabilities = SERIAL_PCF_DTRDSR | SERIAL_PCF_INTTIMEOUTS | SERIAL_PCF_PARITY_CHECK
208 | SERIAL_PCF_RTSCTS | SERIAL_PCF_SETXCHAR | SERIAL_PCF_SPECIALCHARS | SERIAL_PCF_TOTALTIMEOUTS
209 | SERIAL_PCF_XONXOFF;
210 pCommProp->SettableParams = SERIAL_SP_BAUD | SERIAL_SP_DATABITS | SERIAL_SP_HANDSHAKING
211 | SERIAL_SP_PARITY | SERIAL_SP_PARITY_CHECK | SERIAL_SP_STOPBITS;
212
213 /* SettableBaud is related to Uart type */
214 pCommProp->SettableBaud = SERIAL_BAUD_075 | SERIAL_BAUD_110 | SERIAL_BAUD_134_5
215 | SERIAL_BAUD_150 | SERIAL_BAUD_300 | SERIAL_BAUD_600 | SERIAL_BAUD_1200
216 | SERIAL_BAUD_1800 | SERIAL_BAUD_2400 | SERIAL_BAUD_4800 | SERIAL_BAUD_7200
217 | SERIAL_BAUD_9600 | SERIAL_BAUD_USER;
218 pCommProp->MaxBaud = SERIAL_BAUD_USER;
219 if (DeviceExtension->UartType >= Uart16450)
220 {
221 pCommProp->SettableBaud |= SERIAL_BAUD_14400 | SERIAL_BAUD_19200 | SERIAL_BAUD_38400;
222 }
223 if (DeviceExtension->UartType >= Uart16550)
224 {
225 pCommProp->SettableBaud |= SERIAL_BAUD_56K | SERIAL_BAUD_57600 | SERIAL_BAUD_115200 | SERIAL_BAUD_128K;
226 }
227
228 pCommProp->SettableData = SERIAL_DATABITS_5 | SERIAL_DATABITS_6 | SERIAL_DATABITS_7 | SERIAL_DATABITS_8;
229 pCommProp->SettableStopParity = SERIAL_STOPBITS_10 | SERIAL_STOPBITS_15 | SERIAL_STOPBITS_20
230 | SERIAL_PARITY_NONE | SERIAL_PARITY_ODD | SERIAL_PARITY_EVEN | SERIAL_PARITY_MARK | SERIAL_PARITY_SPACE;
231
232 pCommProp->ProvSpec2 = 0; /* Size of provider-specific data */
233
234 return STATUS_SUCCESS;
235 }
236
237 static NTSTATUS
238 SerialGetCommStatus(
239 OUT PSERIAL_STATUS pSerialStatus,
240 IN PSERIAL_DEVICE_EXTENSION DeviceExtension)
241 {
242 KIRQL Irql;
243
244 ASSERT(pSerialStatus);
245 RtlZeroMemory(pSerialStatus, sizeof(SERIAL_STATUS));
246
247 pSerialStatus->Errors = 0;
248 if (DeviceExtension->BreakInterruptErrorCount)
249 pSerialStatus->Errors |= SERIAL_ERROR_BREAK;
250 if (DeviceExtension->SerialPerfStats.FrameErrorCount)
251 pSerialStatus->Errors |= SERIAL_ERROR_FRAMING;
252 if (DeviceExtension->SerialPerfStats.SerialOverrunErrorCount)
253 pSerialStatus->Errors |= SERIAL_ERROR_OVERRUN;
254 if (DeviceExtension->SerialPerfStats.BufferOverrunErrorCount)
255 pSerialStatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN;
256 if (DeviceExtension->SerialPerfStats.ParityErrorCount)
257 pSerialStatus->Errors |= SERIAL_ERROR_PARITY;
258
259 pSerialStatus->HoldReasons = 0; /* FIXME */
260
261 KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
262 pSerialStatus->AmountInInQueue = (DeviceExtension->InputBuffer.WritePosition + DeviceExtension->InputBuffer.Length
263 - DeviceExtension->InputBuffer.ReadPosition) % DeviceExtension->InputBuffer.Length;
264 KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
265
266 KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
267 pSerialStatus->AmountInOutQueue = (DeviceExtension->OutputBuffer.WritePosition + DeviceExtension->OutputBuffer.Length
268 - DeviceExtension->OutputBuffer.ReadPosition) % DeviceExtension->OutputBuffer.Length;
269 KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
270
271 pSerialStatus->EofReceived = FALSE; /* always FALSE */
272 pSerialStatus->WaitForImmediate = FALSE; /* always FALSE */
273
274 return STATUS_SUCCESS;
275 }
276
277 NTSTATUS NTAPI
278 SerialDeviceControl(
279 IN PDEVICE_OBJECT DeviceObject,
280 IN PIRP Irp)
281 {
282 PIO_STACK_LOCATION Stack;
283 ULONG IoControlCode;
284 PSERIAL_DEVICE_EXTENSION DeviceExtension;
285 ULONG LengthIn, LengthOut;
286 ULONG_PTR Information = 0;
287 PVOID BufferIn, BufferOut;
288 PUCHAR ComPortBase;
289 NTSTATUS Status;
290
291 TRACE_(SERIAL, "IRP_MJ_DEVICE_CONTROL dispatch\n");
292
293 Stack = IoGetCurrentIrpStackLocation(Irp);
294 LengthIn = Stack->Parameters.DeviceIoControl.InputBufferLength;
295 LengthOut = Stack->Parameters.DeviceIoControl.OutputBufferLength;
296 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
297 ComPortBase = ULongToPtr(DeviceExtension->BaseAddress);
298 IoControlCode = Stack->Parameters.DeviceIoControl.IoControlCode;
299 SerialGetUserBuffers(Irp, IoControlCode, &BufferIn, &BufferOut);
300
301 /* FIXME: need to probe buffers */
302 /* FIXME: see http://www.osronline.com/ddkx/serial/serref_61bm.htm */
303 switch (IoControlCode)
304 {
305 case IOCTL_SERIAL_CLEAR_STATS:
306 {
307 TRACE_(SERIAL, "IOCTL_SERIAL_CLEAR_STATS\n");
308 KeSynchronizeExecution(
309 DeviceExtension->Interrupt,
310 (PKSYNCHRONIZE_ROUTINE)SerialClearPerfStats,
311 DeviceExtension);
312 Status = STATUS_SUCCESS;
313 break;
314 }
315 case IOCTL_SERIAL_CLR_DTR:
316 {
317 TRACE_(SERIAL, "IOCTL_SERIAL_CLR_DTR\n");
318 /* FIXME: If the handshake flow control of the device is configured to
319 * automatically use DTR, return STATUS_INVALID_PARAMETER */
320 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
321 if (NT_SUCCESS(Status))
322 {
323 DeviceExtension->MCR &= ~SR_MCR_DTR;
324 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
325 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
326 }
327 break;
328 }
329 case IOCTL_SERIAL_CLR_RTS:
330 {
331 TRACE_(SERIAL, "IOCTL_SERIAL_CLR_RTS\n");
332 /* FIXME: If the handshake flow control of the device is configured to
333 * automatically use RTS, return STATUS_INVALID_PARAMETER */
334 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
335 if (NT_SUCCESS(Status))
336 {
337 DeviceExtension->MCR &= ~SR_MCR_RTS;
338 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
339 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
340 }
341 break;
342 }
343 case IOCTL_SERIAL_CONFIG_SIZE:
344 {
345 /* Obsolete on Microsoft Windows 2000+ */
346 PULONG pConfigSize;
347 TRACE_(SERIAL, "IOCTL_SERIAL_CONFIG_SIZE\n");
348 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
349 Status = STATUS_INVALID_PARAMETER;
350 else
351 {
352 pConfigSize = (PULONG)BufferOut;
353 *pConfigSize = 0;
354 Information = sizeof(ULONG);
355 Status = STATUS_SUCCESS;
356 }
357 break;
358 }
359 case IOCTL_SERIAL_GET_BAUD_RATE:
360 {
361 TRACE_(SERIAL, "IOCTL_SERIAL_GET_BAUD_RATE\n");
362 if (LengthOut < sizeof(SERIAL_BAUD_RATE))
363 Status = STATUS_BUFFER_TOO_SMALL;
364 else if (BufferOut == NULL)
365 Status = STATUS_INVALID_PARAMETER;
366 else
367 {
368 ((PSERIAL_BAUD_RATE)BufferOut)->BaudRate = DeviceExtension->BaudRate;
369 Information = sizeof(SERIAL_BAUD_RATE);
370 Status = STATUS_SUCCESS;
371 }
372 break;
373 }
374 case IOCTL_SERIAL_GET_CHARS:
375 {
376 /* FIXME */
377 ERR_(SERIAL, "IOCTL_SERIAL_GET_CHARS not implemented.\n");
378 Status = STATUS_NOT_IMPLEMENTED;
379 break;
380 }
381 case IOCTL_SERIAL_GET_COMMSTATUS:
382 {
383 TRACE_(SERIAL, "IOCTL_SERIAL_GET_COMMSTATUS\n");
384 if (LengthOut < sizeof(SERIAL_STATUS))
385 Status = STATUS_BUFFER_TOO_SMALL;
386 else if (BufferOut == NULL)
387 Status = STATUS_INVALID_PARAMETER;
388 else
389 {
390 Status = SerialGetCommStatus((PSERIAL_STATUS)BufferOut, DeviceExtension);
391 Information = sizeof(SERIAL_STATUS);
392 }
393 break;
394 }
395 case IOCTL_SERIAL_GET_DTRRTS:
396 {
397 PULONG pDtrRts;
398 TRACE_(SERIAL, "IOCTL_SERIAL_GET_DTRRTS\n");
399 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
400 Status = STATUS_INVALID_PARAMETER;
401 else
402 {
403 pDtrRts = (PULONG)BufferOut;
404 *pDtrRts = 0;
405 if (DeviceExtension->MCR & SR_MCR_DTR)
406 *pDtrRts |= SERIAL_DTR_STATE;
407 if (DeviceExtension->MCR & SR_MCR_RTS)
408 *pDtrRts |= SERIAL_RTS_STATE;
409 Information = sizeof(ULONG);
410 Status = STATUS_SUCCESS;
411 }
412 break;
413 }
414 case IOCTL_SERIAL_GET_HANDFLOW:
415 {
416 /* FIXME */
417 ERR_(SERIAL, "IOCTL_SERIAL_GET_HANDFLOW not implemented.\n");
418 Status = STATUS_NOT_IMPLEMENTED;
419 break;
420 }
421 case IOCTL_SERIAL_GET_LINE_CONTROL:
422 {
423 TRACE_(SERIAL, "IOCTL_SERIAL_GET_LINE_CONTROL\n");
424 if (LengthOut < sizeof(SERIAL_LINE_CONTROL))
425 Status = STATUS_BUFFER_TOO_SMALL;
426 else if (BufferOut == NULL)
427 Status = STATUS_INVALID_PARAMETER;
428 else
429 {
430 *((PSERIAL_LINE_CONTROL)BufferOut) = DeviceExtension->SerialLineControl;
431 Information = sizeof(SERIAL_LINE_CONTROL);
432 Status = STATUS_SUCCESS;
433 }
434 break;
435 }
436 case IOCTL_SERIAL_GET_MODEM_CONTROL:
437 {
438 PULONG pMCR;
439 TRACE_(SERIAL, "IOCTL_SERIAL_GET_MODEM_CONTROL\n");
440 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
441 Status = STATUS_INVALID_PARAMETER;
442 else
443 {
444 pMCR = (PULONG)BufferOut;
445 *pMCR = DeviceExtension->MCR;
446 Information = sizeof(ULONG);
447 Status = STATUS_SUCCESS;
448 }
449 break;
450 }
451 case IOCTL_SERIAL_GET_MODEMSTATUS:
452 {
453 PULONG pMSR;
454 TRACE_(SERIAL, "IOCTL_SERIAL_GET_MODEMSTATUS\n");
455 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
456 Status = STATUS_INVALID_PARAMETER;
457 else
458 {
459 pMSR = (PULONG)BufferOut;
460 *pMSR = DeviceExtension->MSR;
461 Information = sizeof(ULONG);
462 Status = STATUS_SUCCESS;
463 }
464 break;
465 }
466 case IOCTL_SERIAL_GET_PROPERTIES:
467 {
468 TRACE_(SERIAL, "IOCTL_SERIAL_GET_PROPERTIES\n");
469 if (LengthOut < sizeof(SERIAL_COMMPROP))
470 Status = STATUS_BUFFER_TOO_SMALL;
471 else if (BufferOut == NULL)
472 Status = STATUS_INVALID_PARAMETER;
473 else
474 {
475 Status = SerialGetCommProp((PSERIAL_COMMPROP)BufferOut, DeviceExtension);
476 Information = sizeof(SERIAL_COMMPROP);
477 }
478 break;
479 }
480 case IOCTL_SERIAL_GET_STATS:
481 {
482 TRACE_(SERIAL, "IOCTL_SERIAL_GET_STATS\n");
483 if (LengthOut < sizeof(SERIALPERF_STATS))
484 Status = STATUS_BUFFER_TOO_SMALL;
485 else if (BufferOut == NULL)
486 Status = STATUS_INVALID_PARAMETER;
487 else
488 {
489 KeSynchronizeExecution(DeviceExtension->Interrupt,
490 (PKSYNCHRONIZE_ROUTINE)SerialGetPerfStats, Irp);
491 Information = sizeof(SERIALPERF_STATS);
492 Status = STATUS_SUCCESS;
493 }
494 break;
495 }
496 case IOCTL_SERIAL_GET_TIMEOUTS:
497 {
498 TRACE_(SERIAL, "IOCTL_SERIAL_GET_TIMEOUTS\n");
499 if (LengthOut != sizeof(SERIAL_TIMEOUTS) || BufferOut == NULL)
500 Status = STATUS_INVALID_PARAMETER;
501 else
502 {
503 *(PSERIAL_TIMEOUTS)BufferOut = DeviceExtension->SerialTimeOuts;
504 Information = sizeof(SERIAL_TIMEOUTS);
505 Status = STATUS_SUCCESS;
506 }
507 break;
508 }
509 case IOCTL_SERIAL_GET_WAIT_MASK:
510 {
511 PULONG pWaitMask;
512 TRACE_(SERIAL, "IOCTL_SERIAL_GET_WAIT_MASK\n");
513 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
514 Status = STATUS_INVALID_PARAMETER;
515 else
516 {
517 pWaitMask = (PULONG)BufferOut;
518 *pWaitMask = DeviceExtension->WaitMask;
519 Information = sizeof(ULONG);
520 Status = STATUS_SUCCESS;
521 }
522 break;
523 }
524 case IOCTL_SERIAL_IMMEDIATE_CHAR:
525 {
526 /* FIXME */
527 ERR_(SERIAL, "IOCTL_SERIAL_IMMEDIATE_CHAR not implemented.\n");
528 Status = STATUS_NOT_IMPLEMENTED;
529 break;
530 }
531 case IOCTL_SERIAL_LSRMST_INSERT:
532 {
533 /* FIXME */
534 ERR_(SERIAL, "IOCTL_SERIAL_LSRMST_INSERT not implemented.\n");
535 Status = STATUS_NOT_IMPLEMENTED;
536 break;
537 }
538 case IOCTL_SERIAL_PURGE:
539 {
540 KIRQL Irql;
541 TRACE_(SERIAL, "IOCTL_SERIAL_PURGE\n");
542 /* FIXME: SERIAL_PURGE_RXABORT and SERIAL_PURGE_TXABORT
543 * should stop current request */
544 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
545 Status = STATUS_INVALID_PARAMETER;
546 else
547 {
548 ULONG PurgeMask = *(PULONG)BufferIn;
549
550 Status = STATUS_SUCCESS;
551 /* FIXME: use SERIAL_PURGE_RXABORT and SERIAL_PURGE_TXABORT flags */
552 if (PurgeMask & SERIAL_PURGE_RXCLEAR)
553 {
554 KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
555 DeviceExtension->InputBuffer.ReadPosition = DeviceExtension->InputBuffer.WritePosition = 0;
556 if (DeviceExtension->UartType >= Uart16550A)
557 {
558 /* Clear also Uart FIFO */
559 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
560 if (NT_SUCCESS(Status))
561 {
562 WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_RCVR);
563 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
564 }
565 }
566 KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
567 }
568
569 if (PurgeMask & SERIAL_PURGE_TXCLEAR)
570 {
571 KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
572 DeviceExtension->OutputBuffer.ReadPosition = DeviceExtension->OutputBuffer.WritePosition = 0;
573 if (DeviceExtension->UartType >= Uart16550A)
574 {
575 /* Clear also Uart FIFO */
576 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
577 if (NT_SUCCESS(Status))
578 {
579 WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_XMIT);
580 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
581 }
582 }
583 KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
584 }
585 }
586 break;
587 }
588 case IOCTL_SERIAL_RESET_DEVICE:
589 {
590 /* FIXME */
591 ERR_(SERIAL, "IOCTL_SERIAL_RESET_DEVICE not implemented.\n");
592 Status = STATUS_NOT_IMPLEMENTED;
593 break;
594 }
595 case IOCTL_SERIAL_SET_BAUD_RATE:
596 {
597 PULONG pNewBaudRate;
598 TRACE_(SERIAL, "IOCTL_SERIAL_SET_BAUD_RATE\n");
599 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
600 Status = STATUS_INVALID_PARAMETER;
601 else
602 {
603 pNewBaudRate = (PULONG)BufferIn;
604 Status = SerialSetBaudRate(DeviceExtension, *pNewBaudRate);
605 }
606 break;
607 }
608 case IOCTL_SERIAL_SET_BREAK_OFF:
609 {
610 /* FIXME */
611 ERR_(SERIAL, "IOCTL_SERIAL_SET_BREAK_OFF not implemented.\n");
612 Status = STATUS_NOT_IMPLEMENTED;
613 break;
614 }
615 case IOCTL_SERIAL_SET_BREAK_ON:
616 {
617 /* FIXME */
618 ERR_(SERIAL, "IOCTL_SERIAL_SET_BREAK_ON not implemented.\n");
619 Status = STATUS_NOT_IMPLEMENTED;
620 break;
621 }
622 case IOCTL_SERIAL_SET_CHARS:
623 {
624 /* FIXME */
625 ERR_(SERIAL, "IOCTL_SERIAL_SET_CHARS not implemented.\n");
626 Status = STATUS_NOT_IMPLEMENTED;
627 break;
628 }
629 case IOCTL_SERIAL_SET_DTR:
630 {
631 /* FIXME: If the handshake flow control of the device is configured to
632 * automatically use DTR, return STATUS_INVALID_PARAMETER */
633 TRACE_(SERIAL, "IOCTL_SERIAL_SET_DTR\n");
634 if (!(DeviceExtension->MCR & SR_MCR_DTR))
635 {
636 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
637 if (NT_SUCCESS(Status))
638 {
639 DeviceExtension->MCR |= SR_MCR_DTR;
640 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
641 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
642 }
643 }
644 else
645 Status = STATUS_SUCCESS;
646 break;
647 }
648 case IOCTL_SERIAL_SET_FIFO_CONTROL:
649 {
650 TRACE_(SERIAL, "IOCTL_SERIAL_SET_FIFO_CONTROL\n");
651 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
652 Status = STATUS_INVALID_PARAMETER;
653 else
654 {
655 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
656 if (NT_SUCCESS(Status))
657 {
658 WRITE_PORT_UCHAR(SER_FCR(ComPortBase), (UCHAR)((*(PULONG)BufferIn) & 0xff));
659 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
660 }
661 }
662 break;
663 }
664 case IOCTL_SERIAL_SET_HANDFLOW:
665 {
666 /* FIXME */
667 ERR_(SERIAL, "IOCTL_SERIAL_SET_HANDFLOW not implemented.\n");
668 Status = STATUS_NOT_IMPLEMENTED;
669 break;
670 }
671 case IOCTL_SERIAL_SET_LINE_CONTROL:
672 {
673 TRACE_(SERIAL, "IOCTL_SERIAL_SET_LINE_CONTROL\n");
674 if (LengthIn < sizeof(SERIAL_LINE_CONTROL))
675 Status = STATUS_BUFFER_TOO_SMALL;
676 else if (BufferIn == NULL)
677 Status = STATUS_INVALID_PARAMETER;
678 else
679 Status = SerialSetLineControl(DeviceExtension, (PSERIAL_LINE_CONTROL)BufferIn);
680 break;
681 }
682 case IOCTL_SERIAL_SET_MODEM_CONTROL:
683 {
684 PULONG pMCR;
685 TRACE_(SERIAL, "IOCTL_SERIAL_SET_MODEM_CONTROL\n");
686 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
687 Status = STATUS_INVALID_PARAMETER;
688 else
689 {
690 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
691 if (NT_SUCCESS(Status))
692 {
693 pMCR = (PULONG)BufferIn;
694 DeviceExtension->MCR = (UCHAR)(*pMCR & 0xff);
695 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
696 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
697 }
698 }
699 break;
700 }
701 case IOCTL_SERIAL_SET_QUEUE_SIZE:
702 {
703 if (LengthIn < sizeof(SERIAL_QUEUE_SIZE ))
704 return STATUS_BUFFER_TOO_SMALL;
705 else if (BufferIn == NULL)
706 return STATUS_INVALID_PARAMETER;
707 else
708 {
709 KIRQL Irql;
710 PSERIAL_QUEUE_SIZE NewQueueSize = (PSERIAL_QUEUE_SIZE)BufferIn;
711 Status = STATUS_SUCCESS;
712 if (NewQueueSize->InSize > DeviceExtension->InputBuffer.Length)
713 {
714 KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
715 Status = IncreaseCircularBufferSize(&DeviceExtension->InputBuffer, NewQueueSize->InSize);
716 KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
717 }
718 if (NT_SUCCESS(Status) && NewQueueSize->OutSize > DeviceExtension->OutputBuffer.Length)
719 {
720 KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
721 Status = IncreaseCircularBufferSize(&DeviceExtension->OutputBuffer, NewQueueSize->OutSize);
722 KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
723 }
724 }
725 break;
726 }
727 case IOCTL_SERIAL_SET_RTS:
728 {
729 /* FIXME: If the handshake flow control of the device is configured to
730 * automatically use DTR, return STATUS_INVALID_PARAMETER */
731 TRACE_(SERIAL, "IOCTL_SERIAL_SET_RTS\n");
732 if (!(DeviceExtension->MCR & SR_MCR_RTS))
733 {
734 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
735 if (NT_SUCCESS(Status))
736 {
737 DeviceExtension->MCR |= SR_MCR_RTS;
738 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
739 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
740 }
741 }
742 else
743 Status = STATUS_SUCCESS;
744 break;
745 }
746 case IOCTL_SERIAL_SET_TIMEOUTS:
747 {
748 TRACE_(SERIAL, "IOCTL_SERIAL_SET_TIMEOUTS\n");
749 if (LengthIn != sizeof(SERIAL_TIMEOUTS) || BufferIn == NULL)
750 Status = STATUS_INVALID_PARAMETER;
751 else
752 {
753 DeviceExtension->SerialTimeOuts = *(PSERIAL_TIMEOUTS)BufferIn;
754 Status = STATUS_SUCCESS;
755 }
756 break;
757 }
758 case IOCTL_SERIAL_SET_WAIT_MASK:
759 {
760 PULONG pWaitMask = (PULONG)BufferIn;
761 TRACE_(SERIAL, "IOCTL_SERIAL_SET_WAIT_MASK\n");
762
763 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
764 Status = STATUS_INVALID_PARAMETER;
765 else if (DeviceExtension->WaitOnMaskIrp) /* FIXME: Race condition ; field may be currently in modification */
766 {
767 WARN_(SERIAL, "An IRP is already currently processed\n");
768 Status = STATUS_INVALID_PARAMETER;
769 }
770 else
771 {
772 DeviceExtension->WaitMask = *pWaitMask;
773 Status = STATUS_SUCCESS;
774 }
775 break;
776 }
777 case IOCTL_SERIAL_SET_XOFF:
778 {
779 /* FIXME */
780 ERR_(SERIAL, "IOCTL_SERIAL_SET_XOFF not implemented.\n");
781 Status = STATUS_NOT_IMPLEMENTED;
782 break;
783 }
784 case IOCTL_SERIAL_SET_XON:
785 {
786 /* FIXME */
787 ERR_(SERIAL, "IOCTL_SERIAL_SET_XON not implemented.\n");
788 Status = STATUS_NOT_IMPLEMENTED;
789 break;
790 }
791 case IOCTL_SERIAL_WAIT_ON_MASK:
792 {
793 PIRP WaitingIrp;
794 TRACE_(SERIAL, "IOCTL_SERIAL_WAIT_ON_MASK\n");
795
796 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
797 Status = STATUS_INVALID_PARAMETER;
798 else
799 {
800 /* FIXME: Race condition here:
801 * If an interrupt comes before we can mark the Irp
802 * as pending, it might be possible to complete the
803 * Irp before pending it, leading to a crash! */
804 WaitingIrp = InterlockedCompareExchangePointer(
805 (PVOID)&DeviceExtension->WaitOnMaskIrp,
806 Irp,
807 NULL);
808
809 /* Check if an Irp is already pending */
810 if (WaitingIrp != NULL)
811 {
812 /* Unable to have a 2nd pending IRP for this IOCTL */
813 WARN_(SERIAL, "Unable to pend a second IRP for IOCTL_SERIAL_WAIT_ON_MASK\n");
814 Status = STATUS_INVALID_PARAMETER;
815 }
816 else
817 {
818 Status = STATUS_PENDING;
819 /* FIXME: immediately return if a wait event already occurred */
820 }
821 }
822 break;
823 }
824 case IOCTL_SERIAL_XOFF_COUNTER:
825 {
826 /* FIXME */
827 ERR_(SERIAL, "IOCTL_SERIAL_XOFF_COUNTER not implemented.\n");
828 Status = STATUS_NOT_IMPLEMENTED;
829 break;
830 }
831 default:
832 {
833 /* Pass Irp to lower driver */
834 TRACE_(SERIAL, "Unknown IOCTL code 0x%x\n", Stack->Parameters.DeviceIoControl.IoControlCode);
835 IoSkipCurrentIrpStackLocation(Irp);
836 return IoCallDriver(DeviceExtension->LowerDevice, Irp);
837 }
838 }
839
840 Irp->IoStatus.Status = Status;
841 if (Status == STATUS_PENDING)
842 {
843 IoMarkIrpPending(Irp);
844 }
845 else
846 {
847 Irp->IoStatus.Information = Information;
848 IoCompleteRequest(Irp, IO_NO_INCREMENT);
849 }
850 return Status;
851 }