Correct timeout issues when reading

Pend read IRPs
Lots of small issues

svn path=/trunk/; revision=14440
This commit is contained in:
Hervé Poussineau 2005-04-02 14:16:47 +00:00
parent 11fe38608b
commit ce4cbf5e57
7 changed files with 341 additions and 177 deletions

View file

@ -7,7 +7,6 @@
*
* PROGRAMMERS: Hervé Poussineau (poussine@freesurf.fr)
*/
/* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
#define NDEBUG
#include "serial.h"
@ -219,22 +218,30 @@ SerialGetCommProp(
pCommProp->ServiceMask = SERIAL_SP_SERIALCOMM;
pCommProp->MaxTxQueue = pCommProp->CurrentTxQueue = DeviceExtension->OutputBuffer.Length - 1;
pCommProp->MaxRxQueue = pCommProp->CurrentRxQueue = DeviceExtension->InputBuffer.Length - 1;
pCommProp->MaxBaud = SERIAL_BAUD_115200;
pCommProp->ProvSubType = 1; // PST_RS232;
/* FIXME: ProvCapabilities may be related to Uart type */
pCommProp->ProvCapabilities = SERIAL_PCF_DTRDSR | SERIAL_PCF_INTTIMEOUTS | SERIAL_PCF_PARITY_CHECK
| SERIAL_PCF_RTSCTS | SERIAL_PCF_SETXCHAR | SERIAL_PCF_SPECIALCHARS | SERIAL_PCF_TOTALTIMEOUTS
| SERIAL_PCF_XONXOFF;
/* FIXME: SettableParams may be related to Uart type */
pCommProp->SettableParams = SERIAL_SP_BAUD | SERIAL_SP_DATABITS | SERIAL_SP_HANDSHAKING
| SERIAL_SP_PARITY | SERIAL_SP_PARITY_CHECK | SERIAL_SP_STOPBITS;
/* FIXME: SettableBaud may be related to Uart type */
/* SettableBaud is related to Uart type */
pCommProp->SettableBaud = SERIAL_BAUD_075 | SERIAL_BAUD_110 | SERIAL_BAUD_134_5
| SERIAL_BAUD_150 | SERIAL_BAUD_300 | SERIAL_BAUD_600 | SERIAL_BAUD_1200
| SERIAL_BAUD_1800 | SERIAL_BAUD_2400 | SERIAL_BAUD_4800 | SERIAL_BAUD_7200
| SERIAL_BAUD_9600 | SERIAL_BAUD_14400 | SERIAL_BAUD_19200 | SERIAL_BAUD_38400
| SERIAL_BAUD_56K | SERIAL_BAUD_57600 | SERIAL_BAUD_115200 | SERIAL_BAUD_128K
| SERIAL_BAUD_USER;
| SERIAL_BAUD_9600 | SERIAL_BAUD_USER;
pCommProp->MaxBaud = SERIAL_BAUD_9600;
if (DeviceExtension->UartType >= Uart16450)
{
pCommProp->SettableBaud |= SERIAL_BAUD_14400 | SERIAL_BAUD_19200 | SERIAL_BAUD_38400;
pCommProp->MaxBaud = SERIAL_BAUD_38400;
}
if (DeviceExtension->UartType >= Uart16550)
{
pCommProp->SettableBaud |= SERIAL_BAUD_56K | SERIAL_BAUD_57600 | SERIAL_BAUD_115200 | SERIAL_BAUD_128K;
pCommProp->MaxBaud = SERIAL_BAUD_115200;
}
pCommProp->SettableData = SERIAL_DATABITS_5 | SERIAL_DATABITS_6 | SERIAL_DATABITS_7 | SERIAL_DATABITS_8;
pCommProp->SettableStopParity = SERIAL_STOPBITS_10 | SERIAL_STOPBITS_15 | SERIAL_STOPBITS_20
| SERIAL_PARITY_NONE | SERIAL_PARITY_ODD | SERIAL_PARITY_EVEN | SERIAL_PARITY_MARK | SERIAL_PARITY_SPACE;
@ -264,8 +271,8 @@ SerialGetCommStatus(
- DeviceExtension->OutputBuffer.ReadPosition) % DeviceExtension->OutputBuffer.Length;
KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
pSerialStatus->EofReceived = FALSE; /* FIXME */
pSerialStatus->WaitForImmediate = FALSE; /* FIXME */
pSerialStatus->EofReceived = FALSE; /* always FALSE */
pSerialStatus->WaitForImmediate = FALSE; /* always FALSE */
return STATUS_SUCCESS;
}
@ -286,8 +293,6 @@ SerialDeviceControl(
DPRINT("Serial: IRP_MJ_DEVICE_CONTROL dispatch\n");
/* FIXME: pend operation if possible */
Stack = IoGetCurrentIrpStackLocation(Irp);
LengthIn = Stack->Parameters.DeviceIoControl.InputBufferLength;
LengthOut = Stack->Parameters.DeviceIoControl.OutputBufferLength;
@ -315,9 +320,13 @@ SerialDeviceControl(
DPRINT("Serial: IOCTL_SERIAL_CLR_DTR\n");
/* FIXME: If the handshake flow control of the device is configured to
* automatically use DTR, return STATUS_INVALID_PARAMETER */
DeviceExtension->MCR &= ~SR_MCR_DTR;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
Status = STATUS_SUCCESS;
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
DeviceExtension->MCR &= ~SR_MCR_DTR;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
break;
}
case IOCTL_SERIAL_CLR_RTS:
@ -325,9 +334,13 @@ SerialDeviceControl(
DPRINT("Serial: IOCTL_SERIAL_CLR_RTS\n");
/* FIXME: If the handshake flow control of the device is configured to
* automatically use RTS, return STATUS_INVALID_PARAMETER */
DeviceExtension->MCR &= ~SR_MCR_RTS;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
Status = STATUS_SUCCESS;
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
DeviceExtension->MCR &= ~SR_MCR_RTS;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
break;
}
case IOCTL_SERIAL_CONFIG_SIZE:
@ -546,14 +559,20 @@ SerialDeviceControl(
DeviceExtension->InputBuffer.ReadPosition = DeviceExtension->InputBuffer.WritePosition = 0;
DeviceExtension->OutputBuffer.ReadPosition = DeviceExtension->OutputBuffer.WritePosition = 0;
/* Clear receive/transmit buffers */
if (DeviceExtension->UartType >= Uart16550)
if (DeviceExtension->UartType >= Uart16550A)
{
WRITE_PORT_UCHAR(SER_FCR(ComPortBase),
SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
/* 16550 UARTs also have FIFO queues, but they are unusable due to a bug */
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
}
else
Status = STATUS_SUCCESS;
KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql2);
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql1);
Status = STATUS_SUCCESS;
break;
}
case IOCTL_SERIAL_RESET_DEVICE:
@ -604,10 +623,16 @@ SerialDeviceControl(
DPRINT("Serial: IOCTL_SERIAL_SET_DTR\n");
if (!(DeviceExtension->MCR & SR_MCR_DTR))
{
DeviceExtension->MCR |= SR_MCR_DTR;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
DeviceExtension->MCR |= SR_MCR_DTR;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
}
Status = STATUS_SUCCESS;
else
Status = STATUS_SUCCESS;
break;
}
case IOCTL_SERIAL_SET_FIFO_CONTROL:
@ -617,8 +642,12 @@ SerialDeviceControl(
Status = STATUS_INVALID_PARAMETER;
else
{
WRITE_PORT_UCHAR(SER_FCR(ComPortBase), (UCHAR)((*(PULONG)BufferIn) & 0xff));
Status = STATUS_SUCCESS;
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
WRITE_PORT_UCHAR(SER_FCR(ComPortBase), (UCHAR)((*(PULONG)BufferIn) & 0xff));
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
}
break;
}
@ -648,10 +677,14 @@ SerialDeviceControl(
Status = STATUS_INVALID_PARAMETER;
else
{
pMCR = (PULONG)BufferIn;
DeviceExtension->MCR = (UCHAR)(*pMCR & 0xff);
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
Status = STATUS_SUCCESS;
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
pMCR = (PULONG)BufferIn;
DeviceExtension->MCR = (UCHAR)(*pMCR & 0xff);
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
}
break;
}
@ -688,10 +721,16 @@ SerialDeviceControl(
DPRINT("Serial: IOCTL_SERIAL_SET_RTS\n");
if (!(DeviceExtension->MCR & SR_MCR_RTS))
{
DeviceExtension->MCR |= SR_MCR_RTS;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
DeviceExtension->MCR |= SR_MCR_RTS;
WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
}
Status = STATUS_SUCCESS;
else
Status = STATUS_SUCCESS;
break;
}
case IOCTL_SERIAL_SET_TIMEOUTS:

