Based on work by Mark Junker <mjscod@gmx.de>
- Detect UART type
- Clear transmit/receive FIFO if applicable
Modified: trunk/reactos/drivers/dd/serial/legacy.c
Modified: trunk/reactos/drivers/dd/serial/pnp.c
Modified: trunk/reactos/drivers/dd/serial/serial.h

Modified: trunk/reactos/drivers/dd/serial/legacy.c
--- trunk/reactos/drivers/dd/serial/legacy.c	2005-03-20 18:20:59 UTC (rev 14228)
+++ trunk/reactos/drivers/dd/serial/legacy.c	2005-03-20 18:30:09 UTC (rev 14229)
@@ -6,67 +6,68 @@
  * PURPOSE:         Legacy serial port enumeration
  * 
  * PROGRAMMERS:     HervÚ Poussineau (poussine@freesurf.fr)
+ *                  Mark Junker (mjscod@gmx.de)
  */
 
 #define NDEBUG
 #include "serial.h"
 
-static BOOLEAN
-SerialDoesPortExist(PUCHAR BaseAddress)
+UART_TYPE
+SerialDetectUartType(
+	IN PUCHAR BaseAddress)
 {
-	BOOLEAN Found;
-	BYTE Mcr;
-	BYTE Msr;
+	UCHAR Lcr, TestLcr;
+	UCHAR OldScr, Scr5A, ScrA5;
+	BOOLEAN FifoEnabled;
+	UCHAR NewFifoStatus;
 	
-	Found = FALSE;
+	Lcr = READ_PORT_UCHAR(SER_LCR(BaseAddress));
+	WRITE_PORT_UCHAR(SER_LCR(BaseAddress), Lcr ^ 0xFF);
+	TestLcr = READ_PORT_UCHAR(SER_LCR(BaseAddress)) ^ 0xFF;
+	WRITE_PORT_UCHAR(SER_LCR(BaseAddress), Lcr);
 	
-	/* save Modem Control Register (MCR) */
-	Mcr = READ_PORT_UCHAR(SER_MCR(BaseAddress));
+	/* Accessing the LCR must work for a usable serial port */
+	if (TestLcr != Lcr)
+		return UartUnknown;
 	
-	/* enable loop mode (set Bit 4 of the MCR) */
-	WRITE_PORT_UCHAR(SER_MCR(BaseAddress), 0x10);
+	/* Ensure that all following accesses are done as required */
+	READ_PORT_UCHAR(SER_RBR(BaseAddress));
+	READ_PORT_UCHAR(SER_IER(BaseAddress));
+	READ_PORT_UCHAR(SER_IIR(BaseAddress));
+	READ_PORT_UCHAR(SER_LCR(BaseAddress));
+	READ_PORT_UCHAR(SER_MCR(BaseAddress));
+	READ_PORT_UCHAR(SER_LSR(BaseAddress));
+	READ_PORT_UCHAR(SER_MSR(BaseAddress));
+	READ_PORT_UCHAR(SER_SCR(BaseAddress));
 	
-	/* clear all modem output bits */
-	WRITE_PORT_UCHAR(SER_MCR(BaseAddress), 0x10);
+	/* Test scratch pad */
+	OldScr = READ_PORT_UCHAR(SER_SCR(BaseAddress));
+	WRITE_PORT_UCHAR(SER_SCR(BaseAddress), 0x5A);
+	Scr5A = READ_PORT_UCHAR(SER_SCR(BaseAddress));
+	WRITE_PORT_UCHAR(SER_SCR(BaseAddress), 0xA5);
+	ScrA5 = READ_PORT_UCHAR(SER_SCR(BaseAddress));
+	WRITE_PORT_UCHAR(SER_SCR(BaseAddress), OldScr);
 	
-	/* read the Modem Status Register */
-	Msr = READ_PORT_UCHAR(SER_MSR(BaseAddress));
+	/* When non-functional, we have a 8250 */
+	if (Scr5A != 0x5A || ScrA5 != 0xA5)
+		return Uart8250;
 	
-	/*
-	 * the upper nibble of the MSR (modem output bits) must be
-	 * equal to the lower nibble of the MCR (modem input bits)
-	 */
-	if ((Msr & 0xf0) == 0x00)
+	/* Test FIFO type */
+	FifoEnabled = (READ_PORT_UCHAR(SER_IIR(BaseAddress)) & 0x80) != 0;
+	WRITE_PORT_UCHAR(SER_FCR(BaseAddress), SR_FCR_ENABLE_FIFO);
+	NewFifoStatus = READ_PORT_UCHAR(SER_IIR(BaseAddress)) & 0xC0;
+	if (!FifoEnabled)
+		WRITE_PORT_UCHAR(SER_FCR(BaseAddress), 0);
+	switch (NewFifoStatus)
 	{
-		/* set all modem output bits */
-		WRITE_PORT_UCHAR(SER_MCR(BaseAddress), 0x1f);
-		
-		/* read the Modem Status Register */
-		Msr = READ_PORT_UCHAR(SER_MSR(BaseAddress));
-		
-		/*
-		 * the upper nibble of the MSR (modem output bits) must be
-		 * equal to the lower nibble of the MCR (modem input bits)
-		 */
-		if ((Msr & 0xf0) == 0xf0)
-		{
-			/*
-			 * setup a resonable state for the port:
-			 * enable fifo and clear recieve/transmit buffers
-			 */
-			WRITE_PORT_UCHAR(SER_FCR(BaseAddress),
-				(SR_FCR_ENABLE_FIFO | SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT));
-			WRITE_PORT_UCHAR(SER_FCR(BaseAddress), 0);
-			READ_PORT_UCHAR(SER_RBR(BaseAddress));
-			WRITE_PORT_UCHAR(SER_IER(BaseAddress), 0);
-			Found = TRUE;
-		}
+		case 0x00:
+			return Uart16450;
+		case 0x80:
+			return Uart16550;
 	}
 	
-	/* restore MCR */
-	WRITE_PORT_UCHAR(SER_MCR(BaseAddress), Mcr);
-	
-	return Found;
+	/* FIFO is only functional for 16550A+ */
+	return Uart16550A;
 }
 
 NTSTATUS
