Correct IOCTL_SERIAL_CLEAR_STATS and IOCTL_SERIAL_PURGE

Manage read errors (overrun, parity, ...)
Use output buffer even if Uart is ready to transmit

svn path=/trunk/; revision=14490
This commit is contained in:
Hervé Poussineau
2005-04-04 22:48:51 +00:00
parent ff0ad6c8cb
commit d9bef42fde
4 changed files with 111 additions and 68 deletions
+57 -20
View File
@@ -182,9 +182,10 @@ SerialSetLineControl(
BOOLEAN
SerialClearPerfStats(
IN PSERIALPERF_STATS pSerialPerfStats)
IN PSERIAL_DEVICE_EXTENSION DeviceExtension)
{
RtlZeroMemory(pSerialPerfStats, sizeof(SERIALPERF_STATS));
RtlZeroMemory(&DeviceExtension->SerialPerfStats, sizeof(SERIALPERF_STATS));
DeviceExtension->BreakInterruptErrorCount = 0;
return TRUE;
}
@@ -258,7 +259,18 @@ SerialGetCommStatus(
RtlZeroMemory(pSerialStatus, sizeof(SERIAL_STATUS));
pSerialStatus->Errors = 0; /* FIXME */
pSerialStatus->Errors = 0;
if (DeviceExtension->BreakInterruptErrorCount)
pSerialStatus->Errors |= SERIAL_ERROR_BREAK;
if (DeviceExtension->SerialPerfStats.FrameErrorCount)
pSerialStatus->Errors |= SERIAL_ERROR_FRAMING;
if (DeviceExtension->SerialPerfStats.SerialOverrunErrorCount)
pSerialStatus->Errors |= SERIAL_ERROR_OVERRUN;
if (DeviceExtension->SerialPerfStats.BufferOverrunErrorCount)
pSerialStatus->Errors |= SERIAL_ERROR_QUEUEOVERRUN;
if (DeviceExtension->SerialPerfStats.ParityErrorCount)
pSerialStatus->Errors |= SERIAL_ERROR_PARITY;
pSerialStatus->HoldReasons = 0; /* FIXME */
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
@@ -311,7 +323,7 @@ SerialDeviceControl(
KeSynchronizeExecution(
DeviceExtension->Interrupt,
(PKSYNCHRONIZE_ROUTINE)SerialClearPerfStats,
&DeviceExtension->SerialPerfStats);
DeviceExtension);
Status = STATUS_SUCCESS;
break;
}
@@ -552,27 +564,52 @@ SerialDeviceControl(
}
case IOCTL_SERIAL_PURGE:
{
KIRQL Irql1, Irql2;
KIRQL Irql;
DPRINT("Serial: IOCTL_SERIAL_PURGE\n");
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql1);
KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql2);
DeviceExtension->InputBuffer.ReadPosition = DeviceExtension->InputBuffer.WritePosition = 0;
DeviceExtension->OutputBuffer.ReadPosition = DeviceExtension->OutputBuffer.WritePosition = 0;
/* Clear receive/transmit buffers */
if (DeviceExtension->UartType >= Uart16550A)
/* FIXME: SERIAL_PURGE_RXABORT and SERIAL_PURGE_TXABORT
* should stop current request */
if (LengthIn != sizeof(ULONG) || BufferIn == NULL)
Status = STATUS_INVALID_PARAMETER;
else
{
/* 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))
ULONG PurgeMask = *(PULONG)BufferIn;
Status = STATUS_SUCCESS;
/* FIXME: use SERIAL_PURGE_RXABORT and SERIAL_PURGE_TXABORT flags */
if (PurgeMask & SERIAL_PURGE_RXCLEAR)
{
WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
DeviceExtension->InputBuffer.ReadPosition = DeviceExtension->InputBuffer.WritePosition = 0;
if (DeviceExtension->UartType >= Uart16550A)
{
/* Clear also Uart FIFO */
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_RCVR);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
}
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql);
}
if (PurgeMask & SERIAL_PURGE_TXCLEAR)
{
KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
DeviceExtension->OutputBuffer.ReadPosition = DeviceExtension->OutputBuffer.WritePosition = 0;
if (DeviceExtension->UartType >= Uart16550A)
{
/* Clear also Uart FIFO */
Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
if (NT_SUCCESS(Status))
{
WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_XMIT);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
}
}
KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
}
}
else
Status = STATUS_SUCCESS;
KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql2);
KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql1);
break;
}
case IOCTL_SERIAL_RESET_DEVICE:
+33 -25
View File
@@ -78,7 +78,7 @@ SerialReceiveByte(
ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql);
while (READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DR)
while (READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DATA_RECEIVED)
{
Byte = READ_PORT_UCHAR(SER_RBR(ComPortBase));
DPRINT("Serial: Byte received on COM%lu: 0x%02x\n",
@@ -116,7 +116,7 @@ SerialSendByte(
KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
while (!IsCircularBufferEmpty(&DeviceExtension->OutputBuffer)
&& READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_TBE)
&& READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_THR_EMPTY)
{
Status = PopCircularBufferEntry(&DeviceExtension->OutputBuffer, &Byte);
if (!NT_SUCCESS(Status))
@@ -153,16 +153,28 @@ SerialInterruptService(
Iir &= SR_IIR_ID_MASK;
if ((Iir & SR_IIR_SELF) != 0) { return FALSE; }
/* FIXME: sometimes, update DeviceExtension->MCR */
switch (Iir)
{
case SR_IIR_MSR_CHANGE:
{
UCHAR IER;
DPRINT1("Serial: SR_IIR_MSR_CHANGE\n");
UCHAR MSR, IER;
DPRINT("Serial: SR_IIR_MSR_CHANGE\n");
DeviceExtension->MSR = READ_PORT_UCHAR(SER_MSR(ComPortBase));
/* FIXME: what to do? */
MSR = READ_PORT_UCHAR(SER_MSR(ComPortBase));
if (MSR & SR_MSR_CTS_CHANGED)
{
if (MSR & SR_MSR_CTS)
KeInsertQueueDpc(&DeviceExtension->SendByteDpc, NULL, NULL);
else
; /* FIXME: stop transmission */
}
if (MSR & SR_MSR_DSR_CHANGED)
{
if (MSR & SR_MSR_DSR)
KeInsertQueueDpc(&DeviceExtension->ReceivedByteDpc, NULL, NULL);
else
; /* FIXME: stop reception */
}
IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_MSR_CHANGE);
return TRUE;
@@ -183,24 +195,20 @@ SerialInterruptService(
}
case SR_IIR_ERROR:
{
/* FIXME: what to do? */
DPRINT1("Serial: SR_IIR_ERROR\n");
break;
/*Error = READ_PORT_UCHAR( Self->Port + UART_LSR );
if( Error & LSR_OVERRUN )
Self->WaitingReadBytes.PushBack( SerialFifo::OVERRUN );
DeviceExtension->SerialPerfStats.SerialOverrunErrorCount++;
if( Error & LSR_PARITY_ERROR )
Self->WaitingReadBytes.PushBack( SerialFifo::PARITY );
DeviceExtension->SerialPerfStats.ParityErrorCount++;
if( Error & LSR_FRAMING_ERROR )
Self->WaitingReadBytes.PushBack( SerialFifo::FRAMING );
DeviceExtension->SerialPerfStats.FrameErrorCount++;
if( Error & LSR_BREAK )
Self->WaitingReadBytes.PushBack( SerialFifo::BREAK );
if( Error & LSR_TIMEOUT )
Self->WaitingReadBytes.PushBack( SerialFifo::TIMEOUT );
return KeInsertQueueDpc( &Self->DataInDpc, Self, 0 );*/
UCHAR LSR;
DPRINT("Serial: SR_IIR_ERROR\n");
LSR = READ_PORT_UCHAR(SER_LSR(ComPortBase));
if (LSR & SR_LSR_OVERRUN_ERROR)
InterlockedIncrement(&DeviceExtension->SerialPerfStats.SerialOverrunErrorCount);
if (LSR & SR_LSR_PARITY_ERROR)
InterlockedIncrement(&DeviceExtension->SerialPerfStats.ParityErrorCount);
if (LSR & SR_LSR_FRAMING_ERROR)
InterlockedIncrement(&DeviceExtension->SerialPerfStats.FrameErrorCount);
if (LSR & SR_LSR_BREAK_INT)
InterlockedIncrement(&DeviceExtension->BreakInterruptErrorCount);
return TRUE;
}
}
return FALSE;
+4 -19
View File
@@ -274,25 +274,8 @@ SerialWrite(
if (!NT_SUCCESS(Status))
goto ByeBye;
/* push bytes into output buffer */
KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql);
if (IsCircularBufferEmpty(&DeviceExtension->OutputBuffer))
{
/* Put the maximum amount of data in UART output buffer */
while (Information < Length)
{
/* if UART output buffer is not full, directly write to it */
if ((READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_TBE) != 0)
{
DPRINT("Serial: direct write 0x%02x (%c)\n", Buffer[Information], Buffer[Information]);
WRITE_PORT_UCHAR(SER_THR(ComPortBase), Buffer[Information]);
DeviceExtension->SerialPerfStats.TransmittedCount++;
Information++;
}
else
break;
}
}
/* write remaining bytes into output buffer */
while (Information < Length)
{
Status = PushCircularBufferEntry(&DeviceExtension->OutputBuffer, Buffer[Information]);
@@ -302,11 +285,13 @@ SerialWrite(
DeviceExtension->SerialPerfStats.BufferOverrunErrorCount++;
break;
}
DPRINT1("Serial: write to buffer 0x%02x\n", Buffer[Information]);
Information++;
}
KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql);
IoReleaseRemoveLock(&DeviceExtension->RemoveLock, (PVOID)DeviceExtension->ComPort);
/* send bytes */
SerialSendByte(NULL, DeviceExtension, NULL, NULL);
ByeBye:
Irp->IoStatus.Information = Information;
+17 -4
View File
@@ -90,6 +90,7 @@ typedef struct _SERIAL_DEVICE_EXTENSION
UART_TYPE UartType;
ULONG WaitMask;
ULONG BreakInterruptErrorCount;
SERIALPERF_STATS SerialPerfStats;
SERIAL_TIMEOUTS SerialTimeOuts;
BOOLEAN IsOpened;
@@ -169,11 +170,23 @@ typedef struct _WORKITEM_DATA
#define SR_MCR_DTR 0x01
#define SR_MCR_RTS 0x02
#define SER_LSR(x) ((x)+5) /* Line Status Register */
#define SR_LSR_DR 0x01
#define SR_LSR_TBE 0x20
#define SR_LSR_DATA_RECEIVED 0x01
#define SR_LSR_OVERRUN_ERROR 0x02
#define SR_LSR_PARITY_ERROR 0x04
#define SR_LSR_FRAMING_ERROR 0x08
#define SR_LSR_BREAK_INT 0x10
#define SR_LSR_THR_EMPTY 0x20
#define SR_LSR_TSR_EMPTY 0x40
#define SR_LSR_ERROR_IN_FIFO 0x80 /* Uart >= 16550A */
#define SER_MSR(x) ((x)+6) /* Modem Status Register */
#define SR_MSR_CTS 0x10
#define SR_MSR_DSR 0x20
#define SR_MSR_CTS_CHANGED 0x01
#define SR_MSR_DSR_CHANGED 0x02
#define SR_MSR_RI_CHANGED 0x04
#define SR_MSR_DCD_CHANGED 0x08
#define SR_MSR_CTS 0x10 /* Clear To Send */
#define SR_MSR_DSR 0x20 /* Data Set Ready */
#define SI_MSR_RI 0x40 /* Ring Indicator */
#define SR_MSR_DCD 0x80 /* Data Carrier Detect */
#define SER_SCR(x) ((x)+7) /* Scratch Pad Register */
/************************************ circularbuffer.c */