Based on work by Mark Junker <mjscod(a)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(a)freesurf.fr)
+ * Mark Junker (mjscod(a)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