Based on work by Mark Junker <mjscod@gmx.de>

- Detect UART type
- Clear transmit/receive FIFO if applicable

svn path=/trunk/; revision=14229
This commit is contained in:
Hervé Poussineau 2005-03-20 18:30:09 +00:00
parent 0936234d7d
commit e8ecbebd7d
3 changed files with 97 additions and 61 deletions

View file

@ -6,67 +6,68 @@
* PURPOSE: Legacy serial port enumeration * PURPOSE: Legacy serial port enumeration
* *
* PROGRAMMERS: Hervé Poussineau (poussine@freesurf.fr) * PROGRAMMERS: Hervé Poussineau (poussine@freesurf.fr)
* Mark Junker (mjscod@gmx.de)
*/ */
#define NDEBUG #define NDEBUG
#include "serial.h" #include "serial.h"
static BOOLEAN UART_TYPE
SerialDoesPortExist(PUCHAR BaseAddress) SerialDetectUartType(
IN PUCHAR BaseAddress)
{ {
BOOLEAN Found; UCHAR Lcr, TestLcr;
BYTE Mcr; UCHAR OldScr, Scr5A, ScrA5;
BYTE Msr; 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) */ /* Accessing the LCR must work for a usable serial port */
Mcr = READ_PORT_UCHAR(SER_MCR(BaseAddress)); if (TestLcr != Lcr)
return UartUnknown;
/* enable loop mode (set Bit 4 of the MCR) */ /* Ensure that all following accesses are done as required */
WRITE_PORT_UCHAR(SER_MCR(BaseAddress), 0x10);
/* clear all modem output bits */
WRITE_PORT_UCHAR(SER_MCR(BaseAddress), 0x10);
/* 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) == 0x00)
{
/* 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)); READ_PORT_UCHAR(SER_RBR(BaseAddress));
WRITE_PORT_UCHAR(SER_IER(BaseAddress), 0); READ_PORT_UCHAR(SER_IER(BaseAddress));
Found = TRUE; 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));
/* 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);
/* When non-functional, we have a 8250 */
if (Scr5A != 0x5A || ScrA5 != 0xA5)
return Uart8250;
/* 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)
{
case 0x00:
return Uart16450;
case 0x80:
return Uart16550;
} }
/* restore MCR */ /* FIFO is only functional for 16550A+ */
WRITE_PORT_UCHAR(SER_MCR(BaseAddress), Mcr); return Uart16550A;
return Found;
} }
NTSTATUS NTSTATUS
@ -78,7 +79,8 @@ DetectLegacyDevice(
ULONG ResourceListSize; ULONG ResourceListSize;
PCM_RESOURCE_LIST ResourceList; PCM_RESOURCE_LIST ResourceList;
PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor; PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor;
BOOLEAN ConflictDetected, FoundPort; BOOLEAN ConflictDetected;
UART_TYPE UartType;
PDEVICE_OBJECT Pdo = NULL; PDEVICE_OBJECT Pdo = NULL;
PDEVICE_OBJECT Fdo; PDEVICE_OBJECT Fdo;
KIRQL Dirql; KIRQL Dirql;
@ -86,7 +88,7 @@ DetectLegacyDevice(
/* Create resource list */ /* Create resource list */
ResourceListSize = sizeof(CM_RESOURCE_LIST) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); 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) if (!ResourceList)
return STATUS_INSUFFICIENT_RESOURCES; return STATUS_INSUFFICIENT_RESOURCES;
ResourceList->Count = 1; ResourceList->Count = 1;
@ -118,16 +120,16 @@ DetectLegacyDevice(
DriverObject, ResourceList, ResourceListSize, DriverObject, ResourceList, ResourceListSize,
NULL, NULL, 0, NULL, NULL, 0,
&ConflictDetected); &ConflictDetected);
if (ConflictDetected) if (Status == STATUS_CONFLICTING_ADDRESSES)
return STATUS_DEVICE_NOT_CONNECTED; return STATUS_DEVICE_NOT_CONNECTED;
if (!NT_SUCCESS(Status)) if (!NT_SUCCESS(Status))
return Status; return Status;
/* Test if port exists */ /* Test if port exists */
FoundPort = SerialDoesPortExist((PUCHAR)ComPortBase); UartType = SerialDetectUartType((PUCHAR)ComPortBase);
/* Report device if detected... */ /* Report device if detected... */
if (FoundPort) if (UartType != UartUnknown)
{ {
Status = IoReportDetectedDevice( Status = IoReportDetectedDevice(
DriverObject, DriverObject,
@ -137,7 +139,7 @@ DetectLegacyDevice(
&Pdo); &Pdo);
if (NT_SUCCESS(Status)) if (NT_SUCCESS(Status))
{ {
Status = SerialAddDeviceInternal(DriverObject, Pdo, &Fdo); Status = SerialAddDeviceInternal(DriverObject, Pdo, UartType, &Fdo);
if (NT_SUCCESS(Status)) if (NT_SUCCESS(Status))
{ {
Status = SerialPnpStartDevice(Fdo, ResourceList); Status = SerialPnpStartDevice(Fdo, ResourceList);

View file

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

View file

@ -42,7 +42,8 @@
#error Unknown compiler! #error Unknown compiler!
#endif #endif
typedef enum { typedef enum
{
dsStopped, dsStopped,
dsStarted, dsStarted,
dsPaused, dsPaused,
@ -50,6 +51,15 @@ typedef enum {
dsSurpriseRemoved dsSurpriseRemoved
} SERIAL_DEVICE_STATE; } SERIAL_DEVICE_STATE;
typedef enum
{
UartUnknown,
Uart8250,
Uart16450,
Uart16550,
Uart16550A
} UART_TYPE;
typedef struct _CIRCULAR_BUFFER typedef struct _CIRCULAR_BUFFER
{ {
PUCHAR Buffer; PUCHAR Buffer;
@ -73,6 +83,7 @@ typedef struct _SERIAL_DEVICE_EXTENSION
PKINTERRUPT Interrupt; PKINTERRUPT Interrupt;
SERIAL_LINE_CONTROL SerialLineControl; SERIAL_LINE_CONTROL SerialLineControl;
UART_TYPE UartType;
ULONG WaitMask; ULONG WaitMask;
SERIALPERF_STATS SerialPerfStats; SERIALPERF_STATS SerialPerfStats;
@ -110,8 +121,12 @@ typedef struct _SERIAL_DEVICE_EXTENSION
#define SR_IIR_ERROR (SR_IIR_SELF | 6) #define SR_IIR_ERROR (SR_IIR_SELF | 6)
#define SER_FCR(x) ((x)+2) #define SER_FCR(x) ((x)+2)
#define SR_FCR_ENABLE_FIFO 0x01 #define SR_FCR_ENABLE_FIFO 0x01
#define SR_FCR_CLEAR_RCVR 0x02 #define SR_FCR_CLEAR_RCVR (0x02 | SR_FCR_ENABLE_FIFO)
#define SR_FCR_CLEAR_XMIT 0x04 #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 SER_LCR(x) ((x)+3)
#define SR_LCR_CS5 0x00 #define SR_LCR_CS5 0x00
#define SR_LCR_CS6 0x01 #define SR_LCR_CS6 0x01
@ -210,6 +225,10 @@ SerialQueryInformation(
/************************************ legacy.c */ /************************************ legacy.c */
UART_TYPE
SerialDetectUartType(
IN PUCHAR ComPortBase);
NTSTATUS NTSTATUS
DetectLegacyDevices( DetectLegacyDevices(
IN PDRIVER_OBJECT DriverObject); IN PDRIVER_OBJECT DriverObject);
@ -237,6 +256,7 @@ NTSTATUS STDCALL
SerialAddDeviceInternal( SerialAddDeviceInternal(
IN PDRIVER_OBJECT DriverObject, IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT Pdo, IN PDEVICE_OBJECT Pdo,
IN UART_TYPE UartType,
OUT PDEVICE_OBJECT* pFdo OPTIONAL); OUT PDEVICE_OBJECT* pFdo OPTIONAL);
NTSTATUS STDCALL NTSTATUS STDCALL