mirror of
https://github.com/reactos/reactos.git
synced 2025-08-06 17:53:21 +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
|
* 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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue