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