mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 17:44:45 +00:00
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:
parent
0936234d7d
commit
e8ecbebd7d
3 changed files with 97 additions and 61 deletions
|
@ -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 @@ 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue