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