reactos/drivers/serial/serial/rw.c
Timo Kreuzer 9ea495ba33 Create a branch for header work.
svn path=/branches/header-work/; revision=45691
2010-02-26 22:57:55 +00:00

321 lines
8.8 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: Serial port driver
* FILE: drivers/dd/serial/create.c
* PURPOSE: Serial IRP_MJ_READ/IRP_MJ_WRITE operations
*
* PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
*/
#include "serial.h"
static IO_WORKITEM_ROUTINE SerialReadWorkItem;
static PVOID
SerialGetUserBuffer(IN PIRP Irp)
{
ASSERT(Irp);
if (Irp->MdlAddress)
return Irp->MdlAddress;
else
return Irp->AssociatedIrp.SystemBuffer;
}
static VOID
ReadBytes(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
PWORKITEM_DATA WorkItemData)
{
PSERIAL_DEVICE_EXTENSION DeviceExtension;
PUCHAR ComPortBase;
ULONG Length;
PUCHAR Buffer;
UCHAR ReceivedByte;
KTIMER TotalTimeoutTimer;
KIRQL Irql;
ULONG ObjectCount;
PVOID ObjectsArray[2];
ULONG_PTR Information = 0;
NTSTATUS Status;
ASSERT(DeviceObject);
ASSERT(WorkItemData);
DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ComPortBase = ULongToPtr(DeviceExtension->BaseAddress);
Length = IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length;
Buffer = SerialGetUserBuffer(Irp);
INFO_(SERIAL, "UseIntervalTimeout = %s, IntervalTimeout = %lu\n",
WorkItemData->UseIntervalTimeout ? "YES" : "NO",
WorkItemData->UseIntervalTimeout ? WorkItemData->IntervalTimeout.QuadPart : 0);
INFO_(SERIAL, "UseTotalTimeout = %s\n",
WorkItemData->UseTotalTimeout ? "YES" : "NO");
ObjectCount = 1;
ObjectsArray[0] = &DeviceExtension->InputBufferNotEmpty;
if (WorkItemData->UseTotalTimeout)
{
KeInitializeTimer(&TotalTimeoutTimer);
KeSetTimer(&TotalTimeoutTimer, WorkItemData->TotalTimeoutTime, NULL);
ObjectsArray[ObjectCount] = &TotalTimeoutTimer;
ObjectCount++;
}
/* while buffer is not fully filled */
while (Length > 0)
{
/* read already received bytes from buffer */
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
while (!IsCircularBufferEmpty(&DeviceExtension->InputBuffer)
&& Length > 0)
{
PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte);
INFO_(SERIAL, "Reading byte from buffer: 0x%02x\n", ReceivedByte);
Buffer[Information++] = ReceivedByte;
Length--;
}
KeClearEvent(&DeviceExtension->InputBufferNotEmpty);
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
if (WorkItemData->DontWait
&& !(WorkItemData->ReadAtLeastOneByte && Information == 0))
{
INFO_(SERIAL, "Buffer empty. Don't wait more bytes\n");
break;
}
Status = KeWaitForMultipleObjects(
ObjectCount,
ObjectsArray,
WaitAny,
Executive,
KernelMode,
FALSE,
(WorkItemData->UseIntervalTimeout && Information > 0) ? &WorkItemData->IntervalTimeout : NULL,
NULL);
if (Status == STATUS_TIMEOUT /* interval timeout */
|| Status == STATUS_WAIT_1) /* total timeout */
{
TRACE_(SERIAL, "Timeout when reading bytes. Status = 0x%08lx\n", Status);
break;
}
}
/* stop total timeout timer */
if (WorkItemData->UseTotalTimeout)
KeCancelTimer(&TotalTimeoutTimer);
Irp->IoStatus.Information = Information;
if (Information == 0)
Irp->IoStatus.Status = STATUS_TIMEOUT;
else
Irp->IoStatus.Status = STATUS_SUCCESS;
}
static VOID NTAPI
SerialReadWorkItem(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID pWorkItemData /* real type PWORKITEM_DATA */)
{
PWORKITEM_DATA WorkItemData;
PIRP Irp;
TRACE_(SERIAL, "SerialReadWorkItem() called\n");
WorkItemData = (PWORKITEM_DATA)pWorkItemData;
Irp = WorkItemData->Irp;
ReadBytes(DeviceObject, Irp, WorkItemData);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
IoFreeWorkItem(WorkItemData->IoWorkItem);
ExFreePoolWithTag(pWorkItemData, SERIAL_TAG);
}
NTSTATUS NTAPI
SerialRead(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION Stack;
PSERIAL_DEVICE_EXTENSION DeviceExtension;
ULONG Length;
PUCHAR Buffer;
PWORKITEM_DATA WorkItemData;
PIO_WORKITEM WorkItem;
NTSTATUS Status;
TRACE_(SERIAL, "IRP_MJ_READ\n");
Stack = IoGetCurrentIrpStackLocation(Irp);
Length = Stack->Parameters.Read.Length;
Buffer = SerialGetUserBuffer(Irp);
DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
if (Stack->Parameters.Read.ByteOffset.QuadPart != 0 || Buffer == NULL)
{
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
if (Length == 0)
{
Status = STATUS_SUCCESS;
goto ByeBye;
}
/* Allocate memory for parameters */
WorkItemData = ExAllocatePoolWithTag(PagedPool, sizeof(WORKITEM_DATA), SERIAL_TAG);
if (!WorkItemData)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ByeBye;
}
RtlZeroMemory(WorkItemData, sizeof(WORKITEM_DATA));
WorkItemData->Irp = Irp;
/* Calculate time outs */
if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == INFINITE &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant > 0 &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant < INFINITE)
{
/* read at least one byte, and at most bytes already received */
WorkItemData->DontWait = TRUE;
WorkItemData->ReadAtLeastOneByte = TRUE;
}
else if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0 &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0)
{
/* read only bytes that are already in buffer */
WorkItemData->DontWait = TRUE;
}
else
{
/* use timeouts */
if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout != 0)
{
WorkItemData->UseIntervalTimeout = TRUE;
WorkItemData->IntervalTimeout.QuadPart = DeviceExtension->SerialTimeOuts.ReadIntervalTimeout;
}
if (DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier != 0 ||
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant != 0)
{
ULONG TotalTimeout;
LARGE_INTEGER SystemTime;
WorkItemData->UseTotalTimeout = TRUE;
TotalTimeout = DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant +
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier * Length;
KeQuerySystemTime(&SystemTime);
WorkItemData->TotalTimeoutTime.QuadPart = SystemTime.QuadPart +
TotalTimeout * 10000;
}
}
/* Pend IRP */
WorkItem = IoAllocateWorkItem(DeviceObject);
if (WorkItem)
{
WorkItemData->IoWorkItem = WorkItem;
IoMarkIrpPending(Irp);
IoQueueWorkItem(WorkItem, SerialReadWorkItem, DelayedWorkQueue, WorkItemData);
return STATUS_PENDING;
}
/* Insufficient resources, we can't pend the Irp */
INFO_(SERIAL, "Insufficient resources\n");
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(WorkItemData, SERIAL_TAG);
goto ByeBye;
}
ReadBytes(DeviceObject, Irp, WorkItemData);
Status = Irp->IoStatus.Status;
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
ByeBye:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
NTSTATUS NTAPI
SerialWrite(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
PIO_STACK_LOCATION Stack;
PSERIAL_DEVICE_EXTENSION DeviceExtension;
ULONG Length;
ULONG_PTR Information = 0;
PUCHAR Buffer;
PUCHAR ComPortBase;
KIRQL Irql;
NTSTATUS Status = STATUS_SUCCESS;
TRACE_(SERIAL, "IRP_MJ_WRITE\n");
/* FIXME: pend operation if possible */
/* FIXME: use write timeouts */
Stack = IoGetCurrentIrpStackLocation(Irp);
Length = Stack->Parameters.Write.Length;
Buffer = SerialGetUserBuffer(Irp);
DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ComPortBase = ULongToPtr(DeviceExtension->BaseAddress);
if (Stack->Parameters.Write.ByteOffset.QuadPart != 0 || Buffer == NULL)
{
Status = STATUS_INVALID_PARAMETER;
goto ByeBye;
}
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
if (!NT_SUCCESS(Status))
goto ByeBye;
/* push bytes into output buffer */
KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
while (Information < Length)
{
Status = PushCircularBufferEntry(&DeviceExtension->OutputBuffer, Buffer[Information]);
if (!NT_SUCCESS(Status))
{
if (Status == STATUS_BUFFER_TOO_SMALL)
{
KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
SerialSendByte(NULL, DeviceExtension, NULL, NULL);
KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
continue;
}
else
{
WARN_(SERIAL, "Buffer overrun on COM%lu\n", DeviceExtension->ComPort);
DeviceExtension->SerialPerfStats.BufferOverrunErrorCount++;
break;
}
}
Information++;
}
KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort));
/* send bytes */
SerialSendByte(NULL, DeviceExtension, NULL, NULL);
ByeBye:
Irp->IoStatus.Information = Information;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}