Correct IOCTL_SERIAL_CLEAR_STATS and IOCTL_SERIAL_PURGE
Manage read errors (overrun, parity, ...)
Use output buffer even if Uart is ready to transmit
Modified: trunk/reactos/drivers/dd/serial/devctrl.c
Modified: trunk/reactos/drivers/dd/serial/misc.c
Modified: trunk/reactos/drivers/dd/serial/rw.c
Modified: trunk/reactos/drivers/dd/serial/serial.h

Modified: trunk/reactos/drivers/dd/serial/devctrl.c
--- trunk/reactos/drivers/dd/serial/devctrl.c	2005-04-04 22:05:08 UTC (rev 14489)
+++ trunk/reactos/drivers/dd/serial/devctrl.c	2005-04-04 22:48:51 UTC (rev 14490)
@@ -182,9 +182,10 @@
 
 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 @@
 	
 	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 @@
 			KeSynchronizeExecution(
 				DeviceExtension->Interrupt,
 				(PKSYNCHRONIZE_ROUTINE)SerialClearPerfStats,
-				&DeviceExtension->SerialPerfStats);
+				DeviceExtension);
 			Status = STATUS_SUCCESS;
 			break;
 		}
@@ -552,27 +564,52 @@
 		}
 		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:

Modified: trunk/reactos/drivers/dd/serial/misc.c
--- trunk/reactos/drivers/dd/serial/misc.c	2005-04-04 22:05:08 UTC (rev 14489)
+++ trunk/reactos/drivers/dd/serial/misc.c	2005-04-04 22:48:51 UTC (rev 14490)
@@ -78,7 +78,7 @@
 	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 @@
 	
 	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 @@
 	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 @@
 		}
 		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;

Modified: trunk/reactos/drivers/dd/serial/rw.c
--- trunk/reactos/drivers/dd/serial/rw.c	2005-04-04 22:05:08 UTC (rev 14489)
+++ trunk/reactos/drivers/dd/serial/rw.c	2005-04-04 22:48:51 UTC (rev 14490)
@@ -274,25 +274,8 @@
 	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 @@
 			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;

Modified: trunk/reactos/drivers/dd/serial/serial.h
--- trunk/reactos/drivers/dd/serial/serial.h	2005-04-04 22:05:08 UTC (rev 14489)
+++ trunk/reactos/drivers/dd/serial/serial.h	2005-04-04 22:48:51 UTC (rev 14490)
@@ -90,6 +90,7 @@
 	UART_TYPE UartType;
 	ULONG WaitMask;
 	
+	ULONG BreakInterruptErrorCount;
 	SERIALPERF_STATS SerialPerfStats;
 	SERIAL_TIMEOUTS SerialTimeOuts;
 	BOOLEAN IsOpened;
@@ -169,11 +170,23 @@
 #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 */