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)
12 static IO_WORKITEM_ROUTINE SerialReadWorkItem
;
15 SerialGetUserBuffer(IN PIRP Irp
)
20 return Irp
->MdlAddress
;
22 return Irp
->AssociatedIrp
.SystemBuffer
;
27 IN PDEVICE_OBJECT DeviceObject
,
29 PWORKITEM_DATA WorkItemData
)
31 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
35 KTIMER TotalTimeoutTimer
;
38 PVOID ObjectsArray
[2];
39 ULONG_PTR Information
= 0;
45 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
46 Length
= IoGetCurrentIrpStackLocation(Irp
)->Parameters
.Read
.Length
;
47 Buffer
= SerialGetUserBuffer(Irp
);
49 INFO_(SERIAL
, "UseIntervalTimeout = %s, IntervalTimeout = %lu\n",
50 WorkItemData
->UseIntervalTimeout
? "YES" : "NO",
51 WorkItemData
->UseIntervalTimeout
? WorkItemData
->IntervalTimeout
.QuadPart
: 0);
52 INFO_(SERIAL
, "UseTotalTimeout = %s\n",
53 WorkItemData
->UseTotalTimeout
? "YES" : "NO");
56 ObjectsArray
[0] = &DeviceExtension
->InputBufferNotEmpty
;
57 if (WorkItemData
->UseTotalTimeout
)
59 KeInitializeTimer(&TotalTimeoutTimer
);
60 KeSetTimer(&TotalTimeoutTimer
, WorkItemData
->TotalTimeoutTime
, NULL
);
61 ObjectsArray
[ObjectCount
] = &TotalTimeoutTimer
;
65 /* while buffer is not fully filled */
68 /* read already received bytes from buffer */
69 KeAcquireSpinLock(&DeviceExtension
->InputBufferLock
, &Irql
);
70 while (!IsCircularBufferEmpty(&DeviceExtension
->InputBuffer
)
73 PopCircularBufferEntry(&DeviceExtension
->InputBuffer
, &ReceivedByte
);
74 INFO_(SERIAL
, "Reading byte from buffer: 0x%02x\n", ReceivedByte
);
76 Buffer
[Information
++] = ReceivedByte
;
79 KeClearEvent(&DeviceExtension
->InputBufferNotEmpty
);
80 KeReleaseSpinLock(&DeviceExtension
->InputBufferLock
, Irql
);
82 if (WorkItemData
->DontWait
83 && !(WorkItemData
->ReadAtLeastOneByte
&& Information
== 0))
85 INFO_(SERIAL
, "Buffer empty. Don't wait more bytes\n");
89 Status
= KeWaitForMultipleObjects(
96 (WorkItemData
->UseIntervalTimeout
&& Information
> 0) ? &WorkItemData
->IntervalTimeout
: NULL
,
99 if (Status
== STATUS_TIMEOUT
/* interval timeout */
100 || Status
== STATUS_WAIT_1
) /* total timeout */
102 TRACE_(SERIAL
, "Timeout when reading bytes. Status = 0x%08lx\n", Status
);
107 /* stop total timeout timer */
108 if (WorkItemData
->UseTotalTimeout
)
109 KeCancelTimer(&TotalTimeoutTimer
);
111 Irp
->IoStatus
.Information
= Information
;
112 if (Information
== 0)
113 Irp
->IoStatus
.Status
= STATUS_TIMEOUT
;
115 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
120 IN PDEVICE_OBJECT DeviceObject
,
121 IN PVOID pWorkItemData
/* real type PWORKITEM_DATA */)
123 PWORKITEM_DATA WorkItemData
;
126 TRACE_(SERIAL
, "SerialReadWorkItem() called\n");
128 WorkItemData
= (PWORKITEM_DATA
)pWorkItemData
;
129 Irp
= WorkItemData
->Irp
;
131 ReadBytes(DeviceObject
, Irp
, WorkItemData
);
133 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
135 IoFreeWorkItem(WorkItemData
->IoWorkItem
);
136 ExFreePoolWithTag(pWorkItemData
, SERIAL_TAG
);
141 IN PDEVICE_OBJECT DeviceObject
,
144 PIO_STACK_LOCATION Stack
;
145 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
148 PWORKITEM_DATA WorkItemData
;
149 PIO_WORKITEM WorkItem
;
152 TRACE_(SERIAL
, "IRP_MJ_READ\n");
154 Stack
= IoGetCurrentIrpStackLocation(Irp
);
155 Length
= Stack
->Parameters
.Read
.Length
;
156 Buffer
= SerialGetUserBuffer(Irp
);
157 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
159 if (Stack
->Parameters
.Read
.ByteOffset
.QuadPart
!= 0 || Buffer
== NULL
)
161 Status
= STATUS_INVALID_PARAMETER
;
167 Status
= STATUS_SUCCESS
;
171 /* Allocate memory for parameters */
172 WorkItemData
= ExAllocatePoolWithTag(PagedPool
, sizeof(WORKITEM_DATA
), SERIAL_TAG
);
175 Status
= STATUS_INSUFFICIENT_RESOURCES
;
178 RtlZeroMemory(WorkItemData
, sizeof(WORKITEM_DATA
));
179 WorkItemData
->Irp
= Irp
;
181 /* Calculate time outs */
182 if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
== INFINITE
&&
183 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
== INFINITE
&&
184 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
> 0 &&
185 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
< INFINITE
)
187 /* read at least one byte, and at most bytes already received */
188 WorkItemData
->DontWait
= TRUE
;
189 WorkItemData
->ReadAtLeastOneByte
= TRUE
;
191 else if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
== INFINITE
&&
192 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
== 0 &&
193 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
== 0)
195 /* read only bytes that are already in buffer */
196 WorkItemData
->DontWait
= TRUE
;
201 if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
!= 0)
203 WorkItemData
->UseIntervalTimeout
= TRUE
;
204 WorkItemData
->IntervalTimeout
.QuadPart
= DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
;
206 if (DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
!= 0 ||
207 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
!= 0)
210 LARGE_INTEGER SystemTime
;
212 WorkItemData
->UseTotalTimeout
= TRUE
;
213 TotalTimeout
= DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
+
214 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
* Length
;
215 KeQuerySystemTime(&SystemTime
);
216 WorkItemData
->TotalTimeoutTime
.QuadPart
= SystemTime
.QuadPart
+
217 TotalTimeout
* 10000;
222 WorkItem
= IoAllocateWorkItem(DeviceObject
);
225 WorkItemData
->IoWorkItem
= WorkItem
;
226 IoMarkIrpPending(Irp
);
227 IoQueueWorkItem(WorkItem
, SerialReadWorkItem
, DelayedWorkQueue
, WorkItemData
);
228 return STATUS_PENDING
;
231 /* Insufficient resources, we can't pend the Irp */
232 INFO_(SERIAL
, "Insufficient resources\n");
233 Status
= IoAcquireRemoveLock(&DeviceExtension
->RemoveLock
, ULongToPtr(DeviceExtension
->ComPort
));
234 if (!NT_SUCCESS(Status
))
236 ExFreePoolWithTag(WorkItemData
, SERIAL_TAG
);
239 ReadBytes(DeviceObject
, Irp
, WorkItemData
);
240 Status
= Irp
->IoStatus
.Status
;
242 IoReleaseRemoveLock(&DeviceExtension
->RemoveLock
, ULongToPtr(DeviceExtension
->ComPort
));
245 Irp
->IoStatus
.Status
= Status
;
246 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
252 IN PDEVICE_OBJECT DeviceObject
,
255 PIO_STACK_LOCATION Stack
;
256 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
258 ULONG_PTR Information
= 0;
261 NTSTATUS Status
= STATUS_SUCCESS
;
263 TRACE_(SERIAL
, "IRP_MJ_WRITE\n");
265 /* FIXME: pend operation if possible */
266 /* FIXME: use write timeouts */
268 Stack
= IoGetCurrentIrpStackLocation(Irp
);
269 Length
= Stack
->Parameters
.Write
.Length
;
270 Buffer
= SerialGetUserBuffer(Irp
);
271 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
273 if (Stack
->Parameters
.Write
.ByteOffset
.QuadPart
!= 0 || Buffer
== NULL
)
275 Status
= STATUS_INVALID_PARAMETER
;
279 Status
= IoAcquireRemoveLock(&DeviceExtension
->RemoveLock
, ULongToPtr(DeviceExtension
->ComPort
));
280 if (!NT_SUCCESS(Status
))
283 /* push bytes into output buffer */
284 KeAcquireSpinLock(&DeviceExtension
->OutputBufferLock
, &Irql
);
285 while (Information
< Length
)
287 Status
= PushCircularBufferEntry(&DeviceExtension
->OutputBuffer
, Buffer
[Information
]);
288 if (!NT_SUCCESS(Status
))
290 if (Status
== STATUS_BUFFER_TOO_SMALL
)
292 KeReleaseSpinLock(&DeviceExtension
->OutputBufferLock
, Irql
);
293 SerialSendByte(NULL
, DeviceExtension
, NULL
, NULL
);
294 KeAcquireSpinLock(&DeviceExtension
->OutputBufferLock
, &Irql
);
299 WARN_(SERIAL
, "Buffer overrun on COM%lu\n", DeviceExtension
->ComPort
);
300 DeviceExtension
->SerialPerfStats
.BufferOverrunErrorCount
++;
306 KeReleaseSpinLock(&DeviceExtension
->OutputBufferLock
, Irql
);
307 IoReleaseRemoveLock(&DeviceExtension
->RemoveLock
, ULongToPtr(DeviceExtension
->ComPort
));
310 SerialSendByte(NULL
, DeviceExtension
, NULL
, NULL
);
313 Irp
->IoStatus
.Information
= Information
;
314 Irp
->IoStatus
.Status
= Status
;
315 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);