@@ -78,7 +79,8 @@
 	ULONG ResourceListSize;
 	PCM_RESOURCE_LIST ResourceList;
 	PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
-	BOOLEAN ConflictDetected, FoundPort;
+	BOOLEAN ConflictDetected;
+	UART_TYPE UartType;
 	PDEVICE_OBJECT Pdo = NULL;
 	PDEVICE_OBJECT Fdo;
 	KIRQL Dirql;
@@ -86,7 +88,7 @@
 	
 	/* Create resource list */
 	ResourceListSize = sizeof(CM_RESOURCE_LIST) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
-	ResourceList = (PCM_RESOURCE_LIST)ExAllocatePoolWithTag(NonPagedPool, ResourceListSize, SERIAL_TAG);
+	ResourceList = (PCM_RESOURCE_LIST)ExAllocatePoolWithTag(PagedPool, ResourceListSize, SERIAL_TAG);
 	if (!ResourceList)
 		return STATUS_INSUFFICIENT_RESOURCES;
 	ResourceList->Count = 1;
@@ -118,16 +120,16 @@
 		DriverObject, ResourceList, ResourceListSize,
 		NULL, NULL, 0,
 		&ConflictDetected);
-	if (ConflictDetected)
+	if (Status == STATUS_CONFLICTING_ADDRESSES)
 		return STATUS_DEVICE_NOT_CONNECTED;
 	if (!NT_SUCCESS(Status))
 		return Status;
 	
 	/* Test if port exists */
-	FoundPort = SerialDoesPortExist((PUCHAR)ComPortBase);
+	UartType = SerialDetectUartType((PUCHAR)ComPortBase);
 	
 	/* Report device if detected... */
