void dc_dbg_init() { #ifdef DBG_COM u32 divisor; u8 lcr; /* set baud rate and data format (8N1) */ /* turn on DTR and RTS */ WRITE_PORT_UCHAR(pv(SER_MCR(COM_BASE)), SR_MCR_DTR | SR_MCR_RTS); /* set DLAB */ lcr = READ_PORT_UCHAR(pv(SER_LCR(COM_BASE))) | SR_LCR_DLAB; WRITE_PORT_UCHAR(pv(SER_LCR(COM_BASE)), lcr); /* set baud rate */ divisor = 115200 / DEFAULT_BAUD_RATE; WRITE_PORT_UCHAR(pv(SER_DLL(COM_BASE)), divisor & 0xff); WRITE_PORT_UCHAR(pv(SER_DLM(COM_BASE)), (divisor >> 8) & 0xff); /* reset DLAB and set 8N1 format */ WRITE_PORT_UCHAR(pv(SER_LCR(COM_BASE)), SR_LCR_CS8 | SR_LCR_ST1 | SR_LCR_PNO); /* read junk out of the RBR */ READ_PORT_UCHAR(pv(SER_RBR(COM_BASE))); #endif /* DBG_COM */ #ifdef DBG_HAL_DISPLAY InbvAcquireDisplayOwnership(); InbvEnableDisplayString(TRUE); #endif /* DBG_HAL_DISPLAY */ }
NTSTATUS NTAPI SerialDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION Stack; ULONG IoControlCode; PSERIAL_DEVICE_EXTENSION DeviceExtension; ULONG LengthIn, LengthOut; ULONG_PTR Information = 0; PVOID BufferIn, BufferOut; PUCHAR ComPortBase; NTSTATUS Status; TRACE_(SERIAL, "IRP_MJ_DEVICE_CONTROL dispatch\n"); Stack = IoGetCurrentIrpStackLocation(Irp); LengthIn = Stack->Parameters.DeviceIoControl.InputBufferLength; LengthOut = Stack->Parameters.DeviceIoControl.OutputBufferLength; DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ComPortBase = ULongToPtr(DeviceExtension->BaseAddress); IoControlCode = Stack->Parameters.DeviceIoControl.IoControlCode; SerialGetUserBuffers(Irp, IoControlCode, &BufferIn, &BufferOut); /* FIXME: need to probe buffers */ /* FIXME: see http://www.osronline.com/ddkx/serial/serref_61bm.htm */ switch (IoControlCode) { case IOCTL_SERIAL_CLEAR_STATS: { TRACE_(SERIAL, "IOCTL_SERIAL_CLEAR_STATS\n"); KeSynchronizeExecution( DeviceExtension->Interrupt, (PKSYNCHRONIZE_ROUTINE)SerialClearPerfStats, DeviceExtension); Status = STATUS_SUCCESS; break; } case IOCTL_SERIAL_CLR_DTR: { TRACE_(SERIAL, "IOCTL_SERIAL_CLR_DTR\n"); /* FIXME: If the handshake flow control of the device is configured to * automatically use DTR, return STATUS_INVALID_PARAMETER */ Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); if (NT_SUCCESS(Status)) { DeviceExtension->MCR &= ~SR_MCR_DTR; WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR); IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); } break; } case IOCTL_SERIAL_CLR_RTS: { TRACE_(SERIAL, "IOCTL_SERIAL_CLR_RTS\n"); /* FIXME: If the handshake flow control of the device is configured to * automatically use RTS, return STATUS_INVALID_PARAMETER */ Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); if (NT_SUCCESS(Status)) { DeviceExtension->MCR &= ~SR_MCR_RTS; WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR); IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); } break; } case IOCTL_SERIAL_CONFIG_SIZE: { /* Obsolete on Microsoft Windows 2000+ */ PULONG pConfigSize; TRACE_(SERIAL, "IOCTL_SERIAL_CONFIG_SIZE\n"); if (LengthOut != sizeof(ULONG) || BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { pConfigSize = (PULONG)BufferOut; *pConfigSize = 0; Information = sizeof(ULONG); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_BAUD_RATE: { TRACE_(SERIAL, "IOCTL_SERIAL_GET_BAUD_RATE\n"); if (LengthOut < sizeof(SERIAL_BAUD_RATE)) Status = STATUS_BUFFER_TOO_SMALL; else if (BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { ((PSERIAL_BAUD_RATE)BufferOut)->BaudRate = DeviceExtension->BaudRate; Information = sizeof(SERIAL_BAUD_RATE); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_CHARS: { /* FIXME */ PSERIAL_CHARS pSerialChars; ERR_(SERIAL, "IOCTL_SERIAL_GET_CHARS not implemented.\n"); if (LengthOut < sizeof(SERIAL_CHARS)) Status = STATUS_BUFFER_TOO_SMALL; else if (BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { pSerialChars = (PSERIAL_CHARS)BufferOut; pSerialChars->EofChar = 0; pSerialChars->ErrorChar = 0; pSerialChars->BreakChar = 0; pSerialChars->EventChar = 0; pSerialChars->XonChar = 0; pSerialChars->XoffChar = 0; Information = sizeof(SERIAL_CHARS); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_COMMSTATUS: { TRACE_(SERIAL, "IOCTL_SERIAL_GET_COMMSTATUS\n"); if (LengthOut < sizeof(SERIAL_STATUS)) Status = STATUS_BUFFER_TOO_SMALL; else if (BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { Status = SerialGetCommStatus((PSERIAL_STATUS)BufferOut, DeviceExtension); Information = sizeof(SERIAL_STATUS); } break; } case IOCTL_SERIAL_GET_DTRRTS: { PULONG pDtrRts; TRACE_(SERIAL, "IOCTL_SERIAL_GET_DTRRTS\n"); if (LengthOut != sizeof(ULONG) || BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { pDtrRts = (PULONG)BufferOut; *pDtrRts = 0; if (DeviceExtension->MCR & SR_MCR_DTR) *pDtrRts |= SERIAL_DTR_STATE; if (DeviceExtension->MCR & SR_MCR_RTS) *pDtrRts |= SERIAL_RTS_STATE; Information = sizeof(ULONG); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_HANDFLOW: { /* FIXME */ PSERIAL_HANDFLOW pSerialHandflow; ERR_(SERIAL, "IOCTL_SERIAL_GET_HANDFLOW not implemented.\n"); if (LengthOut < sizeof(SERIAL_HANDFLOW)) Status = STATUS_BUFFER_TOO_SMALL; else if (BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { pSerialHandflow = (PSERIAL_HANDFLOW)BufferOut; pSerialHandflow->ControlHandShake = 0; pSerialHandflow->FlowReplace = 0; pSerialHandflow->XonLimit = 0; pSerialHandflow->XoffLimit = 0; Information = sizeof(SERIAL_HANDFLOW); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_LINE_CONTROL: { TRACE_(SERIAL, "IOCTL_SERIAL_GET_LINE_CONTROL\n"); if (LengthOut < sizeof(SERIAL_LINE_CONTROL)) Status = STATUS_BUFFER_TOO_SMALL; else if (BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { *((PSERIAL_LINE_CONTROL)BufferOut) = DeviceExtension->SerialLineControl; Information = sizeof(SERIAL_LINE_CONTROL); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_MODEM_CONTROL: { PULONG pMCR; TRACE_(SERIAL, "IOCTL_SERIAL_GET_MODEM_CONTROL\n"); if (LengthOut != sizeof(ULONG) || BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { pMCR = (PULONG)BufferOut; *pMCR = DeviceExtension->MCR; Information = sizeof(ULONG); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_MODEMSTATUS: { PULONG pMSR; TRACE_(SERIAL, "IOCTL_SERIAL_GET_MODEMSTATUS\n"); if (LengthOut != sizeof(ULONG) || BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { pMSR = (PULONG)BufferOut; *pMSR = DeviceExtension->MSR; Information = sizeof(ULONG); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_PROPERTIES: { TRACE_(SERIAL, "IOCTL_SERIAL_GET_PROPERTIES\n"); if (LengthOut < sizeof(SERIAL_COMMPROP)) Status = STATUS_BUFFER_TOO_SMALL; else if (BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { Status = SerialGetCommProp((PSERIAL_COMMPROP)BufferOut, DeviceExtension); Information = sizeof(SERIAL_COMMPROP); } break; } case IOCTL_SERIAL_GET_STATS: { TRACE_(SERIAL, "IOCTL_SERIAL_GET_STATS\n"); if (LengthOut < sizeof(SERIALPERF_STATS)) Status = STATUS_BUFFER_TOO_SMALL; else if (BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { KeSynchronizeExecution(DeviceExtension->Interrupt, (PKSYNCHRONIZE_ROUTINE)SerialGetPerfStats, Irp); Information = sizeof(SERIALPERF_STATS); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_TIMEOUTS: { TRACE_(SERIAL, "IOCTL_SERIAL_GET_TIMEOUTS\n"); if (LengthOut != sizeof(SERIAL_TIMEOUTS) || BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { *(PSERIAL_TIMEOUTS)BufferOut = DeviceExtension->SerialTimeOuts; Information = sizeof(SERIAL_TIMEOUTS); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_GET_WAIT_MASK: { PULONG pWaitMask; TRACE_(SERIAL, "IOCTL_SERIAL_GET_WAIT_MASK\n"); if (LengthOut != sizeof(ULONG) || BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { pWaitMask = (PULONG)BufferOut; *pWaitMask = DeviceExtension->WaitMask; Information = sizeof(ULONG); Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_IMMEDIATE_CHAR: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_IMMEDIATE_CHAR not implemented.\n"); Status = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_SERIAL_LSRMST_INSERT: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_LSRMST_INSERT not implemented.\n"); Status = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_SERIAL_PURGE: { KIRQL Irql; TRACE_(SERIAL, "IOCTL_SERIAL_PURGE\n"); /* FIXME: SERIAL_PURGE_RXABORT and SERIAL_PURGE_TXABORT * should stop current request */ if (LengthIn != sizeof(ULONG) || BufferIn == NULL) Status = STATUS_INVALID_PARAMETER; else { ULONG PurgeMask = *(PULONG)BufferIn; Status = STATUS_SUCCESS; /* FIXME: use SERIAL_PURGE_RXABORT and SERIAL_PURGE_TXABORT flags */ if (PurgeMask & SERIAL_PURGE_RXCLEAR) { KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql); DeviceExtension->InputBuffer.ReadPosition = DeviceExtension->InputBuffer.WritePosition = 0; if (DeviceExtension->UartType >= Uart16550A) { /* Clear also Uart FIFO */ Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); if (NT_SUCCESS(Status)) { WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_RCVR); IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); } } KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql); } if (PurgeMask & SERIAL_PURGE_TXCLEAR) { KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql); DeviceExtension->OutputBuffer.ReadPosition = DeviceExtension->OutputBuffer.WritePosition = 0; if (DeviceExtension->UartType >= Uart16550A) { /* Clear also Uart FIFO */ Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); if (NT_SUCCESS(Status)) { WRITE_PORT_UCHAR(SER_FCR(ComPortBase), SR_FCR_CLEAR_XMIT); IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); } } KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql); } } break; } case IOCTL_SERIAL_RESET_DEVICE: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_RESET_DEVICE not implemented.\n"); Status = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_SERIAL_SET_BAUD_RATE: { PULONG pNewBaudRate; TRACE_(SERIAL, "IOCTL_SERIAL_SET_BAUD_RATE\n"); if (LengthIn != sizeof(ULONG) || BufferIn == NULL) Status = STATUS_INVALID_PARAMETER; else { pNewBaudRate = (PULONG)BufferIn; Status = SerialSetBaudRate(DeviceExtension, *pNewBaudRate); } break; } case IOCTL_SERIAL_SET_BREAK_OFF: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_SET_BREAK_OFF not implemented.\n"); Status = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_SERIAL_SET_BREAK_ON: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_SET_BREAK_ON not implemented.\n"); Status = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_SERIAL_SET_CHARS: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_SET_CHARS not implemented.\n"); Status = STATUS_SUCCESS; break; } case IOCTL_SERIAL_SET_DTR: { /* FIXME: If the handshake flow control of the device is configured to * automatically use DTR, return STATUS_INVALID_PARAMETER */ TRACE_(SERIAL, "IOCTL_SERIAL_SET_DTR\n"); if (!(DeviceExtension->MCR & SR_MCR_DTR)) { Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); if (NT_SUCCESS(Status)) { DeviceExtension->MCR |= SR_MCR_DTR; WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR); IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); } } else Status = STATUS_SUCCESS; break; } case IOCTL_SERIAL_SET_FIFO_CONTROL: { TRACE_(SERIAL, "IOCTL_SERIAL_SET_FIFO_CONTROL\n"); if (LengthIn != sizeof(ULONG) || BufferIn == NULL) Status = STATUS_INVALID_PARAMETER; else { Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); if (NT_SUCCESS(Status)) { WRITE_PORT_UCHAR(SER_FCR(ComPortBase), (UCHAR)((*(PULONG)BufferIn) & 0xff)); IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); } } break; } case IOCTL_SERIAL_SET_HANDFLOW: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_SET_HANDFLOW not implemented.\n"); Status = STATUS_SUCCESS; break; } case IOCTL_SERIAL_SET_LINE_CONTROL: { TRACE_(SERIAL, "IOCTL_SERIAL_SET_LINE_CONTROL\n"); if (LengthIn < sizeof(SERIAL_LINE_CONTROL)) Status = STATUS_BUFFER_TOO_SMALL; else if (BufferIn == NULL) Status = STATUS_INVALID_PARAMETER; else Status = SerialSetLineControl(DeviceExtension, (PSERIAL_LINE_CONTROL)BufferIn); break; } case IOCTL_SERIAL_SET_MODEM_CONTROL: { PULONG pMCR; TRACE_(SERIAL, "IOCTL_SERIAL_SET_MODEM_CONTROL\n"); if (LengthIn != sizeof(ULONG) || BufferIn == NULL) Status = STATUS_INVALID_PARAMETER; else { Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); if (NT_SUCCESS(Status)) { pMCR = (PULONG)BufferIn; DeviceExtension->MCR = (UCHAR)(*pMCR & 0xff); WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR); IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); } } break; } case IOCTL_SERIAL_SET_QUEUE_SIZE: { if (LengthIn < sizeof(SERIAL_QUEUE_SIZE )) return STATUS_BUFFER_TOO_SMALL; else if (BufferIn == NULL) return STATUS_INVALID_PARAMETER; else { KIRQL Irql; PSERIAL_QUEUE_SIZE NewQueueSize = (PSERIAL_QUEUE_SIZE)BufferIn; Status = STATUS_SUCCESS; if (NewQueueSize->InSize > DeviceExtension->InputBuffer.Length) { KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql); Status = IncreaseCircularBufferSize(&DeviceExtension->InputBuffer, NewQueueSize->InSize); KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql); } if (NT_SUCCESS(Status) && NewQueueSize->OutSize > DeviceExtension->OutputBuffer.Length) { KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql); Status = IncreaseCircularBufferSize(&DeviceExtension->OutputBuffer, NewQueueSize->OutSize); KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql); } } break; } case IOCTL_SERIAL_SET_RTS: { /* FIXME: If the handshake flow control of the device is configured to * automatically use DTR, return STATUS_INVALID_PARAMETER */ TRACE_(SERIAL, "IOCTL_SERIAL_SET_RTS\n"); if (!(DeviceExtension->MCR & SR_MCR_RTS)) { Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); if (NT_SUCCESS(Status)) { DeviceExtension->MCR |= SR_MCR_RTS; WRITE_PORT_UCHAR(SER_MCR(ComPortBase), DeviceExtension->MCR); IoReleaseRemoveLock(&DeviceExtension->RemoveLock, ULongToPtr(DeviceExtension->ComPort)); } } else Status = STATUS_SUCCESS; break; } case IOCTL_SERIAL_SET_TIMEOUTS: { TRACE_(SERIAL, "IOCTL_SERIAL_SET_TIMEOUTS\n"); if (LengthIn != sizeof(SERIAL_TIMEOUTS) || BufferIn == NULL) Status = STATUS_INVALID_PARAMETER; else { DeviceExtension->SerialTimeOuts = *(PSERIAL_TIMEOUTS)BufferIn; Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_SET_WAIT_MASK: { PULONG pWaitMask = (PULONG)BufferIn; TRACE_(SERIAL, "IOCTL_SERIAL_SET_WAIT_MASK\n"); if (LengthIn != sizeof(ULONG) || BufferIn == NULL) Status = STATUS_INVALID_PARAMETER; else if (DeviceExtension->WaitOnMaskIrp) /* FIXME: Race condition ; field may be currently in modification */ { WARN_(SERIAL, "An IRP is already currently processed\n"); Status = STATUS_INVALID_PARAMETER; } else { DeviceExtension->WaitMask = *pWaitMask; Status = STATUS_SUCCESS; } break; } case IOCTL_SERIAL_SET_XOFF: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_SET_XOFF not implemented.\n"); Status = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_SERIAL_SET_XON: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_SET_XON not implemented.\n"); Status = STATUS_NOT_IMPLEMENTED; break; } case IOCTL_SERIAL_WAIT_ON_MASK: { PIRP WaitingIrp; TRACE_(SERIAL, "IOCTL_SERIAL_WAIT_ON_MASK\n"); if (LengthOut != sizeof(ULONG) || BufferOut == NULL) Status = STATUS_INVALID_PARAMETER; else { IoMarkIrpPending(Irp); WaitingIrp = InterlockedCompareExchangePointer( &DeviceExtension->WaitOnMaskIrp, Irp, NULL); /* Check if an Irp is already pending */ if (WaitingIrp != NULL) { /* Unable to have a 2nd pending IRP for this IOCTL */ WARN_(SERIAL, "Unable to pend a second IRP for IOCTL_SERIAL_WAIT_ON_MASK\n"); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_INVALID_PARAMETER; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return STATUS_PENDING; } break; } case IOCTL_SERIAL_XOFF_COUNTER: { /* FIXME */ ERR_(SERIAL, "IOCTL_SERIAL_XOFF_COUNTER not implemented.\n"); Status = STATUS_NOT_IMPLEMENTED; break; } default: { /* Pass Irp to lower driver */ TRACE_(SERIAL, "Unknown IOCTL code 0x%x\n", Stack->Parameters.DeviceIoControl.IoControlCode); IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(DeviceExtension->LowerDevice, Irp); } } Irp->IoStatus.Status = Status; if (Status == STATUS_PENDING) { IoMarkIrpPending(Irp); } else { Irp->IoStatus.Information = Information; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return Status; }
UART_TYPE SerialDetectUartType( IN PUCHAR BaseAddress) { UCHAR Lcr, TestLcr; UCHAR OldScr, Scr5A, ScrA5; BOOLEAN FifoEnabled; UCHAR NewFifoStatus; 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); /* Accessing the LCR must work for a usable serial port */ if (TestLcr != Lcr) return UartUnknown; /* 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)); /* 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 0x40: case 0x80: /* Not sure about this but the documentation says that 0x40 * indicates an unusable FIFO but my tests only worked * with 0x80 */ return Uart16550; } /* FIFO is only functional for 16550A+ */ return Uart16550A; }