* Sync up to trunk head (r65095).
[reactos.git] / drivers / serial / serial / misc.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: Serial port driver
4 * FILE: drivers/dd/serial/misc.c
5 * PURPOSE: Misceallenous operations
6 *
7 * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
8 */
9 /* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
10
11 #include "serial.h"
12
13 #include <debug.h>
14
15 static IO_COMPLETION_ROUTINE ForwardIrpAndWaitCompletion;
16
17 static NTSTATUS NTAPI
18 ForwardIrpAndWaitCompletion(
19 IN PDEVICE_OBJECT DeviceObject,
20 IN PIRP Irp,
21 IN PVOID Context)
22 {
23 if (Irp->PendingReturned)
24 KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
25 return STATUS_MORE_PROCESSING_REQUIRED;
26 }
27
28 NTSTATUS
29 ForwardIrpAndWait(
30 IN PDEVICE_OBJECT DeviceObject,
31 IN PIRP Irp)
32 {
33 PDEVICE_OBJECT LowerDevice = ((PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerDevice;
34 KEVENT Event;
35 NTSTATUS Status;
36
37 ASSERT(LowerDevice);
38
39 KeInitializeEvent(&Event, NotificationEvent, FALSE);
40 IoCopyCurrentIrpStackLocationToNext(Irp);
41
42 TRACE_(SERIAL, "Calling lower device %p\n", LowerDevice);
43 IoSetCompletionRoutine(Irp, ForwardIrpAndWaitCompletion, &Event, TRUE, TRUE, TRUE);
44
45 Status = IoCallDriver(LowerDevice, Irp);
46 if (Status == STATUS_PENDING)
47 {
48 Status = KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
49 if (NT_SUCCESS(Status))
50 Status = Irp->IoStatus.Status;
51 }
52
53 return Status;
54 }
55
56 NTSTATUS NTAPI
57 ForwardIrpAndForget(
58 IN PDEVICE_OBJECT DeviceObject,
59 IN PIRP Irp)
60 {
61 PDEVICE_OBJECT LowerDevice = ((PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->LowerDevice;
62
63 ASSERT(LowerDevice);
64
65 IoSkipCurrentIrpStackLocation(Irp);
66 return IoCallDriver(LowerDevice, Irp);
67 }
68
69 VOID NTAPI
70 SerialReceiveByte(
71 IN PKDPC Dpc,
72 IN PVOID pDeviceExtension, // real type PSERIAL_DEVICE_EXTENSION
73 IN PVOID Unused1,
74 IN PVOID Unused2)
75 {
76 PSERIAL_DEVICE_EXTENSION DeviceExtension;
77 PUCHAR ComPortBase;
78 UCHAR Byte;
79 KIRQL Irql;
80 UCHAR IER;
81 NTSTATUS Status;
82
83 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)pDeviceExtension;
84 ComPortBase = ULongToPtr(DeviceExtension->BaseAddress);
85
86 KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
87 while (READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DATA_RECEIVED)
88 {
89 Byte = READ_PORT_UCHAR(SER_RBR(ComPortBase));
90 INFO_(SERIAL, "Byte received on COM%lu: 0x%02x\n",
91 DeviceExtension->ComPort, Byte);
92 Status = PushCircularBufferEntry(&DeviceExtension->InputBuffer, Byte);
93 if (NT_SUCCESS(Status))
94 DeviceExtension->SerialPerfStats.ReceivedCount++;
95 else
96 DeviceExtension->SerialPerfStats.BufferOverrunErrorCount++;
97 }
98 KeSetEvent(&DeviceExtension->InputBufferNotEmpty, 0, FALSE);
99 KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
100
101 /* allow new interrupts */
102 IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
103 WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_DATA_RECEIVED);
104 }
105
106 VOID NTAPI
107 SerialSendByte(
108 IN PKDPC Dpc,
109 IN PVOID pDeviceExtension, // real type PSERIAL_DEVICE_EXTENSION
110 IN PVOID Unused1,
111 IN PVOID Unused2)
112 {
113 PSERIAL_DEVICE_EXTENSION DeviceExtension;
114 PUCHAR ComPortBase;
115 UCHAR Byte;
116 KIRQL Irql;
117 UCHAR IER;
118 NTSTATUS Status;
119
120 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)pDeviceExtension;
121 ComPortBase = ULongToPtr(DeviceExtension->BaseAddress);
122
123 KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
124 while (!IsCircularBufferEmpty(&DeviceExtension->OutputBuffer)
125 && READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_THR_EMPTY)
126 {
127 Status = PopCircularBufferEntry(&DeviceExtension->OutputBuffer, &Byte);
128 if (!NT_SUCCESS(Status))
129 break;
130 WRITE_PORT_UCHAR(SER_THR(ComPortBase), Byte);
131 INFO_(SERIAL, "Byte sent to COM%lu: 0x%02x\n",
132 DeviceExtension->ComPort, Byte);
133 DeviceExtension->SerialPerfStats.TransmittedCount++;
134 }
135 if (!IsCircularBufferEmpty(&DeviceExtension->OutputBuffer))
136 {
137 /* allow new interrupts */
138 IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
139 WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_THR_EMPTY);
140 }
141 KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
142 }
143
144 VOID NTAPI
145 SerialCompleteIrp(
146 IN PKDPC Dpc,
147 IN PVOID pDeviceExtension, // real type PSERIAL_DEVICE_EXTENSION
148 IN PVOID pIrp, // real type PIRP
149 IN PVOID Unused)
150 {
151 IoCompleteRequest((PIRP)pIrp, IO_NO_INCREMENT);
152 }
153
154 BOOLEAN NTAPI
155 SerialInterruptService(
156 IN PKINTERRUPT Interrupt,
157 IN OUT PVOID ServiceContext)
158 {
159 PDEVICE_OBJECT DeviceObject;
160 PSERIAL_DEVICE_EXTENSION DeviceExtension;
161 PUCHAR ComPortBase;
162 UCHAR Iir;
163 ULONG Events = 0;
164 BOOLEAN ret = FALSE;
165
166 /* FIXME: sometimes, produce SERIAL_EV_RXFLAG event */
167
168 DeviceObject = (PDEVICE_OBJECT)ServiceContext;
169 DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
170 ComPortBase = ULongToPtr(DeviceExtension->BaseAddress);
171
172 Iir = READ_PORT_UCHAR(SER_IIR(ComPortBase));
173 if (Iir == 0xff)
174 return TRUE;
175 Iir &= SR_IIR_ID_MASK;
176 if ((Iir & SR_IIR_SELF) != 0) { return FALSE; }
177
178 switch (Iir)
179 {
180 case SR_IIR_MSR_CHANGE:
181 {
182 UCHAR MSR, IER;
183 TRACE_(SERIAL, "SR_IIR_MSR_CHANGE\n");
184
185 MSR = READ_PORT_UCHAR(SER_MSR(ComPortBase));
186 if (MSR & SR_MSR_CTS_CHANGED)
187 {
188 if (MSR & SR_MSR_CTS)
189 KeInsertQueueDpc(&DeviceExtension->SendByteDpc, NULL, NULL);
190 else
191 {
192 ; /* FIXME: stop transmission */
193 }
194 Events |= SERIAL_EV_CTS;
195 }
196 if (MSR & SR_MSR_DSR_CHANGED)
197 {
198 if (MSR & SR_MSR_DSR)
199 KeInsertQueueDpc(&DeviceExtension->ReceivedByteDpc, NULL, NULL);
200 else
201 {
202 ; /* FIXME: stop reception */
203 }
204 Events |= SERIAL_EV_DSR;
205 }
206 if (MSR & SR_MSR_RI_CHANGED)
207 {
208 INFO_(SERIAL, "SR_MSR_RI_CHANGED changed: now %d\n", MSR & SI_MSR_RI);
209 Events |= SERIAL_EV_RING;
210 }
211 if (MSR & SR_MSR_DCD_CHANGED)
212 {
213 INFO_(SERIAL, "SR_MSR_DCD_CHANGED changed: now %d\n", MSR & SR_MSR_DCD);
214 Events |= SERIAL_EV_RLSD;
215 }
216 IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
217 WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_MSR_CHANGE);
218
219 ret = TRUE;
220 goto done;
221 }
222 case SR_IIR_THR_EMPTY:
223 {
224 TRACE_(SERIAL, "SR_IIR_THR_EMPTY\n");
225
226 KeInsertQueueDpc(&DeviceExtension->SendByteDpc, NULL, NULL);
227 Events |= SERIAL_EV_TXEMPTY;
228
229 ret = TRUE;
230 goto done;
231 }
232 case SR_IIR_DATA_RECEIVED:
233 {
234 ULONG AlreadyReceivedBytes, Limit;
235 TRACE_(SERIAL, "SR_IIR_DATA_RECEIVED\n");
236
237 KeInsertQueueDpc(&DeviceExtension->ReceivedByteDpc, NULL, NULL);
238 Events |= SERIAL_EV_RXCHAR;
239
240 /* Check if buffer will be 80% full */
241 AlreadyReceivedBytes = GetNumberOfElementsInCircularBuffer(
242 &DeviceExtension->InputBuffer) * 5;
243 Limit = DeviceExtension->InputBuffer.Length * 4;
244 if (AlreadyReceivedBytes < Limit && AlreadyReceivedBytes + 1 >= Limit)
245 {
246 /* Buffer is full at 80% */
247 Events |= SERIAL_EV_RX80FULL;
248 }
249
250 ret = TRUE;
251 goto done;
252 }
253 case SR_IIR_ERROR:
254 {
255 UCHAR LSR;
256 TRACE_(SERIAL, "SR_IIR_ERROR\n");
257
258 LSR = READ_PORT_UCHAR(SER_LSR(ComPortBase));
259 if (LSR & SR_LSR_OVERRUN_ERROR)
260 {
261 InterlockedIncrement((PLONG)&DeviceExtension->SerialPerfStats.SerialOverrunErrorCount);
262 Events |= SERIAL_EV_ERR;
263 }
264 if (LSR & SR_LSR_PARITY_ERROR)
265 {
266 InterlockedIncrement((PLONG)&DeviceExtension->SerialPerfStats.ParityErrorCount);
267 Events |= SERIAL_EV_ERR;
268 }
269 if (LSR & SR_LSR_FRAMING_ERROR)
270 {
271 InterlockedIncrement((PLONG)&DeviceExtension->SerialPerfStats.FrameErrorCount);
272 Events |= SERIAL_EV_ERR;
273 }
274 if (LSR & SR_LSR_BREAK_INT)
275 {
276 InterlockedIncrement((PLONG)&DeviceExtension->BreakInterruptErrorCount);
277 Events |= SERIAL_EV_BREAK;
278 }
279
280 ret = TRUE;
281 goto done;
282 }
283 }
284
285 done:
286 if (!ret)
287 return FALSE;
288 if (DeviceExtension->WaitOnMaskIrp && (Events & DeviceExtension->WaitMask))
289 {
290 /* Finish pending IRP */
291 PULONG pEvents = (PULONG)DeviceExtension->WaitOnMaskIrp->AssociatedIrp.SystemBuffer;
292
293 DeviceExtension->WaitOnMaskIrp->IoStatus.Status = STATUS_SUCCESS;
294 DeviceExtension->WaitOnMaskIrp->IoStatus.Information = sizeof(ULONG);
295 *pEvents = Events;
296 KeInsertQueueDpc(&DeviceExtension->CompleteIrpDpc, DeviceExtension->WaitOnMaskIrp, NULL);
297
298 /* We are now ready to handle another IRP, even if this one is not completed */
299 DeviceExtension->WaitOnMaskIrp = NULL;
300 return STATUS_SUCCESS;
301 }
302 return TRUE;
303 }