-	if (FoundPort)
+	if (UartType != UartUnknown)
 	{
 		Status = IoReportDetectedDevice(
 			DriverObject,
@@ -137,7 +139,7 @@
 			&Pdo);
 		if (NT_SUCCESS(Status))
 		{
-			Status = SerialAddDeviceInternal(DriverObject, Pdo, &Fdo);
+			Status = SerialAddDeviceInternal(DriverObject, Pdo, UartType, &Fdo);
 			if (NT_SUCCESS(Status))
 			{
 				Status = SerialPnpStartDevice(Fdo, ResourceList);

Modified: trunk/reactos/drivers/dd/serial/pnp.c
--- trunk/reactos/drivers/dd/serial/pnp.c	2005-03-20 18:20:59 UTC (rev 14228)
+++ trunk/reactos/drivers/dd/serial/pnp.c	2005-03-20 18:30:09 UTC (rev 14229)
@@ -17,6 +17,7 @@
 SerialAddDeviceInternal(
 	IN PDRIVER_OBJECT DriverObject,
 	IN PDEVICE_OBJECT Pdo,
+	IN UART_TYPE UartType,
 	OUT PDEVICE_OBJECT* pFdo OPTIONAL)
 {
 	PDEVICE_OBJECT Fdo = NULL;
@@ -69,6 +70,7 @@
 	DeviceExtension->SerialPortNumber = DeviceNumber++;
 	DeviceExtension->Pdo = Pdo;
 	DeviceExtension->PnpState = dsStopped;
+	DeviceExtension->UartType = UartType;
 	Status = InitializeCircularBuffer(&DeviceExtension->InputBuffer, 16);
 	if (!NT_SUCCESS(Status)) goto ByeBye;
 	Status = InitializeCircularBuffer(&DeviceExtension->OutputBuffer, 16);
@@ -119,7 +121,7 @@
 	 * not called with a NULL Pdo. Block this call (blocks
 	 * unfortunately all the other PnP serial ports devices).
 	 */
-	//return SerialAddDeviceInternal(DriverObject, Pdo, NULL);
+	//return SerialAddDeviceInternal(DriverObject, Pdo, UartUnknown, NULL);
 	return STATUS_UNSUCCESSFUL;
 }
 
@@ -142,6 +144,7 @@
 	KINTERRUPT_MODE InterruptMode = Latched;
 	BOOLEAN ShareInterrupt = TRUE;
 	OBJECT_ATTRIBUTES objectAttributes;
+	PUCHAR ComPortBase;
 	UNICODE_STRING KeyName;
 	HANDLE hKey;
 	NTSTATUS Status;
@@ -197,11 +200,15 @@
 	 * for read/write if we don't have an interrupt */
 	if (!Dirql)
 		return STATUS_INSUFFICIENT_RESOURCES;
+	ComPortBase = (PUCHAR)DeviceExtension->BaseAddress;
 	
+	if (DeviceExtension->UartType == UartUnknown)
+		DeviceExtension->UartType = SerialDetectUartType(ComPortBase);
+	
 	/* Get current settings */
-	DeviceExtension->IER = READ_PORT_UCHAR(SER_IER((PUCHAR)DeviceExtension->BaseAddress));
-	DeviceExtension->MCR = READ_PORT_UCHAR(SER_MCR((PUCHAR)DeviceExtension->BaseAddress));
-	DeviceExtension->MSR = READ_PORT_UCHAR(SER_MSR((PUCHAR)DeviceExtension->BaseAddress));
+	DeviceExtension->IER = READ_PORT_UCHAR(SER_IER(ComPortBase));
+	DeviceExtension->MCR = READ_PORT_UCHAR(SER_MCR(ComPortBase));
+	DeviceExtension->MSR = READ_PORT_UCHAR(SER_MSR(ComPortBase));
 	DeviceExtension->WaitMask = 0;
 	
 	/* Set baud rate */
@@ -223,6 +230,13 @@
 		return Status;
 	}
 	
+	/* Clear receive/transmit buffers */
+	if (DeviceExtension->UartType >= Uart16550)
+	{
+		WRITE_PORT_UCHAR(SER_FCR(ComPortBase),
+			SR_FCR_CLEAR_RCVR | SR_FCR_CLEAR_XMIT);
+	}
+	
 	/* Create link \DosDevices\COMX -> \Device\SerialX */
 	swprintf(DeviceNameBuffer, L"\\Device\\Serial%lu", DeviceExtension->SerialPortNumber);
 	swprintf(LinkNameBuffer, L"\\DosDevices\\COM%lu", DeviceExtension->ComPort);
@@ -267,10 +281,10 @@
 	
 	DeviceExtension->IER |= 0x1f; /* Activate interrupt mode */
 	DeviceExtension->IER &= ~1; /* FIXME: Disable receive byte interrupt */
-	WRITE_PORT_UCHAR(SER_IER((PUCHAR)DeviceExtension->BaseAddress), DeviceExtension->IER);
+	WRITE_PORT_UCHAR(SER_IER(ComPortBase), DeviceExtension->IER);
 	
 	DeviceExtension->MCR |= 0x03; /* Activate DTR, RTS */
-	WRITE_PORT_UCHAR(SER_MCR((PUCHAR)DeviceExtension->BaseAddress), DeviceExtension->MCR);
+	WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR);
 	
 	return STATUS_SUCCESS;
 }

