2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: Serial port driver
4 * FILE: drivers/dd/serial/create.c
5 * PURPOSE: Serial IRP_MJ_READ/IRP_MJ_WRITE operations
7 * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
14 static IO_WORKITEM_ROUTINE SerialReadWorkItem
;
17 SerialGetUserBuffer(IN PIRP Irp
)
22 return Irp
->MdlAddress
;
24 return Irp
->AssociatedIrp
.SystemBuffer
;
29 IN PDEVICE_OBJECT DeviceObject
,
31 PWORKITEM_DATA WorkItemData
)
33 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
37 KTIMER TotalTimeoutTimer
;
40 PVOID ObjectsArray
[2];
41 ULONG_PTR Information
= 0;
47 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
48 Length
= IoGetCurrentIrpStackLocation(Irp
)->Parameters
.Read
.Length
;
49 Buffer
= SerialGetUserBuffer(Irp
);
51 INFO_(SERIAL
, "UseIntervalTimeout = %s, IntervalTimeout = %lu\n",
52 WorkItemData
->UseIntervalTimeout
? "YES" : "NO",
53 WorkItemData
->UseIntervalTimeout
? WorkItemData
->IntervalTimeout
.QuadPart
: 0);
54 INFO_(SERIAL
, "UseTotalTimeout = %s\n",
55 WorkItemData
->UseTotalTimeout
? "YES" : "NO");
58 ObjectsArray
[0] = &DeviceExtension
->InputBufferNotEmpty
;
59 if (WorkItemData
->UseTotalTimeout
)
61 KeInitializeTimer(&TotalTimeoutTimer
);
62 KeSetTimer(&TotalTimeoutTimer
, WorkItemData
->TotalTimeoutTime
, NULL
);
63 ObjectsArray
[ObjectCount
] = &TotalTimeoutTimer
;
67 /* while buffer is not fully filled */
70 /* read already received bytes from buffer */
71 KeAcquireSpinLock(&DeviceExtension
->InputBufferLock
, &Irql
);
72 while (!IsCircularBufferEmpty(&DeviceExtension
->InputBuffer
)
75 PopCircularBufferEntry(&DeviceExtension
->InputBuffer
, &ReceivedByte
);
76 INFO_(SERIAL
, "Reading byte from buffer: 0x%02x\n", ReceivedByte
);
78 Buffer
[Information
++] = ReceivedByte
;
81 KeClearEvent(&DeviceExtension
->InputBufferNotEmpty
);
82 KeReleaseSpinLock(&DeviceExtension
->InputBufferLock
, Irql
);
84 if (WorkItemData
->DontWait
85 && !(WorkItemData
->ReadAtLeastOneByte
&& Information
== 0))
87 INFO_(SERIAL
, "Buffer empty. Don't wait more bytes\n");
91 Status
= KeWaitForMultipleObjects(
98 (WorkItemData
->UseIntervalTimeout
&& Information
> 0) ? &WorkItemData
->IntervalTimeout
: NULL
,
101 if (Status
== STATUS_TIMEOUT
/* interval timeout */
102 || Status
== STATUS_WAIT_1
) /* total timeout */
104 TRACE_(SERIAL
, "Timeout when reading bytes. Status = 0x%08lx\n", Status
);
109 /* stop total timeout timer */
110 if (WorkItemData
->UseTotalTimeout
)
111 KeCancelTimer(&TotalTimeoutTimer
);
113 Irp
->IoStatus
.Information
= Information
;
114 if (Information
== 0)
115 Irp
->IoStatus
.Status
= STATUS_TIMEOUT
;
117 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
122 IN PDEVICE_OBJECT DeviceObject
,
123 IN PVOID pWorkItemData
/* real type PWORKITEM_DATA */)
125 PWORKITEM_DATA WorkItemData
;
128 TRACE_(SERIAL
, "SerialReadWorkItem() called\n");
130 WorkItemData
= (PWORKITEM_DATA
)pWorkItemData
;
131 Irp
= WorkItemData
->Irp
;
133 ReadBytes(DeviceObject
, Irp
, WorkItemData
);
135 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
137 IoFreeWorkItem(WorkItemData
->IoWorkItem
);
138 ExFreePoolWithTag(pWorkItemData
, SERIAL_TAG
);
143 IN PDEVICE_OBJECT DeviceObject
,
146 PIO_STACK_LOCATION Stack
;
147 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
150 PWORKITEM_DATA WorkItemData
;
151 PIO_WORKITEM WorkItem
;
154 TRACE_(SERIAL
, "IRP_MJ_READ\n");
156 Stack
= IoGetCurrentIrpStackLocation(Irp
);
157 Length
= Stack
->Parameters
.Read
.Length
;
158 Buffer
= SerialGetUserBuffer(Irp
);
159 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
161 if (Stack
->Parameters
.Read
.ByteOffset
.QuadPart
!= 0 || Buffer
== NULL
)
163 Status
= STATUS_INVALID_PARAMETER
;
169 Status
= STATUS_SUCCESS
;
173 /* Allocate memory for parameters */
174 WorkItemData
= ExAllocatePoolWithTag(PagedPool
, sizeof(WORKITEM_DATA
), SERIAL_TAG
);
177 Status
= STATUS_INSUFFICIENT_RESOURCES
;
180 RtlZeroMemory(WorkItemData
, sizeof(WORKITEM_DATA
));
181 WorkItemData
->Irp
= Irp
;
183 /* Calculate time outs */
184 if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
== INFINITE
&&
185 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
== INFINITE
&&
186 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
> 0 &&
187 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
< INFINITE
)
189 /* read at least one byte, and at most bytes already received */
190 WorkItemData
->DontWait
= TRUE
;
191 WorkItemData
->ReadAtLeastOneByte
= TRUE
;
193 else if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
== INFINITE
&&
194 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
== 0 &&
195 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
== 0)
197 /* read only bytes that are already in buffer */
198 WorkItemData
->DontWait
= TRUE
;
203 if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
!= 0)
205 WorkItemData
->UseIntervalTimeout
= TRUE
;
206 WorkItemData
->IntervalTimeout
.QuadPart
= DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
;
208 if (DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
!= 0 ||
209 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
!= 0)
212 LARGE_INTEGER SystemTime
;
214 WorkItemData
->UseTotalTimeout
= TRUE
;
215 TotalTimeout
= DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
+
216 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
* Length
;
217 KeQuerySystemTime(&SystemTime
);
218 WorkItemData
->TotalTimeoutTime
.QuadPart
= SystemTime
.QuadPart
+
219 TotalTimeout
* 10000;
224 WorkItem
= IoAllocateWorkItem(DeviceObject
);
227 WorkItemData
->IoWorkItem
= WorkItem
;
228 IoMarkIrpPending(Irp
);
229 IoQueueWorkItem(WorkItem
, SerialReadWorkItem
, DelayedWorkQueue
, WorkItemData
);
230 return STATUS_PENDING
;
233 /* Insufficient resources, we can't pend the Irp */
234 INFO_(SERIAL
, "Insufficient resources\n");
235 Status
= IoAcquireRemoveLock(&DeviceExtension
->RemoveLock
, ULongToPtr(DeviceExtension
->ComPort
));
236 if (!NT_SUCCESS(Status
))
238 ExFreePoolWithTag(WorkItemData
, SERIAL_TAG
);
241 ReadBytes(DeviceObject
, Irp
, WorkItemData
);
242 Status
= Irp
->IoStatus
.Status
;
244 IoReleaseRemoveLock(&DeviceExtension
->RemoveLock
, ULongToPtr(DeviceExtension
->ComPort
));
247 Irp
->IoStatus
.Status
= Status
;
248 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
254 IN PDEVICE_OBJECT DeviceObject
,
257 PIO_STACK_LOCATION Stack
;
258 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
260 ULONG_PTR Information
= 0;
263 NTSTATUS Status
= STATUS_SUCCESS
;
265 TRACE_(SERIAL
, "IRP_MJ_WRITE\n");
267 /* FIXME: pend operation if possible */
268 /* FIXME: use write timeouts */
270 Stack
= IoGetCurrentIrpStackLocation(Irp
);
271 Length
= Stack
->Parameters
.Write
.Length
;
272 Buffer
= SerialGetUserBuffer(Irp
);
273 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
275 if (Stack
->Parameters
.Write
.ByteOffset
.QuadPart
!= 0 || Buffer
== NULL
)
277 Status
= STATUS_INVALID_PARAMETER
;
281 Status
= IoAcquireRemoveLock(&DeviceExtension
->RemoveLock
, ULongToPtr(DeviceExtension
->ComPort
));
282 if (!NT_SUCCESS(Status
))
285 /* push bytes into output buffer */
286 KeAcquireSpinLock(&DeviceExtension
->OutputBufferLock
, &Irql
);
287 while (Information
< Length
)
289 Status
= PushCircularBufferEntry(&DeviceExtension
->OutputBuffer
, Buffer
[Information
]);
290 if (!NT_SUCCESS(Status
))
292 if (Status
== STATUS_BUFFER_TOO_SMALL
)
294 KeReleaseSpinLock(&DeviceExtension
->OutputBufferLock
, Irql
);
295 SerialSendByte(NULL
, DeviceExtension
, NULL
, NULL
);
296 KeAcquireSpinLock(&DeviceExtension
->OutputBufferLock
, &Irql
);
301 WARN_(SERIAL
, "Buffer overrun on COM%lu\n", DeviceExtension
->ComPort
);
302 DeviceExtension
->SerialPerfStats
.BufferOverrunErrorCount
++;
308 KeReleaseSpinLock(&DeviceExtension
->OutputBufferLock
, Irql
);
309 IoReleaseRemoveLock(&DeviceExtension
->RemoveLock
, ULongToPtr(DeviceExtension
->ComPort
));
312 SerialSendByte(NULL
, DeviceExtension
, NULL
, NULL
);
315 Irp
->IoStatus
.Information
= Information
;
316 Irp
->IoStatus
.Status
= Status
;
317 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);