Synchronize with trunk r58457.
[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 PSERIAL_CHARS pSerialChars;
378 ERR_(SERIAL, "IOCTL_SERIAL_GET_CHARS not implemented.\n");
379 if (LengthOut < sizeof(SERIAL_CHARS))
380 Status = STATUS_BUFFER_TOO_SMALL;
381 else if (BufferOut == NULL)
382 Status = STATUS_INVALID_PARAMETER;
383 else
384 {
385 pSerialChars = (PSERIAL_CHARS)BufferOut;
386 pSerialChars->EofChar = 0;
387 pSerialChars->ErrorChar = 0;
388 pSerialChars->BreakChar = 0;
389 pSerialChars->EventChar = 0;
390 pSerialChars->XonChar = 0;
391 pSerialChars->XoffChar = 0;
392 Information = sizeof(SERIAL_CHARS);
393 Status = STATUS_SUCCESS;
394 }
395 break;
396 }
397 case IOCTL_SERIAL_GET_COMMSTATUS:
398 {
399 TRACE_(SERIAL, "IOCTL_SERIAL_GET_COMMSTATUS\n");
400 if (LengthOut < sizeof(SERIAL_STATUS))
401 Status = STATUS_BUFFER_TOO_SMALL;
402 else if (BufferOut == NULL)
403 Status = STATUS_INVALID_PARAMETER;
404 else
405 {
406 Status = SerialGetCommStatus((PSERIAL_STATUS)BufferOut, DeviceExtension);
407 Information = sizeof(SERIAL_STATUS);
408 }
409 break;
410 }
411 case IOCTL_SERIAL_GET_DTRRTS:
412 {
413 PULONG pDtrRts;
414 TRACE_(SERIAL, "IOCTL_SERIAL_GET_DTRRTS\n");
415 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
416 Status = STATUS_INVALID_PARAMETER;
417 else
418 {
419 pDtrRts = (PULONG)BufferOut;
420 *pDtrRts = 0;
421 if (DeviceExtension->MCR & SR_MCR_DTR)
422 *pDtrRts |= SERIAL_DTR_STATE;
423 if (DeviceExtension->MCR & SR_MCR_RTS)
424 *pDtrRts |= SERIAL_RTS_STATE;
425 Information = sizeof(ULONG);
426 Status = STATUS_SUCCESS;
427 }
428 break;
429 }
430 case IOCTL_SERIAL_GET_HANDFLOW:
431 {
432 /* FIXME */
433 PSERIAL_HANDFLOW pSerialHandflow;
434 ERR_(SERIAL, "IOCTL_SERIAL_GET_HANDFLOW not implemented.\n");
435 if (LengthOut < sizeof(SERIAL_HANDFLOW))
436 Status = STATUS_BUFFER_TOO_SMALL;
437 else if (BufferOut == NULL)
438 Status = STATUS_INVALID_PARAMETER;
439 else
440 {
441 pSerialHandflow = (PSERIAL_HANDFLOW)BufferOut;
442 pSerialHandflow->ControlHandShake = 0;
443 pSerialHandflow->FlowReplace = 0;
444 pSerialHandflow->XonLimit = 0;
445 pSerialHandflow->XoffLimit = 0;
446 Information = sizeof(SERIAL_HANDFLOW);
447 Status = STATUS_SUCCESS;
448 }
449 break;
450 }
451 case IOCTL_SERIAL_GET_LINE_CONTROL:
452 {
453 TRACE_(SERIAL, "IOCTL_SERIAL_GET_LINE_CONTROL\n");
454 if (LengthOut < sizeof(SERIAL_LINE_CONTROL))
455 Status = STATUS_BUFFER_TOO_SMALL;
456 else if (BufferOut == NULL)
457 Status = STATUS_INVALID_PARAMETER;
458 else
459 {
460 *((PSERIAL_LINE_CONTROL)BufferOut) = DeviceExtension->SerialLineControl;
461 Information = sizeof(SERIAL_LINE_CONTROL);
462 Status = STATUS_SUCCESS;
463 }
464 break;
465 }
466 case IOCTL_SERIAL_GET_MODEM_CONTROL:
467 {
468 PULONG pMCR;
469 TRACE_(SERIAL, "IOCTL_SERIAL_GET_MODEM_CONTROL\n");
470 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
471 Status = STATUS_INVALID_PARAMETER;
472 else
473 {
474 pMCR = (PULONG)BufferOut;
475 *pMCR = DeviceExtension->MCR;
476 Information = sizeof(ULONG);
477 Status = STATUS_SUCCESS;
478 }
479 break;
480 }
481 case IOCTL_SERIAL_GET_MODEMSTATUS:
482 {
483 PULONG pMSR;
484 TRACE_(SERIAL, "IOCTL_SERIAL_GET_MODEMSTATUS\n");
485 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
486 Status = STATUS_INVALID_PARAMETER;
487 else
488 {
489 pMSR = (PULONG)BufferOut;
490 *pMSR = DeviceExtension->MSR;
491 Information = sizeof(ULONG);
492 Status = STATUS_SUCCESS;
493 }
494 break;
495 }
496 case IOCTL_SERIAL_GET_PROPERTIES:
497 {
498 TRACE_(SERIAL, "IOCTL_SERIAL_GET_PROPERTIES\n");
499 if (LengthOut < sizeof(SERIAL_COMMPROP))
500 Status = STATUS_BUFFER_TOO_SMALL;
501 else if (BufferOut == NULL)
502 Status = STATUS_INVALID_PARAMETER;
503 else
504 {
505 Status = SerialGetCommProp((PSERIAL_COMMPROP)BufferOut, DeviceExtension);
506 Information = sizeof(SERIAL_COMMPROP);
507 }
508 break;
509 }
510 case IOCTL_SERIAL_GET_STATS:
511 {
512 TRACE_(SERIAL, "IOCTL_SERIAL_GET_STATS\n");
513 if (LengthOut < sizeof(SERIALPERF_STATS))
514 Status = STATUS_BUFFER_TOO_SMALL;
515 else if (BufferOut == NULL)
516 Status = STATUS_INVALID_PARAMETER;
517 else
518 {
519 KeSynchronizeExecution(DeviceExtension->Interrupt,
520 (PKSYNCHRONIZE_ROUTINE)SerialGetPerfStats, Irp);
521 Information = sizeof(SERIALPERF_STATS);
522 Status = STATUS_SUCCESS;
523 }
524 break;
525 }
526 case IOCTL_SERIAL_GET_TIMEOUTS:
527 {
528 TRACE_(SERIAL, "IOCTL_SERIAL_GET_TIMEOUTS\n");
529 if (LengthOut != sizeof(SERIAL_TIMEOUTS) || BufferOut == NULL)
530 Status = STATUS_INVALID_PARAMETER;
531 else
532 {
533 *(PSERIAL_TIMEOUTS)BufferOut = DeviceExtension->SerialTimeOuts;
534 Information = sizeof(SERIAL_TIMEOUTS);
535 Status = STATUS_SUCCESS;
536 }
537 break;
538 }
539 case IOCTL_SERIAL_GET_WAIT_MASK:
540 {
541 PULONG pWaitMask;
542 TRACE_(SERIAL, "IOCTL_SERIAL_GET_WAIT_MASK\n");
543 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
544 Status = STATUS_INVALID_PARAMETER;
545 else
546 {
547 pWaitMask = (PULONG)BufferOut;
548 *pWaitMask = DeviceExtension->WaitMask;
549 Information = sizeof(ULONG);
550 Status = STATUS_SUCCESS;
551 }
552 break;
553 }
554 case IOCTL_SERIAL_IMMEDIATE_CHAR:
555 {
556 /* FIXME */
557 ERR_(SERIAL, "IOCTL_SERIAL_IMMEDIATE_CHAR not implemented.\n");
558 Status = STATUS_NOT_IMPLEMENTED;
559 break;
560 }
561 case IOCTL_SERIAL_LSRMST_INSERT:
562 {
563 /* FIXME */
564 ERR_(SERIAL, "IOCTL_SERIAL_LSRMST_INSERT not implemented.\n");
565 Status = STATUS_NOT_IMPLEMENTED;
566 break;
567 }
568 case IOCTL_SERIAL_PURGE:
569 {
570 KIRQL Irql;
571 TRACE_(SERIAL, "IOCTL_SERIAL_PURGE\n");
572 /* FIXME: SERIAL_PURGE_RXABORT and SERIAL_PURGE_TXABORT
573 * should stop current request */
574 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
575 Status = STATUS_INVALID_PARAMETER;
576 else
577 {
578 ULONG PurgeMask = *(PULONG)BufferIn;
579
580 Status = STATUS_SUCCESS;
581 /* FIXME: use SERIAL_PURGE_RXABORT and SERIAL_PURGE_TXABORT flags */
582 if (PurgeMask & SERIAL_PURGE_RXCLEAR)
583 {
584 KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
585 DeviceExtension->InputBuffer.ReadPosition = DeviceExtension->InputBuffer.WritePosition = 0;
586 if (DeviceExtension->UartType >= Uart16550A)
587 {
588 /* Clear also Uart FIFO */
589 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
590 if (NT_SUCCESS(Status))
591 {
592 WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_RCVR);
593 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
594 }
595 }
596 KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
597 }
598
599 if (PurgeMask & SERIAL_PURGE_TXCLEAR)
600 {
601 KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
602 DeviceExtension->OutputBuffer.ReadPosition = DeviceExtension->OutputBuffer.WritePosition = 0;
603 if (DeviceExtension->UartType >= Uart16550A)
604 {
605 /* Clear also Uart FIFO */
606 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
607 if (NT_SUCCESS(Status))
608 {
609 WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_XMIT);
610 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
611 }
612 }
613 KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
614 }
615 }
616 break;
617 }
618 case IOCTL_SERIAL_RESET_DEVICE:
619 {
620 /* FIXME */
621 ERR_(SERIAL, "IOCTL_SERIAL_RESET_DEVICE not implemented.\n");
622 Status = STATUS_NOT_IMPLEMENTED;
623 break;
624 }
625 case IOCTL_SERIAL_SET_BAUD_RATE:
626 {
627 PULONG pNewBaudRate;
628 TRACE_(SERIAL, "IOCTL_SERIAL_SET_BAUD_RATE\n");
629 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
630 Status = STATUS_INVALID_PARAMETER;
631 else
632 {
633 pNewBaudRate = (PULONG)BufferIn;
634 Status = SerialSetBaudRate(DeviceExtension, *pNewBaudRate);
635 }
636 break;
637 }
638 case IOCTL_SERIAL_SET_BREAK_OFF:
639 {
640 /* FIXME */
641 ERR_(SERIAL, "IOCTL_SERIAL_SET_BREAK_OFF not implemented.\n");
642 Status = STATUS_NOT_IMPLEMENTED;
643 break;
644 }
645 case IOCTL_SERIAL_SET_BREAK_ON:
646 {
647 /* FIXME */
648 ERR_(SERIAL, "IOCTL_SERIAL_SET_BREAK_ON not implemented.\n");
649 Status = STATUS_NOT_IMPLEMENTED;
650 break;
651 }
652 case IOCTL_SERIAL_SET_CHARS:
653 {
654 /* FIXME */
655 ERR_(SERIAL, "IOCTL_SERIAL_SET_CHARS not implemented.\n");
656 Status = STATUS_SUCCESS;
657 break;
658 }
659 case IOCTL_SERIAL_SET_DTR:
660 {
661 /* FIXME: If the handshake flow control of the device is configured to
662 * automatically use DTR, return STATUS_INVALID_PARAMETER */
663 TRACE_(SERIAL, "IOCTL_SERIAL_SET_DTR\n");
664 if (!(DeviceExtension->MCR & SR_MCR_DTR))
665 {
666 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
667 if (NT_SUCCESS(Status))
668 {
669 DeviceExtension->MCR |= SR_MCR_DTR;
670 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
671 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
672 }
673 }
674 else
675 Status = STATUS_SUCCESS;
676 break;
677 }
678 case IOCTL_SERIAL_SET_FIFO_CONTROL:
679 {
680 TRACE_(SERIAL, "IOCTL_SERIAL_SET_FIFO_CONTROL\n");
681 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
682 Status = STATUS_INVALID_PARAMETER;
683 else
684 {
685 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
686 if (NT_SUCCESS(Status))
687 {
688 WRITE_PORT_UCHAR(SER_FCR(ComPortBase), (UCHAR)((*(PULONG)BufferIn) & 0xff));
689 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
690 }
691 }
692 break;
693 }
694 case IOCTL_SERIAL_SET_HANDFLOW:
695 {
696 /* FIXME */
697 ERR_(SERIAL, "IOCTL_SERIAL_SET_HANDFLOW not implemented.\n");
698 Status = STATUS_SUCCESS;
699 break;
700 }
701 case IOCTL_SERIAL_SET_LINE_CONTROL:
702 {
703 TRACE_(SERIAL, "IOCTL_SERIAL_SET_LINE_CONTROL\n");
704 if (LengthIn < sizeof(SERIAL_LINE_CONTROL))
705 Status = STATUS_BUFFER_TOO_SMALL;
706 else if (BufferIn == NULL)
707 Status = STATUS_INVALID_PARAMETER;
708 else
709 Status = SerialSetLineControl(DeviceExtension, (PSERIAL_LINE_CONTROL)BufferIn);
710 break;
711 }
712 case IOCTL_SERIAL_SET_MODEM_CONTROL:
713 {
714 PULONG pMCR;
715 TRACE_(SERIAL, "IOCTL_SERIAL_SET_MODEM_CONTROL\n");
716 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
717 Status = STATUS_INVALID_PARAMETER;
718 else
719 {
720 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
721 if (NT_SUCCESS(Status))
722 {
723 pMCR = (PULONG)BufferIn;
724 DeviceExtension->MCR = (UCHAR)(*pMCR & 0xff);
725 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
726 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
727 }
728 }
729 break;
730 }
731 case IOCTL_SERIAL_SET_QUEUE_SIZE:
732 {
733 if (LengthIn < sizeof(SERIAL_QUEUE_SIZE ))
734 return STATUS_BUFFER_TOO_SMALL;
735 else if (BufferIn == NULL)
736 return STATUS_INVALID_PARAMETER;
737 else
738 {
739 KIRQL Irql;
740 PSERIAL_QUEUE_SIZE NewQueueSize = (PSERIAL_QUEUE_SIZE)BufferIn;
741 Status = STATUS_SUCCESS;
742 if (NewQueueSize->InSize > DeviceExtension->InputBuffer.Length)
743 {
744 KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
745 Status = IncreaseCircularBufferSize(&DeviceExtension->InputBuffer, NewQueueSize->InSize);
746 KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
747 }
748 if (NT_SUCCESS(Status) && NewQueueSize->OutSize > DeviceExtension->OutputBuffer.Length)
749 {
750 KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
751 Status = IncreaseCircularBufferSize(&DeviceExtension->OutputBuffer, NewQueueSize->OutSize);
752 KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
753 }
754 }
755 break;
756 }
757 case IOCTL_SERIAL_SET_RTS:
758 {
759 /* FIXME: If the handshake flow control of the device is configured to
760 * automatically use DTR, return STATUS_INVALID_PARAMETER */
761 TRACE_(SERIAL, "IOCTL_SERIAL_SET_RTS\n");
762 if (!(DeviceExtension->MCR & SR_MCR_RTS))
763 {
764 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
765 if (NT_SUCCESS(Status))
766 {
767 DeviceExtension->MCR |= SR_MCR_RTS;
768 WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
769 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
770 }
771 }
772 else
773 Status = STATUS_SUCCESS;
774 break;
775 }
776 case IOCTL_SERIAL_SET_TIMEOUTS:
777 {
778 TRACE_(SERIAL, "IOCTL_SERIAL_SET_TIMEOUTS\n");
779 if (LengthIn != sizeof(SERIAL_TIMEOUTS) || BufferIn == NULL)
780 Status = STATUS_INVALID_PARAMETER;
781 else
782 {
783 DeviceExtension->SerialTimeOuts = *(PSERIAL_TIMEOUTS)BufferIn;
784 Status = STATUS_SUCCESS;
785 }
786 break;
787 }
788 case IOCTL_SERIAL_SET_WAIT_MASK:
789 {
790 PULONG pWaitMask = (PULONG)BufferIn;
791 TRACE_(SERIAL, "IOCTL_SERIAL_SET_WAIT_MASK\n");
792
793 if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
794 Status = STATUS_INVALID_PARAMETER;
795 else if (DeviceExtension->WaitOnMaskIrp) /* FIXME: Race condition ; field may be currently in modification */
796 {
797 WARN_(SERIAL, "An IRP is already currently processed\n");
798 Status = STATUS_INVALID_PARAMETER;
799 }
800 else
801 {
802 DeviceExtension->WaitMask = *pWaitMask;
803 Status = STATUS_SUCCESS;
804 }
805 break;
806 }
807 case IOCTL_SERIAL_SET_XOFF:
808 {
809 /* FIXME */
810 ERR_(SERIAL, "IOCTL_SERIAL_SET_XOFF not implemented.\n");
811 Status = STATUS_NOT_IMPLEMENTED;
812 break;
813 }
814 case IOCTL_SERIAL_SET_XON:
815 {
816 /* FIXME */
817 ERR_(SERIAL, "IOCTL_SERIAL_SET_XON not implemented.\n");
818 Status = STATUS_NOT_IMPLEMENTED;
819 break;
820 }
821 case IOCTL_SERIAL_WAIT_ON_MASK:
822 {
823 PIRP WaitingIrp;
824 TRACE_(SERIAL, "IOCTL_SERIAL_WAIT_ON_MASK\n");
825
826 if (LengthOut != sizeof(ULONG) || BufferOut == NULL)
827 Status = STATUS_INVALID_PARAMETER;
828 else
829 {
830 /* FIXME: Race condition here:
831 * If an interrupt comes before we can mark the Irp
832 * as pending, it might be possible to complete the
833 * Irp before pending it, leading to a crash! */
834 WaitingIrp = InterlockedCompareExchangePointer(
835 (PVOID)&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 Status = STATUS_INVALID_PARAMETER;
845 }
846 else
847 {
848 Status = STATUS_PENDING;
849 /* FIXME: immediately return if a wait event already occurred */
850 }
851 }
852 break;
853 }
854 case IOCTL_SERIAL_XOFF_COUNTER:
855 {
856 /* FIXME */
857 ERR_(SERIAL, "IOCTL_SERIAL_XOFF_COUNTER not implemented.\n");
858 Status = STATUS_NOT_IMPLEMENTED;
859 break;
860 }
861 default:
862 {
863 /* Pass Irp to lower driver */
864 TRACE_(SERIAL, "Unknown IOCTL code 0x%x\n", Stack->Parameters.DeviceIoControl.IoControlCode);
865 IoSkipCurrentIrpStackLocation(Irp);
866 return IoCallDriver(DeviceExtension->LowerDevice, Irp);
867 }
868 }
869
870 Irp->IoStatus.Status = Status;
871 if (Status == STATUS_PENDING)
872 {
873 IoMarkIrpPending(Irp);
874 }
875 else
876 {
877 Irp->IoStatus.Information = Information;
878 IoCompleteRequest(Irp, IO_NO_INCREMENT);
879 }
880 return Status;
881 }