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