View file

@ -96,8 +96,8 @@ DetectLegacyDevice(
if (!ResourceList)
return STATUS_INSUFFICIENT_RESOURCES;
ResourceList->Count = 1;
ResourceList->List[0].InterfaceType = Isa;
ResourceList->List[0].BusNumber = -1; /* FIXME */
ResourceList->List[0].InterfaceType = InterfaceTypeUndefined;
ResourceList->List[0].BusNumber = -1; /* unknown */
ResourceList->List[0].PartialResourceList.Version = 1;
ResourceList->List[0].PartialResourceList.Revision = 1;
ResourceList->List[0].PartialResourceList.Count = 2;
@ -137,7 +137,7 @@ DetectLegacyDevice(
{
Status = IoReportDetectedDevice(
DriverObject,
ResourceList->List[0].InterfaceType, ResourceList->List[0].BusNumber, -1/*FIXME*/,
ResourceList->List[0].InterfaceType, ResourceList->List[0].BusNumber, -1 /* unknown */,
ResourceList, NULL,
TRUE,
&Pdo);

View file

@ -105,9 +105,6 @@ SerialSendByte(
DeviceExtension = (PSERIAL_DEVICE_EXTENSION)pDeviceExtension;
ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
DPRINT1("Serial: sending bytes (if any) on COM%lu\n",
DeviceExtension->ComPort);
KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
while (!IsCircularBufferEmpty(&DeviceExtension->OutputBuffer)
&& READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_TBE)

View file

@ -80,7 +80,7 @@ SerialAddDeviceInternal(
KeInitializeSpinLock(&DeviceExtension->OutputBufferLock);
KeInitializeDpc(&DeviceExtension->ReceivedByteDpc, SerialReceiveByte, DeviceExtension);
KeInitializeDpc(&DeviceExtension->SendByteDpc, SerialSendByte, DeviceExtension);
//Fdo->Flags |= DO_POWER_PAGEABLE (or DO_POWER_INRUSH?)
Fdo->Flags |= DO_POWER_PAGABLE;
Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
if (!NT_SUCCESS(Status))
{
@ -126,8 +126,8 @@ SerialAddDevice(
* not called with a NULL Pdo. Block this call (blocks
* unfortunately all the other PnP serial ports devices).
*/
//return SerialAddDeviceInternal(DriverObject, Pdo, UartUnknown, NULL);
return STATUS_UNSUCCESSFUL;
return SerialAddDeviceInternal(DriverObject, Pdo, UartUnknown, NULL);
//return STATUS_UNSUCCESSFUL;
}
NTSTATUS STDCALL
@ -230,8 +230,9 @@ SerialPnpStartDevice(
}
/* Clear receive/transmit buffers */
if (DeviceExtension->UartType >= Uart16550)
if (DeviceExtension->UartType >= Uart16550A)
{
/* 16550 UARTs also have FIFO queues, but they are unusable due to a bug */
WRITE_PORT_UCHAR(SER_FCR(ComPortBase),
SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
}
@ -307,6 +308,69 @@ SerialPnp(
{
DPRINT("Serial: IRP_MJ_PNP / IRP_MN_START_DEVICE\n");
/* FIXME: first HACK: PnP manager can send multiple
* IRP_MN_START_DEVICE for one device
*/
if (((PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->PnpState != dsStopped)
{
DPRINT1("Serial: device already started. Ignoring this irp!\n");
Status = STATUS_SUCCESS;
break;
}
/* FIXME: AllocatedResources MUST never be NULL ;
* that's the second HACK because resource arbitration
* doesn't exist in ReactOS yet...
*/
if (Stack->Parameters.StartDevice.AllocatedResources == NULL)
{
ULONG ResourceListSize;
PCM_RESOURCE_LIST ResourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
KIRQL Dirql;
ULONG ComPortBase;
ULONG Irq;
DPRINT1("Serial: no allocated resources for this device! Creating fake list\n");
/* These values are resources of the ONLY serial
* port that will be managed by this driver
* (default is COM2) */
ComPortBase = 0x2f8;
Irq = 3;
/* Create resource list */
ResourceListSize = sizeof(CM_RESOURCE_LIST) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
ResourceList = (PCM_RESOURCE_LIST)ExAllocatePoolWithTag(PagedPool, ResourceListSize, SERIAL_TAG);
if (!ResourceList)
return STATUS_INSUFFICIENT_RESOURCES;
ResourceList->Count = 1;
ResourceList->List[0].InterfaceType = Isa;
ResourceList->List[0].BusNumber = -1; /* FIXME */
ResourceList->List[0].PartialResourceList.Version = 1;
ResourceList->List[0].PartialResourceList.Revision = 1;
ResourceList->List[0].PartialResourceList.Count = 2;
ResourceDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[0];
ResourceDescriptor->Type = CmResourceTypePort;
ResourceDescriptor->ShareDisposition = CmResourceShareDriverExclusive;
ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
ResourceDescriptor->u.Port.Start.u.HighPart = 0;
ResourceDescriptor->u.Port.Start.u.LowPart = ComPortBase;
ResourceDescriptor->u.Port.Length = 8;
ResourceDescriptor = &ResourceList->List[0].PartialResourceList.PartialDescriptors[1];
ResourceDescriptor->Type = CmResourceTypeInterrupt;
ResourceDescriptor->ShareDisposition = CmResourceShareShared;
ResourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
ResourceDescriptor->u.Interrupt.Vector = HalGetInterruptVector(
Internal, 0, 0, Irq,
&Dirql,
&ResourceDescriptor->u.Interrupt.Affinity);
ResourceDescriptor->u.Interrupt.Level = (ULONG)Dirql;
Stack->Parameters.StartDevice.AllocatedResources =
Stack->Parameters.StartDevice.AllocatedResourcesTranslated =
ResourceList;
}
/* Call lower driver */
Status = ForwardIrpAndWait(DeviceObject, Irp);
if (NT_SUCCESS(Status))

View file

@ -15,9 +15,122 @@
static PVOID
SerialGetUserBuffer(IN PIRP Irp)
{
ASSERT(Irp);
return Irp->AssociatedIrp.SystemBuffer;
ASSERT(Irp);
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;
ULONG Information = 0;
LARGE_INTEGER SystemTime, ByteTimeoutTime;
UCHAR ReceivedByte;
BOOLEAN IsByteReceived;
//KIRQL Irql;
DPRINT("Serial: ReadBytes() called\n");
DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
Length = IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length;
Buffer = SerialGetUserBuffer(Irp);
/* FIXME: remove disabling interrupts */
WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER & ~1);
while (Length > 0)
{
/* Calculate dead line to receive the next byte */
KeQuerySystemTime(&SystemTime);
ByteTimeoutTime.QuadPart = SystemTime.QuadPart +
WorkItemData->IntervalTimeout * 10000;
IsByteReceived = FALSE;
while (TRUE)
{
#if 1
if ((READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DR) != 0)
{
ReceivedByte = READ_PORT_UCHAR(ComPortBase);
DPRINT("Serial: received byte 0x%02x (%c)\n", ReceivedByte, ReceivedByte);
IsByteReceived = TRUE;
break;
}
else if (WorkItemData->DontWait &&
!(WorkItemData->ReadAtLeastOneByte && Information == 0))
{
DPRINT("Serial: read buffer empty\n");
break;
}
#else
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
if (!IsCircularBufferEmpty(&DeviceExtension->InputBuffer))
{
CHECKPOINT1;
PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte);
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
DPRINT("Serial: reading byte from buffer 0x%02x (%c)\n", ReceivedByte, ReceivedByte);
IsByteReceived = TRUE;
break;
}
else if (WorkItemData->DontWait &&
!(WorkItemData->ReadAtLeastOneByte && Information == 0))
{
DPRINT("Serial: read buffer empty\n");
break;
}
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
#endif
if (IsByteReceived) break;
KeQuerySystemTime(&SystemTime);
if (WorkItemData->UseIntervalTimeout && Information > 0)
{
if (SystemTime.QuadPart >= ByteTimeoutTime.QuadPart)
break;
}
if (WorkItemData->UseTotalTimeout)
{
if (SystemTime.QuadPart >= WorkItemData->TotalTimeoutTime.QuadPart)
break;
}
}
if (!IsByteReceived) break;
Buffer[Information++] = ReceivedByte;
Length--;
}
/* FIXME: remove enabling interrupts */
WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER);
Irp->IoStatus.Information = Information;
if (Information == 0)
Irp->IoStatus.Status = STATUS_TIMEOUT;
else
Irp->IoStatus.Status = STATUS_SUCCESS;
}
static VOID STDCALL
SerialReadWorkItem(
IN PDEVICE_OBJECT DeviceObject,
IN PVOID pWorkItemData /* real type PWORKITEM_DATA */)
{
PWORKITEM_DATA WorkItemData;
PIRP Irp;
DPRINT("Serial: SerialReadWorkItem() called\n");
WorkItemData = (PWORKITEM_DATA)pWorkItemData;
Irp = WorkItemData->Irp;
ReadBytes(DeviceObject, Irp, WorkItemData);
ExFreePoolWithTag(pWorkItemData, SERIAL_TAG);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
NTSTATUS STDCALL
@ -28,12 +141,10 @@ SerialRead(
PIO_STACK_LOCATION Stack;
PSERIAL_DEVICE_EXTENSION DeviceExtension;
ULONG Length;
ULONG Information = 0;
PUCHAR Buffer;
PUCHAR ComPortBase;
UCHAR ReceivedByte;
KIRQL Irql;
NTSTATUS Status = STATUS_SUCCESS;
PWORKITEM_DATA WorkItemData;
PIO_WORKITEM WorkItem;
NTSTATUS Status;
DPRINT("Serial: IRP_MJ_READ\n");
@ -43,7 +154,6 @@ SerialRead(
Length = Stack->Parameters.Read.Length;
Buffer = SerialGetUserBuffer(Irp);
DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
if (Stack->Parameters.Read.ByteOffset.QuadPart != 0 || Buffer == NULL)
{
@ -57,136 +167,79 @@ SerialRead(
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 = 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)
{
IoQueueWorkItem(WorkItem, SerialReadWorkItem, DelayedWorkQueue, WorkItemData);
IoMarkIrpPending(Irp);
return STATUS_PENDING;
}
/* insufficient resources, we can't pend the Irp */
CHECKPOINT;
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (!NT_SUCCESS(Status))
{
ExFreePoolWithTag(WorkItemData, SERIAL_TAG);
goto ByeBye;
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
while (Length-- > 0)
{
Status = PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte);
if (!NT_SUCCESS(Status))
break;
DPRINT("Serial: read from buffer 0x%x (%c)\n", ReceivedByte, ReceivedByte);
Buffer[Information++] = ReceivedByte;
}
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
if (Length > 0 &&
!(DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant == 0 &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == 0))
{
ULONG IntervalTimeout = 0;
ULONG TotalTimeout = 0;
BOOLEAN UseIntervalTimeout = FALSE;
BOOLEAN UseTotalTimeout = FALSE;
ULONG ThisByteTimeout;
BOOLEAN IsByteReceived;
ULONG i;
/* Extract timeouts informations */
if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout == INFINITE &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier == INFINITE &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant > 0 &&
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant < INFINITE)
{
if (Information > 0)
{
/* don't read mode bytes */
Length = 0;
}
else
{
/* read only one byte */
UseTotalTimeout = TRUE;
TotalTimeout = DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant;
Length = 1;
}
}
else
{
if (DeviceExtension->SerialTimeOuts.ReadIntervalTimeout != 0)
{
UseIntervalTimeout = TRUE;
IntervalTimeout = DeviceExtension->SerialTimeOuts.ReadIntervalTimeout;
}
if (DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant != 0 ||
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier != 0)
{
UseTotalTimeout = TRUE;
TotalTimeout = DeviceExtension->SerialTimeOuts.ReadTotalTimeoutConstant +
DeviceExtension->SerialTimeOuts.ReadTotalTimeoutMultiplier * Length;
}
}
DPRINT("Serial: UseIntervalTimeout = %ws, IntervalTimeout = %lu\n",
UseIntervalTimeout ? L"YES" : L"NO",
UseIntervalTimeout ? IntervalTimeout : 0);
DPRINT("Serial: UseTotalTimeout = %ws, TotalTimeout = %lu\n",
UseTotalTimeout ? L"YES" : L"NO",
UseTotalTimeout ? TotalTimeout : 0);
/* FIXME: it should be better to use input buffer instead of
* disabling interrupts, and try to directly read for port! */
/* FIXME: NtQueryPerformanceCounter gives a more accurate
* timer, but it is not available on all computers. First try
* NtQueryPerformanceCounter, and current method if it is not
* implemented. */
/* FIXME: remove disabling interrupts */
WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER & ~1);
while (Length > 0)
{
ThisByteTimeout = IntervalTimeout;
IsByteReceived = FALSE;
while (TRUE)
{
for (i = 0; i < 1000; i++)
{
#if 1
if ((READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DR) != 0)
{
ReceivedByte = READ_PORT_UCHAR(ComPortBase);
DPRINT("Serial: received byte 0x%02x (%c)\n", ReceivedByte, ReceivedByte);
IsByteReceived = TRUE;
break;
}
#else
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
if (!IsCircularBufferEmpty(&DeviceExtension->InputBuffer))
{
PopCircularBufferEntry(&DeviceExtension->InputBuffer, &ReceivedByte);
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
DPRINT("Serial: reading byte from buffer 0x%02x (%c)\n", ReceivedByte, ReceivedByte);
IsByteReceived = TRUE;
break;
}
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
#endif
KeStallExecutionProcessor(1);
}
if (IsByteReceived) break;
if (UseIntervalTimeout)
{
if (ThisByteTimeout == 0) break; else ThisByteTimeout--;
}
if (UseTotalTimeout)
{
if (TotalTimeout == 0) break; else TotalTimeout--;
}
}
if (!IsByteReceived) break;
Buffer[Information++] = ReceivedByte;
Length--;
}
/* FIXME: remove enabling interrupts */
WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER);
}
Status = STATUS_SUCCESS;
ReadBytes(DeviceObject, Irp, WorkItemData);
Status = Irp->IoStatus.Status;
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
ByeBye:
Irp->IoStatus.Information = Information;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;

View file

@ -7,7 +7,6 @@
*
* PROGRAMMERS: Hervé Poussineau (poussine@freesurf.fr)
*/
/* FIXME: call IoAcquireRemoveLock/IoReleaseRemoveLock around each I/O operation */
//#define NDEBUG
#include "serial.h"

View file

@ -20,7 +20,7 @@
#define STDCALL
#define DPRINT1 DbgPrint("(%s:%d) ", __FILE__, __LINE__), DbgPrint
#define CHECKPOINT1 DbgPrint("(%s:%d)\n")
#define CHECKPOINT1 DbgPrint("(%s:%d)\n", __FILE__, __LINE__)
#define TAG(A, B, C, D) (ULONG)(((A)<<0) + ((B)<<8) + ((C)<<16) + ((D)<<24))
@ -102,6 +102,18 @@ typedef struct _SERIAL_DEVICE_EXTENSION
UCHAR MSR; /* Base+6, Modem Status Register */
} SERIAL_DEVICE_EXTENSION, *PSERIAL_DEVICE_EXTENSION;
typedef struct _WORKITEM_DATA
{
PIRP Irp;
BOOLEAN UseIntervalTimeout;
BOOLEAN UseTotalTimeout;
ULONG IntervalTimeout;
LARGE_INTEGER TotalTimeoutTime;
BOOLEAN DontWait;
BOOLEAN ReadAtLeastOneByte;
} WORKITEM_DATA, *PWORKITEM_DATA;
#define SERIAL_TAG TAG('S', 'e', 'r', 'l')
#define INFINITE ((ULONG)-1)