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.com)
14 SerialGetUserBuffer(IN PIRP Irp
)
18 return Irp
->AssociatedIrp
.SystemBuffer
;
23 IN PDEVICE_OBJECT DeviceObject
,
25 PWORKITEM_DATA WorkItemData
)
27 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
32 KTIMER TotalTimeoutTimer
;
35 PVOID ObjectsArray
[2];
36 ULONG_PTR Information
= 0;
42 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
43 ComPortBase
= (PUCHAR
)DeviceExtension
->BaseAddress
;
44 Length
= IoGetCurrentIrpStackLocation(Irp
)->Parameters
.Read
.Length
;
45 Buffer
= SerialGetUserBuffer(Irp
);
47 DPRINT("Serial: UseIntervalTimeout = %s, IntervalTimeout = %lu\n",
48 WorkItemData
->UseIntervalTimeout
? "YES" : "NO",
49 WorkItemData
->UseIntervalTimeout
? WorkItemData
->IntervalTimeout
.QuadPart
: 0);
50 DPRINT("Serial: UseTotalTimeout = %s\n",
51 WorkItemData
->UseTotalTimeout
? "YES" : "NO");
54 ObjectsArray
[0] = &DeviceExtension
->InputBufferNotEmpty
;
55 if (WorkItemData
->UseTotalTimeout
)
57 KeInitializeTimer(&TotalTimeoutTimer
);
58 KeSetTimer(&TotalTimeoutTimer
, WorkItemData
->TotalTimeoutTime
, NULL
);
59 ObjectsArray
[ObjectCount
] = &TotalTimeoutTimer
;
63 /* while buffer is not fully filled */
66 /* read already received bytes from buffer */
67 KeAcquireSpinLock(&DeviceExtension
->InputBufferLock
, &Irql
);
68 while (!IsCircularBufferEmpty(&DeviceExtension
->InputBuffer
)
71 PopCircularBufferEntry(&DeviceExtension
->InputBuffer
, &ReceivedByte
);
72 DPRINT("Serial: reading byte from buffer: 0x%02x\n", ReceivedByte
);
74 Buffer
[Information
++] = ReceivedByte
;
77 KeClearEvent(&DeviceExtension
->InputBufferNotEmpty
);
78 KeReleaseSpinLock(&DeviceExtension
->InputBufferLock
, Irql
);
80 if (WorkItemData
->DontWait
81 && !(WorkItemData
->ReadAtLeastOneByte
&& Information
== 0))
83 DPRINT("Serial: buffer empty. Don't wait more bytes\n");
87 Status
= KeWaitForMultipleObjects(
94 (WorkItemData
->UseIntervalTimeout
&& Information
> 0) ? &WorkItemData
->IntervalTimeout
: NULL
,
97 if (Status
== STATUS_TIMEOUT
/* interval timeout */
98 || Status
== STATUS_WAIT_1
) /* total timeout */
100 DPRINT("Serial: timeout when reading bytes. Status = 0x%08lx\n", Status
);
105 /* stop total timeout timer */
106 if (WorkItemData
->UseTotalTimeout
)
107 KeCancelTimer(&TotalTimeoutTimer
);
109 Irp
->IoStatus
.Information
= Information
;
110 if (Information
== 0)
111 Irp
->IoStatus
.Status
= STATUS_TIMEOUT
;
113 Irp
->IoStatus
.Status
= STATUS_SUCCESS
;
118 IN PDEVICE_OBJECT DeviceObject
,
119 IN PVOID pWorkItemData
/* real type PWORKITEM_DATA */)
121 PWORKITEM_DATA WorkItemData
;
124 DPRINT("Serial: SerialReadWorkItem() called\n");
126 WorkItemData
= (PWORKITEM_DATA
)pWorkItemData
;
127 Irp
= WorkItemData
->Irp
;
129 ReadBytes(DeviceObject
, Irp
, WorkItemData
);
131 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
133 IoFreeWorkItem(WorkItemData
->IoWorkItem
);
134 ExFreePoolWithTag(pWorkItemData
, SERIAL_TAG
);
139 IN PDEVICE_OBJECT DeviceObject
,
142 PIO_STACK_LOCATION Stack
;
143 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
146 PWORKITEM_DATA WorkItemData
;
147 PIO_WORKITEM WorkItem
;
150 DPRINT("Serial: IRP_MJ_READ\n");
152 Stack
= IoGetCurrentIrpStackLocation(Irp
);
153 Length
= Stack
->Parameters
.Read
.Length
;
154 Buffer
= SerialGetUserBuffer(Irp
);
155 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
157 if (Stack
->Parameters
.Read
.ByteOffset
.QuadPart
!= 0 || Buffer
== NULL
)
159 Status
= STATUS_INVALID_PARAMETER
;
165 Status
= STATUS_SUCCESS
;
169 /* Allocate memory for parameters */
170 WorkItemData
= ExAllocatePoolWithTag(PagedPool
, sizeof(WORKITEM_DATA
), SERIAL_TAG
);
173 Status
= STATUS_INSUFFICIENT_RESOURCES
;
176 RtlZeroMemory(WorkItemData
, sizeof(WORKITEM_DATA
));
177 WorkItemData
->Irp
= Irp
;
179 /* Calculate time outs */
180 if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
== INFINITE
&&
181 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
== INFINITE
&&
182 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
> 0 &&
183 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
< INFINITE
)
185 /* read at least one byte, and at most bytes already received */
186 WorkItemData
->DontWait
= TRUE
;
187 WorkItemData
->ReadAtLeastOneByte
= TRUE
;
189 else if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
== INFINITE
&&
190 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
== 0 &&
191 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
== 0)
193 /* read only bytes that are already in buffer */
194 WorkItemData
->DontWait
= TRUE
;
199 if (DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
!= 0)
201 WorkItemData
->UseIntervalTimeout
= TRUE
;
202 WorkItemData
->IntervalTimeout
.QuadPart
= DeviceExtension
->SerialTimeOuts
.ReadIntervalTimeout
;
204 if (DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
!= 0 ||
205 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
!= 0)
208 LARGE_INTEGER SystemTime
;
210 WorkItemData
->UseTotalTimeout
= TRUE
;
211 TotalTimeout
= DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutConstant
+
212 DeviceExtension
->SerialTimeOuts
.ReadTotalTimeoutMultiplier
* Length
;
213 KeQuerySystemTime(&SystemTime
);
214 WorkItemData
->TotalTimeoutTime
.QuadPart
= SystemTime
.QuadPart
+
215 TotalTimeout
* 10000;
220 WorkItem
= IoAllocateWorkItem(DeviceObject
);
223 WorkItemData
->IoWorkItem
= WorkItem
;
224 IoQueueWorkItem(WorkItem
, SerialReadWorkItem
, DelayedWorkQueue
, WorkItemData
);
225 IoMarkIrpPending(Irp
);
226 return STATUS_PENDING
;
229 /* insufficient resources, we can't pend the Irp */
231 Status
= IoAcquireRemoveLock(&DeviceExtension
->RemoveLock
, (PVOID
)DeviceExtension
->ComPort
);
232 if (!NT_SUCCESS(Status
))
234 ExFreePoolWithTag(WorkItemData
, SERIAL_TAG
);
237 ReadBytes(DeviceObject
, Irp
, WorkItemData
);
238 Status
= Irp
->IoStatus
.Status
;
240 IoReleaseRemoveLock(&DeviceExtension
->RemoveLock
, (PVOID
)DeviceExtension
->ComPort
);
243 Irp
->IoStatus
.Status
= Status
;
244 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
250 IN PDEVICE_OBJECT DeviceObject
,
253 PIO_STACK_LOCATION Stack
;
254 PSERIAL_DEVICE_EXTENSION DeviceExtension
;
256 ULONG_PTR Information
= 0;
260 NTSTATUS Status
= STATUS_SUCCESS
;
262 DPRINT("Serial: IRP_MJ_WRITE\n");
264 /* FIXME: pend operation if possible */
265 /* FIXME: use write timeouts */
267 Stack
= IoGetCurrentIrpStackLocation(Irp
);
268 Length
= Stack
->Parameters
.Write
.Length
;
269 Buffer
= SerialGetUserBuffer(Irp
);
270 DeviceExtension
= (PSERIAL_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
271 ComPortBase
= (PUCHAR
)DeviceExtension
->BaseAddress
;
273 if (Stack
->Parameters
.Write
.ByteOffset
.QuadPart
!= 0 || Buffer
== NULL
)
275 Status
= STATUS_INVALID_PARAMETER
;
279 Status
= IoAcquireRemoveLock(&DeviceExtension
->RemoveLock
, (PVOID
)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 DPRINT("Serial: buffer overrun on COM%lu\n", DeviceExtension
->ComPort
);
300 DeviceExtension
->SerialPerfStats
.BufferOverrunErrorCount
++;
306 KeReleaseSpinLock(&DeviceExtension
->OutputBufferLock
, Irql
);
307 IoReleaseRemoveLock(&DeviceExtension
->RemoveLock
, (PVOID
)DeviceExtension
->ComPort
);
310 SerialSendByte(NULL
, DeviceExtension
, NULL
, NULL
);
313 Irp
->IoStatus
.Information
= Information
;
314 Irp
->IoStatus
.Status
= Status
;
315 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);