void com_putchar(char ch) { if (ch == '\n') { com_putchar('\r'); } while ((READ_PORT_UCHAR (pv(SER_LSR(COM_BASE))) & SR_LSR_TBE) == 0); WRITE_PORT_UCHAR(pv(SER_THR(COM_BASE)), ch); }
VOID NTAPI SerialSendByte( IN PKDPC Dpc, IN PVOID pDeviceExtension, // real type PSERIAL_DEVICE_EXTENSION IN PVOID Unused1, IN PVOID Unused2) { PSERIAL_DEVICE_EXTENSION DeviceExtension; PUCHAR ComPortBase; UCHAR Byte; KIRQL Irql; UCHAR IER; NTSTATUS Status; DeviceExtension = (PSERIAL_DEVICE_EXTENSION)pDeviceExtension; ComPortBase = ULongToPtr(DeviceExtension->BaseAddress); KeAcquireSpinLock(&DeviceExtension->OutputBufferLock, &Irql); while (!IsCircularBufferEmpty(&DeviceExtension->OutputBuffer) && READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_THR_EMPTY) { Status = PopCircularBufferEntry(&DeviceExtension->OutputBuffer, &Byte); if (!NT_SUCCESS(Status)) break; WRITE_PORT_UCHAR(SER_THR(ComPortBase), Byte); INFO_(SERIAL, "Byte sent to COM%lu: 0x%02x\n", DeviceExtension->ComPort, Byte); DeviceExtension->SerialPerfStats.TransmittedCount++; } if (!IsCircularBufferEmpty(&DeviceExtension->OutputBuffer)) { /* allow new interrupts */ IER = READ_PORT_UCHAR(SER_IER(ComPortBase)); WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_THR_EMPTY); } KeReleaseSpinLock(&DeviceExtension->OutputBufferLock, Irql); }
VOID NTAPI SerialReceiveByte( IN PKDPC Dpc, IN PVOID pDeviceExtension, // real type PSERIAL_DEVICE_EXTENSION IN PVOID Unused1, IN PVOID Unused2) { PSERIAL_DEVICE_EXTENSION DeviceExtension; PUCHAR ComPortBase; UCHAR Byte; KIRQL Irql; UCHAR IER; NTSTATUS Status; DeviceExtension = (PSERIAL_DEVICE_EXTENSION)pDeviceExtension; ComPortBase = ULongToPtr(DeviceExtension->BaseAddress); KeAcquireSpinLock(&DeviceExtension->InputBufferLock, &Irql); while (READ_PORT_UCHAR(SER_LSR(ComPortBase)) & SR_LSR_DATA_RECEIVED) { Byte = READ_PORT_UCHAR(SER_RBR(ComPortBase)); INFO_(SERIAL, "Byte received on COM%lu: 0x%02x\n", DeviceExtension->ComPort, Byte); Status = PushCircularBufferEntry(&DeviceExtension->InputBuffer, Byte); if (NT_SUCCESS(Status)) DeviceExtension->SerialPerfStats.ReceivedCount++; else DeviceExtension->SerialPerfStats.BufferOverrunErrorCount++; } KeSetEvent(&DeviceExtension->InputBufferNotEmpty, 0, FALSE); KeReleaseSpinLock(&DeviceExtension->InputBufferLock, Irql); /* allow new interrupts */ IER = READ_PORT_UCHAR(SER_IER(ComPortBase)); WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_DATA_RECEIVED); }
BOOLEAN NTAPI SerialInterruptService( IN PKINTERRUPT Interrupt, IN OUT PVOID ServiceContext) { PDEVICE_OBJECT DeviceObject; PSERIAL_DEVICE_EXTENSION DeviceExtension; PUCHAR ComPortBase; UCHAR Iir; ULONG Events = 0; BOOLEAN ret = FALSE; /* FIXME: sometimes, produce SERIAL_EV_RXFLAG event */ DeviceObject = (PDEVICE_OBJECT)ServiceContext; DeviceExtension = (PSERIAL_DEVICE_EXTENSION)DeviceObject->DeviceExtension; ComPortBase = ULongToPtr(DeviceExtension->BaseAddress); Iir = READ_PORT_UCHAR(SER_IIR(ComPortBase)); if (Iir == 0xff) return TRUE; Iir &= SR_IIR_ID_MASK; if ((Iir & SR_IIR_SELF) != 0) { return FALSE; } switch (Iir) { case SR_IIR_MSR_CHANGE: { UCHAR MSR, IER; TRACE_(SERIAL, "SR_IIR_MSR_CHANGE\n"); MSR = READ_PORT_UCHAR(SER_MSR(ComPortBase)); if (MSR & SR_MSR_CTS_CHANGED) { if (MSR & SR_MSR_CTS) KeInsertQueueDpc(&DeviceExtension->SendByteDpc, NULL, NULL); else { ; /* FIXME: stop transmission */ } Events |= SERIAL_EV_CTS; } if (MSR & SR_MSR_DSR_CHANGED) { if (MSR & SR_MSR_DSR) KeInsertQueueDpc(&DeviceExtension->ReceivedByteDpc, NULL, NULL); else { ; /* FIXME: stop reception */ } Events |= SERIAL_EV_DSR; } if (MSR & SR_MSR_RI_CHANGED) { INFO_(SERIAL, "SR_MSR_RI_CHANGED changed: now %d\n", MSR & SI_MSR_RI); Events |= SERIAL_EV_RING; } if (MSR & SR_MSR_DCD_CHANGED) { INFO_(SERIAL, "SR_MSR_DCD_CHANGED changed: now %d\n", MSR & SR_MSR_DCD); Events |= SERIAL_EV_RLSD; } IER = READ_PORT_UCHAR(SER_IER(ComPortBase)); WRITE_PORT_UCHAR(SER_IER(ComPortBase), IER | SR_IER_MSR_CHANGE); ret = TRUE; goto done; } case SR_IIR_THR_EMPTY: { TRACE_(SERIAL, "SR_IIR_THR_EMPTY\n"); KeInsertQueueDpc(&DeviceExtension->SendByteDpc, NULL, NULL); Events |= SERIAL_EV_TXEMPTY; ret = TRUE; goto done; } case SR_IIR_DATA_RECEIVED: { ULONG AlreadyReceivedBytes, Limit; TRACE_(SERIAL, "SR_IIR_DATA_RECEIVED\n"); KeInsertQueueDpc(&DeviceExtension->ReceivedByteDpc, NULL, NULL); Events |= SERIAL_EV_RXCHAR; /* Check if buffer will be 80% full */ AlreadyReceivedBytes = GetNumberOfElementsInCircularBuffer( &DeviceExtension->InputBuffer) * 5; Limit = DeviceExtension->InputBuffer.Length * 4; if (AlreadyReceivedBytes < Limit && AlreadyReceivedBytes + 1 >= Limit) { /* Buffer is full at 80% */ Events |= SERIAL_EV_RX80FULL; } ret = TRUE; goto done; } case SR_IIR_ERROR: { UCHAR LSR; TRACE_(SERIAL, "SR_IIR_ERROR\n"); LSR = READ_PORT_UCHAR(SER_LSR(ComPortBase)); if (LSR & SR_LSR_OVERRUN_ERROR) { InterlockedIncrement((PLONG)&DeviceExtension->SerialPerfStats.SerialOverrunErrorCount); Events |= SERIAL_EV_ERR; } if (LSR & SR_LSR_PARITY_ERROR) { InterlockedIncrement((PLONG)&DeviceExtension->SerialPerfStats.ParityErrorCount); Events |= SERIAL_EV_ERR; } if (LSR & SR_LSR_FRAMING_ERROR) { InterlockedIncrement((PLONG)&DeviceExtension->SerialPerfStats.FrameErrorCount); Events |= SERIAL_EV_ERR; } if (LSR & SR_LSR_BREAK_INT) { InterlockedIncrement((PLONG)&DeviceExtension->BreakInterruptErrorCount); Events |= SERIAL_EV_BREAK; } ret = TRUE; goto done; } } done: if (!ret) return FALSE; if (DeviceExtension->WaitOnMaskIrp && (Events & DeviceExtension->WaitMask)) { /* Finish pending IRP */ PULONG pEvents = (PULONG)DeviceExtension->WaitOnMaskIrp->AssociatedIrp.SystemBuffer; DeviceExtension->WaitOnMaskIrp->IoStatus.Status = STATUS_SUCCESS; DeviceExtension->WaitOnMaskIrp->IoStatus.Information = sizeof(ULONG); *pEvents = Events; KeInsertQueueDpc(&DeviceExtension->CompleteIrpDpc, DeviceExtension->WaitOnMaskIrp, NULL); /* We are now ready to handle another IRP, even if this one is not completed */ DeviceExtension->WaitOnMaskIrp = NULL; return STATUS_SUCCESS; } return TRUE; }
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; }