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
*
* 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);
/* 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);
/* Ensure that all following accesses are done as required */
READ_PORT_UCHAR(SER_RBR(BaseAddress));
WRITE_PORT_UCHAR(SER_IER(BaseAddress), 0);
Found = TRUE;
}
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));
/* 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 */
WRITE_PORT_UCHAR(SER_MCR(BaseAddress), Mcr);
return Found;
/* FIFO is only functional for 16550A+ */
return Uart16550A;
}
NTSTATUS
@ -78,7 +79,8 @@ DetectLegacyDevice(
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 @@ DetectLegacyDevice(
/* 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 @@ DetectLegacyDevice(
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 @@ DetectLegacyDevice(
&Pdo);
if (NT_SUCCESS(Status))
{
Status = SerialAddDeviceInternal(DriverObject, Pdo, &Fdo);
Status = SerialAddDeviceInternal(DriverObject, Pdo, UartType, &Fdo);
if (NT_SUCCESS(Status))
{
Status = SerialPnpStartDevice(Fdo, ResourceList);

View file

@ -17,6 +17,7 @@ NTSTATUS STDCALL
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 @@ SerialAddDeviceInternal(
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 @@ SerialAddDevice(
* 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 @@ SerialPnpStartDevice(
KINTERRUPT_MODE InterruptMode = Latched;
BOOLEAN ShareInterrupt = TRUE;
OBJECT_ATTRIBUTES objectAttributes;
PUCHAR ComPortBase;
UNICODE_STRING KeyName;
HANDLE hKey;
NTSTATUS Status;
@ -197,11 +200,15 @@ SerialPnpStartDevice(
* 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 @@ SerialPnpStartDevice(
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 @@ SerialPnpStartDevice(
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;
}

View file

@ -42,7 +42,8 @@
#error Unknown compiler!
#endif
typedef enum {
typedef enum
{
dsStopped,
dsStarted,
dsPaused,
@ -50,6 +51,15 @@ typedef enum {
dsSurpriseRemoved
} SERIAL_DEVICE_STATE;
typedef enum
{
UartUnknown,
Uart8250,
Uart16450,
Uart16550,
Uart16550A
} UART_TYPE;
typedef struct _CIRCULAR_BUFFER
{
PUCHAR Buffer;
@ -73,6 +83,7 @@ typedef struct _SERIAL_DEVICE_EXTENSION
PKINTERRUPT Interrupt;
SERIAL_LINE_CONTROL SerialLineControl;
UART_TYPE UartType;
ULONG WaitMask;
SERIALPERF_STATS SerialPerfStats;
@ -110,8 +121,12 @@ typedef struct _SERIAL_DEVICE_EXTENSION
#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 @@ SerialQueryInformation(
/************************************ legacy.c */
UART_TYPE
SerialDetectUartType(
IN PUCHAR ComPortBase);
NTSTATUS
DetectLegacyDevices(
IN PDRIVER_OBJECT DriverObject);
@ -237,6 +256,7 @@ NTSTATUS STDCALL
SerialAddDeviceInternal(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT Pdo,
IN UART_TYPE UartType,
OUT PDEVICE_OBJECT* pFdo OPTIONAL);
NTSTATUS STDCALL