diff --git a/reactos/drivers/dd/serial/legacy.c b/reactos/drivers/dd/serial/legacy.c index b22e50daadb..8b000561025 100644 --- a/reactos/drivers/dd/serial/legacy.c +++ b/reactos/drivers/dd/serial/legacy.c @@ -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); diff --git a/reactos/drivers/dd/serial/pnp.c b/reactos/drivers/dd/serial/pnp.c index 8658eb731ce..8708c237085 100644 --- a/reactos/drivers/dd/serial/pnp.c +++ b/reactos/drivers/dd/serial/pnp.c @@ -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; } diff --git a/reactos/drivers/dd/serial/serial.h b/reactos/drivers/dd/serial/serial.h index fdfaf278a6c..e4f1f801eee 100644 --- a/reactos/drivers/dd/serial/serial.h +++ b/reactos/drivers/dd/serial/serial.h @@ -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