Repository navigation
Serial Port Driver
Driver for serial ports (COM1-4) with UART register access via I/O ports. Handles communication via RS-232 serial interface.
The Serial Port Driver provides access to standard PC serial ports (COM1-COM4) using the 16550/16450 UART hardware. It implements the SerialPortAPI interface and extends the Driver framework, integrating with JNode's device management system. The driver uses programmed I/O (PIO) to access UART registers through I/O ports, supporting configurable baud rate, data bits, stop bits, and parity.
| Class/File | Purpose |
|---|---|
SerialPortDriver.java |
Main driver implementation - extends Driver, implements SerialPortAPI and ByteChannel
|
SerialPortAPI.java |
API interface defining baud rate constants and configuration methods |
SerialPortFinder.java |
Device finder that registers serial0 and serial1 devices at boot |
The driver accesses UART registers via I/O ports starting at a configurable base address. Each register is at offset basePort + n:
| Offset | Register | Access | Purpose |
|---|---|---|---|
| +0 | Data/ divisor LSB | R/W | DLAB=0: receive/transmit data; DLAB=1: divisor bits 0-7 |
| +1 | IER / divisor MSB | R/W | DLAB=0: interrupt enable; DLAB=1: divisor bits 8-15 |
| +2 | FCR / IIR | W/R | FIFO control (write) / FIFO status (read) |
| +3 | LCR | R/W | Line control: DLAB, parity, stop bits, data length |
| +4 | MCR | R/W | Modem control: DTR, RTS, loopback |
| +5 | LSR | R/W | Line status: data ready, overrun, framing, parity |
| +6 | MSR | R/W | Modem status: CTS, DSR, RI, CD |
| +7 | Scratch | R/W | 1-byte scratch register |
Baud rate is configured via a divisor value using the formula: baudRate = 115200 / divisor
Standard baud rate divisors:
- 9600 baud: divisor = 12 (BAUD9600 constant)
- 4800 baud: divisor = 24 (BAUD4800 constant)
- 2400 baud: divisor = 48 (BAUD2400 constant)
- 1200 baud: divisor = 96 (BAUD1200 constant)
Configuration sequence (from SerialPortDriver.java:121-124):
port.outPortByte(basePort + 3, control | 128); // Set DLAB=1
port.outPortByte(basePort + 0, divisor); // Divisor LSB
port.outPortByte(basePort + 1, divisor >> 8); // Divisor MSB
port.outPortByte(basePort + 3, control); // Clear DLAB, set LCRSerialPortFinder implements DeviceFinder to register serial ports at boot:
| Device Name | I/O Port Base | Standard Mapping |
|---|---|---|
| serial0 | 0x3f8 | COM1 |
| serial1 | 0x2f8 | COM2 |
The finder creates a Device instance for each port, associates it with a SerialPortDriver instance, and registers it with the DeviceManager.
The driver implements ByteChannel for NIO-based I/O:
-
read(ByteBuffer)- reads bytes into buffer viareadSingle()polling -
write(ByteBuffer)- writes bytes viawriteSingle()polling -
isOpen()/close()- channel state management
Both read and write use busy-wait loops polling the line status register (LSR).
-
Busy-wait I/O: The current implementation uses busy-wait polling for both read and readSingle (
SerialPortDriver.java:136-138) and write (SerialPortDriver.java:143-145). This is CPU-intensive and blocks the calling thread. -
No interrupt support: The driver does not use UART interrupts for data availability notification. Interrupt-driven I/O would be more efficient.
-
FIFO not utilized: Although the UART supports 16-byte transmit/receive FIFOs, the driver does not enable or utilize them, potentially limiting throughput.
-
Limited port detection: Only COM1 (0x3f8) and COM2 (0x2f8) are registered. COM3/COM4 at 0x3e8 and 0x2e8 are not automatically discovered.
-
No DMA support: Serial port DMA transfers are not implemented.
JNode supports interactive serial console operation via SerialConsolePlugin, which auto-starts during boot:
File: shell/src/shell/org/jnode/shell/command/driver/console/SerialConsolePlugin.java
When the plugin system starts, SerialConsolePlugin.startPlugin():
- Opens
serial1(COM2, I/O 0x2f8) at 115200 baud - Creates a
SerialAgentConsolewith raw text mode - Starts a
CommandShellthread with prompt[JNODE_AGENT_READY] - Thread is daemon — dies with the system
No manual activation needed — the plugin starts automatically.
File: core/src/driver/org/jnode/driver/serial/console/SerialAgentConsole.java
Implements agent-mode serial console with:
-
RawKeyboardReader— reads raw serial input bytes via a daemon pump thread -
SerialTextScreen— text screen backed by serial I/O -
SerialTextScreenManager— manages screen dimensions
Job Control (Ctrl-C / Ctrl-Z): The agent console translates control characters into keyboard events so shell job control works over serial as on VGA. The RawKeyboardReader daemon pump thread drains the serial port continuously. When it sees ETX (0x03, Ctrl-C) or SUB (0x1A, Ctrl-Z), it fires a KeyboardEvent via AbstractConsole.enqueueKeyboardEvent() -- bypassing the focus check, since agent consoles are never focused. All other bytes are queued for the shell's read() calls.
The enqueueKeyboardEvent() method on AbstractConsole adds events directly to the keyboard event processor's queue, allowing async dispatch without blocking the serial input pump. This is critical because the shell may be blocked waiting for a listener that writes to a stalled UART.
Limitation: Thread.stop() remains ineffective against timer-sleeping proclet workers (JVM-level issue). The Ctrl-C/Ctrl-Z keystroke is delivered and diagnostics are printed, but the kill itself may not terminate the worker.
File: shell/src/shell/org/jnode/shell/command/driver/console/SerialConsoleCommand.java
Interactive shell command for manual serial console:
serialconsole -p serial1 -b 115200 -aParameters: -p port, -b baud rate, -a agent mode
File: core/src/core/org/jnode/log4j/config/SerialAppender.java
A log4j WriterAppender that writes to a serial port via SerialPortDriver's API:
-
Port:
serial0(COM1, 0x3F8) by default - Data format: 8N1
-
Conversion:
\n→\r\n -
Activation: Requires
lkdboot flag -
Lazy initialization: Uses
DeviceUtils.getAPI()to acquireSerialPortAPI— the serial port device may not be available when this appender is created (plugins start before device finders). Pending output is buffered (up to 8KB) until the device becomes available, then drained. -
Fallback: If
SerialPortAppenderfails to acquire the device, falls back toUnsafeDebugAppender(routes throughUnsafe.debug()which outputs to both VGA and serial at assembly level). CatchesRuntimeException(not checkedException) since device API failures are unchecked.
When lkd is present, Log4jConfigurePlugin adds SerialAppender to the root logger, routing all Log4j output to COM1.
File: core/src/driver/org/jnode/driver/serial/SerialPortFinder.java
Discovers and registers serial port devices at boot. Handles registration failures gracefully — if a device name is already registered or a driver cannot be set, a warning is logged but boot continues. Registers both serial0 (0x3f8) and serial1 (0x2f8).
- Driver-Framework - Base driver architecture
- Device-Manager - Device registration and lifecycle
- DeviceAPI - Device API pattern
- Resource-Management - I/O port allocation
- Input-Drivers - Related input device drivers