Modified: trunk/reactos/drivers/dd/serial/serial.h
--- trunk/reactos/drivers/dd/serial/serial.h	2005-03-20 18:20:59 UTC (rev 14228)
+++ trunk/reactos/drivers/dd/serial/serial.h	2005-03-20 18:30:09 UTC (rev 14229)
@@ -42,7 +42,8 @@
   #error Unknown compiler!
 #endif
 
-typedef enum {
+typedef enum
+{
   dsStopped,
   dsStarted,
   dsPaused,
@@ -50,6 +51,15 @@
   dsSurpriseRemoved
 } SERIAL_DEVICE_STATE;
 
+typedef enum
+{
+	UartUnknown,
+	Uart8250,
+	Uart16450,
+	Uart16550,
+	Uart16550A
+} UART_TYPE;
+
 typedef struct _CIRCULAR_BUFFER
 {
 	PUCHAR Buffer;
@@ -73,6 +83,7 @@
 	PKINTERRUPT Interrupt;
 	
 	SERIAL_LINE_CONTROL SerialLineControl;
+	UART_TYPE UartType;
 	ULONG WaitMask;
 	
 	SERIALPERF_STATS SerialPerfStats;
@@ -110,8 +121,12 @@
 #define     SR_IIR_ERROR         (SR_IIR_SELF | 6)
 #define   SER_FCR(x)   ((x)+2)
 #define     SR_FCR_ENABLE_FIFO 0x01
-#define     SR_FCR_CLEAR_RCVR  0x02
-#define     SR_FCR_CLEAR_XMIT  0x04
+#define     SR_FCR_CLEAR_RCVR  (0x02 | SR_FCR_ENABLE_FIFO)
+#define     SR_FCR_CLEAR_XMIT  (0x04 | SR_FCR_ENABLE_FIFO)
+#define     SR_FCR_1_BYTE      (0x00 | SR_FCR_ENABLE_FIFO)
+#define     SR_FCR_4_BYTES     (0x40 | SR_FCR_ENABLE_FIFO)
+#define     SR_FCR_8_BYTES     (0x80 | SR_FCR_ENABLE_FIFO)
+#define     SR_FCR_14_BYTES    (0xC0 | SR_FCR_ENABLE_FIFO)
 #define   SER_LCR(x)   ((x)+3)
 #define     SR_LCR_CS5 0x00
 #define     SR_LCR_CS6 0x01
@@ -210,6 +225,10 @@
 
 /************************************ legacy.c */
 
+UART_TYPE
+SerialDetectUartType(
+	IN PUCHAR ComPortBase);
+
 NTSTATUS
 DetectLegacyDevices(
 	IN PDRIVER_OBJECT DriverObject);
@@ -237,6 +256,7 @@
 SerialAddDeviceInternal(
 	IN PDRIVER_OBJECT DriverObject,
 	IN PDEVICE_OBJECT Pdo,
+	IN UART_TYPE UartType,
 	OUT PDEVICE_OBJECT* pFdo OPTIONAL);
 
 NTSTATUS STDCALL