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